Authorization header. The API supports two token types: API Keys for server-to-server integrations, and OAuth tokens for integrations that act on behalf of a specific Virtuous user.
If you have not yet generated a credential, the Quickstart walks you through creating an API Key and verifying it works.
Choose your authentication method
| Method | Best for | Token lifetime |
|---|---|---|
| API Key | Partner integrations, automated syncs, background jobs, service accounts | 15 years (static) |
| OAuth Token | User-delegated access — your integration acts on behalf of a specific Virtuous user | 15 days (access token) / 365 days (refresh token) |
API Keys
Set up an API Key
Log into the Virtuous dashboard
Navigate to your organization’s Virtuous instance and log in with an account that has administrator permissions. Only administrators can create API Keys.
Create a new key
Click Create a Key. Enter a descriptive name (typically the name of the integration you are building) and select the appropriate permission group. Click Save.
Copy the key immediately
After saving, the key is displayed once. Copy it and store it in a secrets manager or environment variable — the key cannot be retrieved again from the UI. If you lose it, create a replacement.
API Keys for partner integrations
A common partner question: does each of our nonprofit customers need a separate API Key? Yes. An API Key is generated inside a specific Virtuous organization by an administrator at that organization, and it grants access only to that organization’s data. A partner integration that serves multiple nonprofit customers stores one API Key per customer and uses the correct key when calling the API on that customer’s behalf. See Base URLs and Environments for guidance on storing and routing per-tenant credentials.OAuth Tokens
Obtain an OAuth token
OAuth tokens are issued via aPOST to https://api.virtuoussoftware.com/Token using the password grant type. URL-encode the email address and password before constructing the request body to handle special characters correctly.
access_token as your Bearer token.
The
expires_in value is in seconds and reflects the access token’s session window — the example above shows roughly one hour. The OAuth token’s full effective lifetime is 15 days; use the refresh token to obtain a new access token without re-prompting the user for credentials.Refresh an OAuth token
When the access token expires, use the refresh token to obtain a new one without requiring the user to re-authenticate. Refresh tokens are valid for 365 days.access_token and refresh_token from the response — Virtuous may rotate the refresh token on each refresh, so always replace both.
Two-factor authentication
If the account has Two-Factor Authentication (2FA) enabled, the initial token request returns a202 Accepted response indicating that a verification code is required. The user receives the code by SMS.
Resubmit the request with the code added as an otp field:
cURL
Making an authenticated request
Pass your token in theAuthorization header on every API request. The header format is identical for API Keys and OAuth tokens.
Authentication errors
| Scenario | Status | What it means |
|---|---|---|
Missing Authorization header | 401 | No credentials were provided on the request. |
| Invalid or expired token | 401 | The token is malformed, has been revoked in the Virtuous UI, or — for OAuth — has expired. |
| Valid token, insufficient permissions | 403 | The credential is valid but the permission group attached to it does not grant access to this resource or action. |
The CRM+ API does not currently return a structured JSON error body for all
401 responses — some endpoints return a plain-text Authorization has been denied for this request. message. The target canonical error shape is documented on the Error Handling page. Handle 401 defensively by checking response.status first; do not assume the body is always JSON.403, the most common cause is that the API Key’s permission group does not include the resource you are calling. Confirm the permission group assignment in Settings → Integrations → API Keys and adjust if needed.
HMAC-authenticated endpoints
A small number of CRM+ endpoints require HMAC authentication instead of Bearer token authentication. The most notable isGET /api/Contact/ByReference/{referenceId}, which is used to look up Contacts by an external reference identifier.
HMAC auth uses a different credential and signing mechanism from the standard API Key. If you receive a 401 on an endpoint marked as “HMAC Auth only,” contact Virtuous support to obtain HMAC credentials and signing instructions.
Security recommendations for partner integrations
- Use a secrets manager. Store every customer’s API Key in a managed secrets service (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Doppler, 1Password, etc.) — never in your application database in plaintext.
- Scope per customer. Use a separate API Key for each nonprofit customer. Do not pool credentials across customers.
- Rotate on compromise. If an API Key is exposed in logs, source control, or a third-party system, treat it as compromised. Revoke it in the Virtuous UI and generate a replacement immediately.
- Audit your dependency chain. If your integration calls third-party services with the Virtuous-derived data, confirm those services do not log or store the API Key inadvertently.
- Prefer API Keys over OAuth for service accounts. OAuth tokens are tied to a specific Virtuous user; if that user leaves the nonprofit, the OAuth credentials are revoked. API Keys are independent of any single user and survive personnel changes.
Cross-API note
The Raise API and Volunteer API use the sameAuthorization: Bearer header format, but tokens are not interchangeable across products. Each product’s authentication is independent — partner integrations spanning multiple Virtuous products store and route a separate credential for each.
Next steps
Base URLs and Environments
The base URL, why there is no sandbox environment, and how to scope credentials per customer.
Make Your First API Call
A walkthrough of request anatomy, response inspection, and debugging your first call.
Error Handling
The error response shape and how to write defensive client code.
Rate Limits
The 5,000-requests-per-hour limit and how to handle 429 responses gracefully.