Skip to main content
A partner integration handles credentials that, if mishandled, could expose customer donor data to attackers, lock customers out of their own systems, or produce data breaches that the customer is legally responsible for. This page covers the practices that keep credentials, secrets, and sensitive data safe across the lifecycle of an integration — from initial customer onboarding through eventual offboarding. The audience is the engineering team responsible for the integration’s security posture, plus the operations team handling customer credential lifecycle events.

What needs protection

A partner integration typically handles four classes of secrets and three classes of sensitive data: Each class needs different controls. The next sections walk through them.

Storing Virtuous API tokens

Virtuous API tokens are the integration’s primary credential. A leaked token can be used to read or write any data the token’s permission set grants — including donor PII, financial history, and webhook configuration.

Storage

Store tokens in a dedicated secrets manager. The acceptable options: Not acceptable:

Naming

Use a key naming convention that makes the secret’s scope obvious:
This structure supports per-customer-per-environment access control and makes the secret’s purpose clear in audit logs.

Access control

The principle: only the workers that need a customer’s token can access it. Three rules: The cloud secrets managers all support these controls natively. Use them.

Rotation

API tokens should be rotated periodically. The cadence depends on the integration’s threat model — typical recommendations are every 90 days for high-sensitivity integrations, every 6–12 months for lower-sensitivity ones. The rotation flow:
1

Generate a new token in Virtuous

The customer’s Virtuous admin generates a new API key. (Or the partner does it on the customer’s behalf if the partner has admin access.)
2

Add the new token alongside the existing one

Store the new token in the secrets manager. Mark it as “pending.”
3

Cut over reads

Workers start reading the new token. Old token remains accessible to handle in-flight requests.
4

Validate

Confirm a successful API call with the new token. Confirm no errors from workers using the new token.
5

Retire the old token

Delete the old token from the secrets manager. Revoke it in Virtuous.
Build this flow into the integration from the start — bolting it on later is much harder.

Storing webhook secrets

Webhook secrets are different from API tokens in one important way: the integration generates them rather than receiving them from Virtuous. When you call POST /api/Webhook to subscribe, you provide the secret field.

Generation

Generate webhook secrets with a cryptographically secure random source — at least 32 bytes of entropy:
JavaScript
Never reuse the same secret across customers. Each customer’s webhook subscription has its own randomly-generated secret.

Storage

The same secrets manager that holds API tokens holds webhook secrets. Use a parallel naming convention:

Rotation

Webhook secret rotation is more complex than API token rotation because the verifier must accept both old and new during a rotation window. See Webhooks Overview: Rotating the secret for the pattern.

OAuth and source-platform credentials

For source platforms that use OAuth (Mailchimp, Constant Contact, Stripe Connect, etc.), the integration typically holds:
  • A long-lived refresh token that can produce fresh access tokens.
  • Short-lived access tokens issued from the refresh token.

Refresh token storage

Refresh tokens are the long-term credential — protect them like API tokens. Store in the secrets manager:

Access token handling

Access tokens are short-lived. Cache them in memory or a short-TTL cache (e.g., Redis with the token’s natural expiration as TTL). Don’t store access tokens in the secrets manager — they expire and the constant write churn is wasteful.
JavaScript

Revoked credentials

Customers can revoke a partner integration’s OAuth grant from the source platform’s dashboard. When this happens, refresh attempts return errors. Detect and surface:
JavaScript
A revoked credential is a customer-side event, not an integration bug. The right response is to pause the customer’s sync and notify them through your platform’s UI.

Network and transport security

A few practices outside of secret storage:

TLS everywhere

Every external API call uses HTTPS with a valid TLS certificate from a public CA. Reject endpoints that present invalid certificates or use plain HTTP.
JavaScript

Egress IP allowlisting

Some customers want to allowlist the egress IPs your integration uses to talk to Virtuous. Plan to support this:
  • Run your workers behind a NAT gateway with a stable IP, or use a cloud-provider feature that produces stable egress IPs.
  • Document the egress IPs in your customer documentation so customer firewall teams can allowlist them.
  • Notify customers in advance when egress IPs change.

Webhook receiver hardening

Your webhook receiver is internet-facing. Standard web-application hardening applies:
  • HTTPS-only with a valid certificate.
  • Rate limiting at the edge to prevent abuse.
  • A WAF or similar to filter obvious attacks.
  • Signature verification on every incoming request (see Signature Verification).
  • Reject requests with unexpected Content-Type or oversize bodies.

PII and sensitive data handling

Beyond credentials, the integration handles a lot of donor PII. Treat it accordingly.

Don’t log PII

