> ## Documentation Index
> Fetch the complete documentation index at: https://docs.virtuous.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Base URLs and Environments

> The CRM+ base URL, environment scoping, and how partner integrations should handle credentials across multiple nonprofit customers.

The CRM+ API exposes a single production base URL. There is no separate sandbox environment, no staging URL, and no version prefix in the path. This page explains the implications for partner integrations and the patterns that work in production.

## Base URL

| Property       | Value                                                      |
| -------------- | ---------------------------------------------------------- |
| Base URL       | `https://api.virtuoussoftware.com`                         |
| Protocol       | HTTPS only — plain HTTP requests fail                      |
| Version prefix | None — endpoints are versioned individually as they evolve |

Every endpoint in this documentation is reached by appending its path to the base URL. For example, the endpoint listed in the reference as `GET /api/Contact/{contactId}` is called as:

```text theme={null}
GET https://api.virtuoussoftware.com/api/Contact/4821
```

<Info>
  A small number of endpoints use a `/v2/` segment in the path (for example, `GET /api/v2/Pledge/{pledgeId}`). These are versioned in place rather than at the host level — the base URL is the same, and only the specific resource path includes the version segment.
</Info>

***

## Environments and sandbox access

There is one CRM+ API host. Every API Key — whether it belongs to a production nonprofit organization or to a Seeded Sandbox — connects to `https://api.virtuoussoftware.com`. The isolation between environments lives at the **organization** level, not the API host level: a given API Key resolves to exactly one Virtuous organization, and Virtuous organizations are independent of one another.

<Warning>
  Calls made with a valid API Key affect that organization's live data. A `POST /api/Gift` made against a production nonprofit's organization creates a real Gift in that nonprofit's database. There is no automatic rollback. Partner integrations must verify their write paths against a Seeded Sandbox before pointing them at customer data.
</Warning>

### Seeded Sandbox

Virtuous provisions a **Seeded Sandbox** for partner integrations: a dedicated Virtuous organization pre-loaded with representative Contact, Gift, Campaign, and Designation data so partners can build and test against realistic records without touching customer data. The sandbox name typically ends with `(Sandbox)` to make it visually distinct in the UI and in `GET /api/Organization/Current` responses.

To request a Seeded Sandbox, contact your Virtuous partner manager. The provisioning is handled by the Virtuous team — see [Sandbox Access](/crm/partners/sandbox-access) for the full request process, what data is seeded, and how the sandbox is refreshed.

Practical guidance for using the sandbox effectively:

* **Use a separate API Key for the sandbox.** Generate the sandbox's API Key inside the sandbox organization itself. Treat it as a fully independent credential from any production customer's key.
* **Verify which organization a credential belongs to.** Call `GET /api/Organization/Current` after switching credentials — the `organizationName` should include `(Sandbox)` and `isEnabled` should be `true`. This prevents accidentally pointing a sandbox-targeted test run at a production customer.
* **Sandbox data is seeded but mutable.** Writes made by your integration persist in the sandbox until the sandbox is refreshed. Each partner organization is provisioned a single shared sandbox, so coordinate test data among developers on your team — for example, prefix Contact names or external reference IDs with a developer initial — to avoid conflicting writes during parallel development.
* **Restrict early read-only testing to the sandbox.** Even before your write paths are validated, exercise `GET /api/Organization/Current`, `POST /api/Contact/Query`, and similar read endpoints against the sandbox rather than a customer organization to avoid surfacing your in-progress integration in customer audit logs.

***

## Credential scoping

An API Key is generated inside a specific Virtuous organization and grants access only to that organization's data. The API Key is the unit of scoping — there is no header or query parameter that switches the target organization at request time when authenticating with an API Key.

This has three implications for partner integrations:

* **One API Key per nonprofit customer.** 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. You cannot use a single global API Key to access multiple customers' data.
* **API Keys cannot be reused across products.** A CRM+ API Key does not authenticate against the [Raise API](/raise/authentication) or the [Volunteer API](/volunteer/authentication). If your integration spans products, store a separate credential for each.
* **Permission groups apply to the key.** Each API Key is assigned a permission group at creation time. Two keys generated in the same organization can have different permission groups and therefore different access levels.

***

## Verify which organization a credential belongs to

