Skip to main content
This page walks through one CRM+ request in detail — every header, every byte of the response, and the failure modes you are most likely to hit on a first integration attempt. The goal is to make every component of an HTTP call to Virtuous obvious so you can debug confidently when something does not work. If you only need to copy a working snippet and move on, the Quickstart is shorter and more action-oriented.

What we will call

We will use GET /api/Organization/Current — the simplest authenticated CRM+ endpoint. It takes no path parameters, no query parameters, no request body, and returns a single object describing the organization the credential belongs to. Its only job is to confirm that authentication is working and that the credential resolves to the expected Virtuous organization.

Prerequisites

  • A valid CRM+ API Key — see the Quickstart or Authentication page if you have not generated one.
  • A terminal with curl available, or a Node.js environment if you prefer JavaScript.
  • The API Key available as an environment variable, secrets manager entry, or otherwise outside of source control.
Do not paste your API Key into a public terminal session, a shared notebook, or any chat tool. Set it as an environment variable (export VIRTUOUS_API_TOKEN="...") for the duration of testing and reference it from there.

Anatomy of the request

Every CRM+ API request has four components: the method, the URL, the headers, and (for write operations) a body. The call we are making uses three of them:
GET /api/Organization/Current HTTP/1.1
Host: api.virtuoussoftware.com
Authorization: Bearer YOUR_API_TOKEN
ComponentValueWhy it matters
MethodGETReads data. Safe to retry. Never modifies records.
URLhttps://api.virtuoussoftware.com/api/Organization/CurrentBase URL (https://api.virtuoussoftware.com) plus the endpoint path (/api/Organization/Current).
Authorization headerBearer YOUR_API_TOKENIdentifies and authorizes the caller. Required on every request.
Content-Type headerOmitted on GETGET requests have no body, so the header is optional. Include Content-Type: application/json on POST, PUT, and PATCH requests.

The Authorization header in detail

The Authorization header has a precise shape:
Authorization: Bearer YOUR_API_TOKEN
Three things to get right:
  • The word Authorization is the header name. Some HTTP libraries normalize header casing automatically; if yours does not, use this exact spelling.
  • The word Bearer is the auth scheme, followed by a single space.
  • The token comes immediately after Bearer . Do not include quotes around it, do not include = or :, and do not URL-encode it.
A common mistake when copy-pasting from secrets managers is to include surrounding whitespace or quotes inside the value — Bearer "abc123" or Bearer abc123 (two spaces) will both return 401.

Make the call

curl -i https://api.virtuoussoftware.com/api/Organization/Current \
  -H "Authorization: Bearer $VIRTUOUS_API_TOKEN"
The -i flag on curl prints the response headers along with the body — useful for seeing the rate-limit headers and the exact status code on the first call.

Anatomy of the response

A successful response has three components: the status code, the headers, and the body.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 1499
X-RateLimit-Reset: 1713914400

{
  "organizationUserId": 67890,
  "organizationName": "The Human Fund",
  "organizationTimeZone": "US/Arizona",
  "organizationCulture": "en-US",
  "currentUserTimeZone": "US/Arizona",
  "isAdministrator": true,
  "isEnabled": true
}

Status code

A 200 OK confirms the request was authenticated, authorized, and processed successfully. The full list of status codes you may encounter is on the Error Handling page.

Headers worth inspecting

HeaderMeaning
Content-TypeAlways application/json on successful responses. Treat anything else as a transport-level error and do not attempt to parse the body as JSON.
X-RateLimit-LimitTotal request limit for the current window. Always 5000.
X-RateLimit-RemainingRequests remaining in the current window. Read this on every response.
X-RateLimit-ResetUnix timestamp (in seconds) when the rate-limit window resets.
See Rate Limits for how to use these headers in production integrations.

Body fields

The body is a single JSON object describing the organization the credential belongs to:
FieldTypeMeaning
organizationUserIdintegerThe unique identifier for the (user, organization) pair this credential authenticates as.
organizationNamestringThe human-readable name of the Virtuous organization.
organizationTimeZonestringThe organization’s configured time zone. Date and time fields elsewhere in the API are interpreted in this zone.
organizationCulturestringThe organization’s locale (e.g., en-US).
currentUserTimeZonestringThe current user’s personal time zone preference.
isAdministratorbooleanWhether the authenticated user has administrator privileges in this organization.
isEnabledbooleanWhether the organization is currently enabled in Virtuous. A false value indicates the organization is administratively disabled and most API calls will fail.
If your response has 200 OK, a JSON body, and the organization name matches the Virtuous account you generated the API Key in, your integration is correctly configured to talk to Virtuous. You are ready to make read and write calls against the rest of the API.

Common first-call failures

Most first-call failures fall into one of four categories. Each one is fixable in under a minute once you know what to look for.

401 Unauthorized

The credential is missing, malformed, or rejected. Inspect the response body — many 401 responses return a plain-text message rather than JSON:
Authorization has been denied for this request.
Walk through the checklist:
  • Did you include the Authorization header? Run curl -v to confirm the header is being sent.
  • Is the header value exactly Bearer YOUR_API_TOKEN with one space between Bearer and the token? No quotes, no trailing whitespace, no extra characters.
  • Is the token currently active in Settings → Integrations → API Keys? A key can be revoked at any time.
  • For OAuth tokens: has the access token expired? OAuth access tokens expire after the expires_in value returned at issuance. Use the refresh token to obtain a new one — see Authentication.

403 Forbidden

The credential is valid, but the permission group attached to it does not grant access to this resource. GET /api/Organization/Current requires only basic authenticated access, so a 403 here typically means the API Key was created in a permission group with extremely restrictive settings. Open the key in the Virtuous UI and confirm the permission group includes basic read access.

404 Not Found

The URL is wrong. Confirm:
  • The base URL is https://api.virtuoussoftware.com (not www.virtuoussoftware.com or app.virtuoussoftware.com).
  • The path begins with /api/ and matches the casing in the reference documentation.
  • For endpoints with path parameters, the parameter is actually substituted into the URL — /api/Contact/{contactId} is the documented form; the real call uses /api/Contact/4821.

429 Too Many Requests

The rate limit is exceeded. Wait for the period indicated in the Retry-After response header before retrying. See Rate Limits for the full retry-with-backoff pattern.

Debugging tools

When a call is not behaving the way the documentation suggests, these tools usually find the problem quickly:
  • curl -v — prints the full request and response, including all headers. Confirms what is actually being sent on the wire.
  • A logging proxy — point your integration’s HTTP client at a local proxy (mitmproxy, Charles Proxy, Proxyman) to see exactly what your client library is generating. Often the bug is a header your library is silently dropping or adding.
  • GET /api/Organization/Current — the request on this page is the canonical “is the credential working” check. If this call returns 200, the credential is fine and the problem is on the specific endpoint you were originally calling.
  • The HTTP response body, always — even on errors. The CRM+ API returns helpful error messages in most cases. Read them before reaching out to support.

Where to go from here

You have made a successful authenticated call, inspected the response, and seen the failure modes. The next pages cover the patterns every partner integration needs:

Error Handling

Full coverage of status codes, the error envelope shape, and defensive client patterns.

Rate Limits

How the 5,000-per-hour limit works and how to handle 429 with exponential backoff.

Pagination and Filtering

Iterate large result sets safely using skip and take.

Virtuous CRM Data Model

The Contact, Gift, and Designation hierarchy every integration depends on.

Create a Contact

Your first write workflow — and why most partners should use the transaction endpoint.

Sync External Donations

The canonical recipe for pushing Gifts from your platform into Virtuous.
Last modified on June 4, 2026