Avoid logging fields that contain donor identifiers. Structured logs should reference donors by stable identifiers (Virtuous Contact ID, your platform’s user ID) rather than PII.
JavaScript
If a specific debug investigation requires PII, do it in a controlled debug-mode flag — not as production-default logging.

Don’t log request bodies in error paths

A common pattern that leaks PII:
JavaScript
Log error context (status code, error message, request ID) but redact request bodies. If the body is needed for investigation, store it in a secure debug-data store with retention and access controls.

PII in error responses

If your integration’s own API surface returns errors to your customers’ developers, scrub PII from error messages. A 400 Bad Request response should explain what was wrong with the schema, not echo back the donor data that was rejected.

Retention

Establish retention policies for sync state and dead-letter records: Retention also applies to your secrets manager — old rotated credentials should be deleted, not just marked inactive.

Customer onboarding and offboarding

The credential lifecycle starts at onboarding and ends at offboarding. Both need explicit handling.

Onboarding

When a customer connects your integration:
1

Receive their Virtuous API token securely

Through your platform’s UI (not via email). Validate immediately with a low-impact API call (e.g., GET /api/Health or equivalent) before storing.
2

Store in the secrets manager

Under the customer’s scoped path. Confirm storage succeeded before returning to the customer.
3

Configure webhook subscription

Generate a webhook secret, store it, and call POST /api/Webhook with the customer’s payload URL pointing at your integration’s endpoint.
4

Initial reconciliation

Run the initial bulk-load or cross-match reconciliation per the integration’s architecture.
5

Notify the customer of completion

Surface in your platform’s UI that sync is active.

Offboarding

When a customer disconnects:
1

Stop syncing immediately

Pause the customer’s submitter and reconciliation workers. Stop draining the queue.
2

Delete the webhook subscription in Virtuous

DELETE /api/Webhook/{webhookId} so Virtuous stops attempting deliveries to your endpoint.
3

Revoke the customer's API token

Have the customer’s admin revoke the token in Virtuous, or do it for them if your integration has the access.
4

Delete the customer's secrets

Remove the API token, webhook secret, and source-platform credentials from the secrets manager.
5

Delete or anonymize the customer's data

Per your retention policy and the customer’s data-protection requirements (GDPR, CCPA, etc.). If the customer requests full deletion, that includes sync state, dead-letter entries, and historical logs.
6

Confirm to the customer

Send a confirmation that all customer data has been removed and provide a record of the deletion for their compliance file.
The deletion step is the one most integrations get wrong — old sync records, dead-letter entries, and operational logs linger long after the customer leaves. Have a documented deletion process that’s tested periodically.

Permissions: least privilege

Virtuous API tokens carry the permission set configured on the underlying API key. The principle: request only the permissions your integration actually uses. If your integration only writes Gifts and reads Contacts, request a permission set that allows those operations and nothing else. Don’t request “all access” if you only need a subset — a compromised token with narrower permissions is less damaging than one with broader permissions. Document the permission requirements in your customer-facing setup documentation so customers can configure their API keys appropriately.
The exact permission groups and operations available in CRM+ are an admin-side concern in the Virtuous UI. Walk through the configuration with the customer’s admin during onboarding.⚠️ Human input required: Document the specific permission groups CRM+ exposes for API keys (read-only vs. write-enabled, by resource type) so partner integrations can recommend the minimum-required configuration.

Incident response

Plan for the credential-incident scenarios before they happen:

Suspected credential compromise

A customer reports unexpected data in their Virtuous account that might trace to your integration’s writes:
1

Rotate the customer's API token immediately

Even if compromise isn’t confirmed, rotation is cheap. Generate a new token, update the secrets manager, retire the old one.
2

Audit the integration's recent writes

Pull the recent activity log for that customer. Identify any anomalous writes.
3

Check audit logs for unexpected token access

The secrets manager’s audit log shows every read of the token. Look for unexpected accessors or unusual access patterns.
4

Report to the customer

Whether or not compromise is confirmed, the customer needs a transparent report of what was investigated and what was found.

Source-platform credential revoked

Already covered above. The integration detects the revoke automatically, pauses sync, and notifies the customer.

Webhook signature failures spike

A sudden increase in signature verification failures suggests either an attack (someone trying to forge webhooks) or a rotation problem (old secret no longer accepted). Alert on this; investigate quickly.

Where to go next

Versioning and Backward Compatibility

The companion practices for long-term integration durability.

Signature Verification

The verification pattern that the webhook security on this page builds on.

Authentication

The authentication reference for the Virtuous API token format.

Sandbox Access

How to obtain Seeded Sandbox credentials for development without using production data.
Last modified on May 21, 2026