When onboarding a new nonprofit customer's API Key into your integration, call `GET /api/Organization/Current` to confirm which Virtuous organization the credential resolves to. This is the fastest way to detect a misconfigured credential before it writes data to the wrong organization.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.virtuoussoftware.com/api/Organization/Current \
    -H "Authorization: Bearer CUSTOMER_API_TOKEN"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.virtuoussoftware.com/api/Organization/Current',
    {
      headers: { Authorization: `Bearer ${customerApiToken}` },
    }
  );

  const org = await response.json();
  console.log(`Credential resolves to: ${org.organizationName} (${org.organizationUserId})`);
  ```
</CodeGroup>

A successful response:

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

Store the `organizationUserId` and `organizationName` alongside the API Key in your integration's tenant configuration. Compare them on each onboarding refresh to detect when a customer rotates a key into a different organization.

<Tip>
  `isEnabled: false` on the response indicates the organization is disabled in Virtuous (typically for billing or administrative reasons). Treat a disabled organization as a soft failure in your integration — surface a clear error to the customer rather than retrying.
</Tip>

***

## Users with access to multiple organizations

OAuth-authenticated users may belong to more than one Virtuous organization — for example, a consultant working across several nonprofits. The CRM+ API provides three endpoints for working with the available organizations:

| Endpoint                        | Purpose                                                                  |
| ------------------------------- | ------------------------------------------------------------------------ |
| `GET /api/Organization`         | List every organization the current user belongs to.                     |
| `GET /api/Organization/Current` | Read the current active organization for this session.                   |
| `PUT /api/Organization/Switch`  | Change the active organization for this session by `organizationUserId`. |

<Note>
  `PUT /api/Organization/Switch` applies to the current session and is most relevant to OAuth-token integrations where a user interactively chooses a context. Server-to-server integrations using API Keys do not switch organizations — the API Key itself is bound to a single organization.
</Note>

### Example: list and select an organization

<CodeGroup>
  ```bash cURL theme={null}
  # List organizations the user belongs to
  curl https://api.virtuoussoftware.com/api/Organization \
    -H "Authorization: Bearer OAUTH_ACCESS_TOKEN"

  # Switch to a specific organization
  curl -X PUT https://api.virtuoussoftware.com/api/Organization/Switch \
    -H "Authorization: Bearer OAUTH_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{ "organizationUserId": "12345" }'
  ```

  ```javascript JavaScript theme={null}
  // List organizations
  const list = await fetch(
    'https://api.virtuoussoftware.com/api/Organization',
    { headers: { Authorization: `Bearer ${accessToken}` } }
  ).then((r) => r.json());

  // Switch active organization
  await fetch(
    'https://api.virtuoussoftware.com/api/Organization/Switch',
    {
      method: 'PUT',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ organizationUserId: '12345' }),
    }
  );
  ```
</CodeGroup>

A sample `GET /api/Organization` response:

```json theme={null}
[
  {
    "organizationUserId": 12345,
    "organizationName": "The Human Fund (Sandbox)",
    "organizationTimeZone": "US/Arizona",
    "organizationCulture": "en-US",
    "isAdministrator": true,
    "isEnabled": true
  },
  {
    "organizationUserId": 67890,
    "organizationName": "The Human Fund",
    "organizationTimeZone": "US/Arizona",
    "organizationCulture": "en-US",
    "isAdministrator": true,
    "isEnabled": true
  }
]
```

***

## Multi-tenant integration pattern

A typical partner integration that serves many nonprofit customers stores credentials and metadata per tenant. A reasonable shape:

| Field                  | Source                                        | Purpose                                                                                                                   |
| ---------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Customer identifier    | Your system                                   | Internal tenant ID.                                                                                                       |
| API Key                | Generated in the customer's Virtuous instance | Credential used for all CRM+ calls on that customer's behalf.                                                             |
| `organizationUserId`   | `GET /api/Organization/Current` at onboarding | Verifies the credential resolves to the expected Virtuous organization.                                                   |
| `organizationName`     | `GET /api/Organization/Current` at onboarding | Used in your UI to confirm the connection to the customer.                                                                |
| `organizationTimeZone` | `GET /api/Organization/Current`               | Required to interpret date and time fields correctly — Virtuous dates are stored in the organization's timezone, not UTC. |
| Permission group note  | Captured at onboarding                        | Useful for diagnosing `403` responses later.                                                                              |

On every API call, look up the customer's API Key from your secrets manager, attach it as the Bearer token, and route the response back to the originating tenant context.

<Warning>
  Never log API Keys, even in test environments. Configure your logging library to redact the `Authorization` header on every outbound HTTP call. A single API Key leaked to a log aggregator counts as a credential compromise and requires rotation.
</Warning>

***

## Cross-API base URLs

If your integration spans multiple Virtuous products, each product has its own base URL and its own credentials:

| Product          | Base URL                           |
| ---------------- | ---------------------------------- |
| CRM+             | `https://api.virtuoussoftware.com` |
| Raise            | `https://prod-api.raisedonors.com` |
| Volunteer (VOMO) | `https://api.vomo.org/v1`          |

Tokens are not interchangeable. A CRM+ API Key authenticates only against the CRM+ API. See [Raise Authentication](/raise/authentication) and [Volunteer Authentication](/volunteer/authentication) for details on those products.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Make Your First API Call" icon="terminal" href="/crm/first-api-call">
    Walk through the anatomy of a request and response, and learn to debug common first-call failures.
  </Card>

  <Card title="Authentication" icon="key" href="/crm/authentication">
    Full coverage of API Keys, OAuth, refresh tokens, and the two-factor flow.
  </Card>

  <Card title="Error Handling" icon="circle-exclamation" href="/crm/error-handling">
    Status codes, error envelope shape, and defensive client patterns.
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/crm/rate-limits">
    The 5,000-requests-per-hour limit and how it interacts with multi-tenant integrations.
  </Card>
</CardGroup>
