> ## 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 & Environments

> The Volunteer API base URL, the URL versioning convention, HTTPS requirements, and what's known and unknown about sandbox or test environments.

The Volunteer API has a single documented base URL. This page covers the URL structure, the `/v1/` versioning convention, the HTTPS requirements, and what's known about test or sandbox environments.

## The single documented base URL

```text theme={null}
https://api.vomo.org/v1
```

That's the entire spec-documented surface. Every endpoint in the API is reached by appending the endpoint path to this base — `GET /v1/users` is `GET https://api.vomo.org/v1/users`.

| **Component**       | **Value**                                                                                   |
| :------------------ | :------------------------------------------------------------------------------------------ |
| **Protocol**        | `https` only — plain HTTP is not supported                                                  |
| **Host**            | `api.vomo.org`                                                                              |
| **Version segment** | `/v1` — present on every path                                                               |
| **Resource paths**  | `/users`, `/projects`, `/groups`, `/campaigns`, `/organizations`, `/forms`, `/certificates` |

A few example requests showing the full URL structure:

```bash theme={null}
GET  https://api.vomo.org/v1/users
GET  https://api.vomo.org/v1/users/12345
GET  https://api.vomo.org/v1/projects?page=2
POST https://api.vomo.org/v1/groups
GET  https://api.vomo.org/v1/forms/789/completions
```

The version segment (`/v1`) is part of the base URL — not part of individual endpoint paths. When the documentation refers to an endpoint as `GET /users`, the full URL is `https://api.vomo.org/v1/users`.

***

## The `/v1` versioning convention

Volunteer's base URL includes an explicit `/v1` segment. This is different from the other two Virtuous APIs:

| **API**       | **Base URL**                           | **Version segment?** |
| :------------ | :------------------------------------- | :------------------- |
| CRM+          | `https://api.virtuoussoftware.com/api` | No                   |
| Raise         | `https://prod-api.raisedonors.com/api` | No                   |
| **Volunteer** | `https://api.vomo.org/v1`              | **Yes**              |

### What this means for partner integrations

The presence of `/v1` suggests the platform may introduce future versions (`/v2`, etc.) without breaking existing integrations. Integrations targeting `/v1` should continue to work as long as the v1 contract is maintained, even after a v2 is released.

This is purely speculative for now — no v2 exists. But the URL pattern is set up for a future migration to be possible.

For practical integration code, hardcode the `/v1` in your base URL constant:

```javascript theme={null}
const VOMO_BASE_URL = 'https://api.vomo.org/v1';

async function get(path, options = {}) {
  return fetch(`${VOMO_BASE_URL}${path}`, options);
}

// Usage
const users = await get('/users');
const oneUser = await get('/users/12345');
```

If a v2 is released someday, switching is a one-line change to the constant — assuming the path structure is preserved.

See [<u>Versioning and Backward Compatibility</u>](https://markdowntorichtext.com/volunteer/best-practices/versioning-and-backward-compatibility) for the broader discussion.

***

## HTTPS is required

All Volunteer API requests must use HTTPS. The platform does not respond to plain HTTP requests, and any code that attempts `http://api.vomo.org/v1/users` will fail or be redirected.

| **Why HTTPS matters**                                                                       |
| :------------------------------------------------------------------------------------------ |
| The Bearer token in the `Authorization` header would be visible on the wire over plain HTTP |
| PII in responses (names, emails, addresses) would be visible over plain HTTP                |
| Modern browsers and many HTTP clients block or warn on plain-HTTP API calls                 |
| Compliance frameworks (PCI, SOC 2, GDPR) require encryption in transit                      |

In practice, this means every integration uses `https://` in its URL constant. Don't try to override certificate validation, use self-signed certs against the production host, or otherwise loosen the TLS posture.

### TLS version

The Volunteer API requires TLS 1.2 or later. Most modern HTTP libraries default to this, but legacy clients (very old Java, old PHP, certain embedded systems) may need explicit configuration.

If you see TLS handshake errors, check the client's TLS configuration before suspecting the API.

***

## Test and sandbox environments

The Volunteer OpenAPI spec documents one host: production. There's no separately-documented sandbox URL.

**⚠️ Spec gap:** The OpenAPI spec doesn't expose a sandbox or test environment. Test and development access is typically handled by issuing a token against a dedicated test organization on the production host, rather than by using a separate URL. Coordinate with the customer's VOMO concierge if a dedicated test environment is needed.

### What this means in practice

For partner integration development:

| **Approach**                                       | **When to use**                                                                                                             |
| :------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- |
| **Develop against a customer's test organization** | If the customer has a separate VOMO organization specifically for testing, use a token from that organization.              |
| **Develop against a developer-owned VOMO account** | The partner sets up their own VOMO account specifically for integration development.                                        |
| **Develop against production with care**           | Last resort — only if no test organization is available. Use clearly-marked test data (`dev+timestamp@example.com` emails). |

The production host is `https://api.vomo.org/v1` regardless of which approach you use. Isolation is at the organization (token) level, not the URL level.

### Don't develop against a real customer's production data

A common anti-pattern: developing the integration against a real customer's live VOMO account. This produces test users mingled with real users, test groups in production reports, and potentially destructive operations affecting real records.

The right path is to coordinate a test organization upfront. If the customer doesn't have one, request that one be set up before integration development begins.

***

## Service health and status

The Volunteer OpenAPI spec doesn't document a status page URL or health-check endpoint.

For partner integrations operating at scale, knowing when the API is degraded matters — sustained `5xx` responses or elevated latency may indicate a platform issue rather than an integration bug.

### What partner integrations can do

| **Practice**                                   | **Description**                                                                                        |
| :--------------------------------------------- | :----------------------------------------------------------------------------------------------------- |
| **Track 5xx response rate**                    | A spike across multiple customers usually means a platform issue, not a per-customer one.              |
| **Track API latency over time**                | Sudden latency increases without your traffic changing typically indicate platform-side issues.        |
| **Maintain a status page for the integration** | Independent of VOMO — communicates to your customers when your integration is degraded for any reason. |
| **Coordinate with VOMO during incidents**      | When sustained issues appear, reach out to VOMO support to confirm whether it's a platform issue.      |

Don't poll the API to detect health — that wastes rate-limit budget. Track the health of real workloads instead.

***

## Latency and geographic considerations

The spec doesn't document the API's deployment geography or expected latency from different regions. In practice, partner integrations should expect:

| **Source region** | **Expected latency to API**              |
| :---------------- | :--------------------------------------- |
| North America     | Low (typically \<100ms for a simple GET) |
| Europe            | Moderate (typically \<200ms)             |
| Asia / Pacific    | Higher (varies; can be >300ms)           |

These are typical web API characteristics, not specific guarantees. For latency-sensitive workloads, measure from your actual deployment environment.

### Implications for sync architecture

For integrations doing bulk reads (backfills, daily reconciliation), latency adds up. A backfill of 100,000 users with 100ms round-trip per request takes \~3 hours even with no rate-limit throttling. Plan for this when scoping the backfill window.

The patterns on [<u>Sync Architecture Patterns</u>](https://markdowntorichtext.com/volunteer/best-practices/sync-architecture-patterns) cover backfill design for large datasets.

***

## URL construction patterns

A few practices that prevent common URL-construction bugs:

### Don't hand-concatenate query parameters

```javascript theme={null}
// ❌ Anti-pattern — breaks on special characters
const url = `https://api.vomo.org/v1/users?name_like=${searchTerm}`;

// ✅ Use URLSearchParams (or equivalent)
const params = new URLSearchParams({ name_like: searchTerm });
const url = `https://api.vomo.org/v1/users?${params}`;
```

Names with spaces, apostrophes, or other characters need URL encoding. Manual concatenation forgets this; `URLSearchParams` handles it correctly.

### Don't hand-construct paginated URLs

```javascript theme={null}
// ❌ Anti-pattern — breaks if pagination changes
const nextUrl = `${baseUrl}/users?page=${currentPage + 1}`;

// ✅ Follow links.next from the previous response
const nextUrl = previousResponse.links.next;
```

The API tells you the next page's URL in `links.next`. Use it. Manual page-number construction works today but can break if the pagination scheme evolves.

### Don't strip `/v1/` from URLs returned by the API

The `links` field in paginated responses includes the full URL with `/v1/`:

```json theme={null}
{
  "links": {
    "next": "https://api.vomo.org/v1/users?page=2"
  }
}
```

If your code strips the version segment ("I'll add it myself when I make the call"), you'll either double-prefix or miss it entirely. Use returned URLs as-is.

### Be cautious with trailing slashes

The Volunteer paths don't include trailing slashes in the spec:

```text theme={null}
✅ /users
✅ /users/12345
❌ /users/      (trailing slash — typically rejected or redirected)
```

Most HTTP clients handle this correctly, but if you're constructing URLs by string template, double-check that you're not appending a stray `/`.

***

## A reference base URL setup

A minimal, well-organized base URL setup for a partner integration:

```javascript theme={null}
// config.js
export const VOMO_API_BASE_URL = 'https://api.vomo.org/v1';

// vomo-client.js
import { VOMO_API_BASE_URL } from './config.js';

export class VomoClient {
  constructor({ token }) {
    this.token = token;
  }

  async _request(path, options = {}) {
    const url = path.startsWith('http') ? path : `${VOMO_API_BASE_URL}${path}`;

    const response = await fetch(url, {
      ...options,
      headers: {
        Authorization: `Bearer ${this.token}`,
        Accept: 'application/json',
        ...options.headers,
      },
    });

    if (!response.ok) {
      throw await this._buildError(response);
    }

    return response.json();
  }

  async getUsers(params = {}) {
    const query = new URLSearchParams(params).toString();
    return this._request(`/users${query ? `?${query}` : ''}`);
  }

  async getUser(userId) {
    return this._request(`/users/${userId}`);
  }

  async followLink(url) {
    // For pagination — accept the full URL from a previous response
    return this._request(url);
  }
}
```

This pattern:

* Defines the base URL in one place (easy to change for future v2).
* Auto-detects whether a path or full URL is passed (so `followLink` works with `links.next`).
* Centralizes auth headers (no per-call boilerplate).
* Throws structured errors (caller doesn't deal with raw `Response` objects).

***

## Where to go next

Walk through a slightly richer call now that the URL structure is clear.The Bearer token pattern used with this base URL.How the `links.next` URLs returned by the API work.The longer discussion of `/v1` and what a future `/v2` might mean.

The Volunteer API has a single documented base URL. This page covers the URL structure, the `/v1/` versioning convention, the HTTPS requirements, and what's known about test or sandbox environments.

## The single documented base URL

```text theme={null}
https://api.vomo.org/v1
```

That's the entire spec-documented surface. Every endpoint in the API is reached by appending the endpoint path to this base — `GET /v1/users` is `GET https://api.vomo.org/v1/users`.

| **Component**       | **Value**                                                                                   |
| :------------------ | :------------------------------------------------------------------------------------------ |
| **Protocol**        | `https` only — plain HTTP is not supported                                                  |
| **Host**            | `api.vomo.org`                                                                              |
| **Version segment** | `/v1` — present on every path                                                               |
| **Resource paths**  | `/users`, `/projects`, `/groups`, `/campaigns`, `/organizations`, `/forms`, `/certificates` |

A few example requests showing the full URL structure:

```bash theme={null}
GET  https://api.vomo.org/v1/users
GET  https://api.vomo.org/v1/users/12345
GET  https://api.vomo.org/v1/projects?page=2
POST https://api.vomo.org/v1/groups
GET  https://api.vomo.org/v1/forms/789/completions
```

The version segment (`/v1`) is part of the base URL — not part of individual endpoint paths. When the documentation refers to an endpoint as `GET /users`, the full URL is `https://api.vomo.org/v1/users`.

***

## The `/v1` versioning convention

Volunteer's base URL includes an explicit `/v1` segment. This is different from the other two Virtuous APIs:

| **API**       | **Base URL**                           | **Version segment?** |
| :------------ | :------------------------------------- | :------------------- |
| CRM+          | `https://api.virtuoussoftware.com/api` | No                   |
| Raise         | `https://prod-api.raisedonors.com/api` | No                   |
| **Volunteer** | `https://api.vomo.org/v1`              | **Yes**              |

### What this means for partner integrations

The presence of `/v1` suggests the platform may introduce future versions (`/v2`, etc.) without breaking existing integrations. Integrations targeting `/v1` should continue to work as long as the v1 contract is maintained, even after a v2 is released.

This is purely speculative for now — no v2 exists. But the URL pattern is set up for a future migration to be possible.

For practical integration code, hardcode the `/v1` in your base URL constant:

```javascript theme={null}
const VOMO_BASE_URL = 'https://api.vomo.org/v1';

async function get(path, options = {}) {
  return fetch(`${VOMO_BASE_URL}${path}`, options);
}

// Usage
const users = await get('/users');
const oneUser = await get('/users/12345');
```

If a v2 is released someday, switching is a one-line change to the constant — assuming the path structure is preserved.

See [<u>Versioning and Backward Compatibility</u>](https://markdowntorichtext.com/volunteer/best-practices/versioning-and-backward-compatibility) for the broader discussion.

***

## HTTPS is required

All Volunteer API requests must use HTTPS. The platform does not respond to plain HTTP requests, and any code that attempts `http://api.vomo.org/v1/users` will fail or be redirected.

| **Why HTTPS matters**                                                                       |
| :------------------------------------------------------------------------------------------ |
| The Bearer token in the `Authorization` header would be visible on the wire over plain HTTP |
| PII in responses (names, emails, addresses) would be visible over plain HTTP                |
| Modern browsers and many HTTP clients block or warn on plain-HTTP API calls                 |
| Compliance frameworks (PCI, SOC 2, GDPR) require encryption in transit                      |

In practice, this means every integration uses `https://` in its URL constant. Don't try to override certificate validation, use self-signed certs against the production host, or otherwise loosen the TLS posture.

### TLS version

The Volunteer API requires TLS 1.2 or later. Most modern HTTP libraries default to this, but legacy clients (very old Java, old PHP, certain embedded systems) may need explicit configuration.

If you see TLS handshake errors, check the client's TLS configuration before suspecting the API.

***

## Test and sandbox environments

The Volunteer OpenAPI spec documents one host: production. There's no separately-documented sandbox URL.

**⚠️ Spec gap:** The OpenAPI spec doesn't expose a sandbox or test environment. Test and development access is typically handled by issuing a token against a dedicated test organization on the production host, rather than by using a separate URL. Coordinate with the customer's VOMO concierge if a dedicated test environment is needed.

### What this means in practice

For partner integration development:

| **Approach**                                       | **When to use**                                                                                                             |
| :------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- |
| **Develop against a customer's test organization** | If the customer has a separate VOMO organization specifically for testing, use a token from that organization.              |
| **Develop against a developer-owned VOMO account** | The partner sets up their own VOMO account specifically for integration development.                                        |
| **Develop against production with care**           | Last resort — only if no test organization is available. Use clearly-marked test data (`dev+timestamp@example.com` emails). |

The production host is `https://api.vomo.org/v1` regardless of which approach you use. Isolation is at the organization (token) level, not the URL level.

### Don't develop against a real customer's production data

A common anti-pattern: developing the integration against a real customer's live VOMO account. This produces test users mingled with real users, test groups in production reports, and potentially destructive operations affecting real records.

The right path is to coordinate a test organization upfront. If the customer doesn't have one, request that one be set up before integration development begins.

***

## Service health and status

The Volunteer OpenAPI spec doesn't document a status page URL or health-check endpoint.

For partner integrations operating at scale, knowing when the API is degraded matters — sustained `5xx` responses or elevated latency may indicate a platform issue rather than an integration bug.

### What partner integrations can do

| **Practice**                                   | **Description**                                                                                        |
| :--------------------------------------------- | :----------------------------------------------------------------------------------------------------- |
| **Track 5xx response rate**                    | A spike across multiple customers usually means a platform issue, not a per-customer one.              |
| **Track API latency over time**                | Sudden latency increases without your traffic changing typically indicate platform-side issues.        |
| **Maintain a status page for the integration** | Independent of VOMO — communicates to your customers when your integration is degraded for any reason. |
| **Coordinate with VOMO during incidents**      | When sustained issues appear, reach out to VOMO support to confirm whether it's a platform issue.      |

Don't poll the API to detect health — that wastes rate-limit budget. Track the health of real workloads instead.

***

## Latency and geographic considerations

The spec doesn't document the API's deployment geography or expected latency from different regions. In practice, partner integrations should expect:

| **Source region** | **Expected latency to API**              |
| :---------------- | :--------------------------------------- |
| North America     | Low (typically \<100ms for a simple GET) |
| Europe            | Moderate (typically \<200ms)             |
| Asia / Pacific    | Higher (varies; can be >300ms)           |

These are typical web API characteristics, not specific guarantees. For latency-sensitive workloads, measure from your actual deployment environment.

### Implications for sync architecture

For integrations doing bulk reads (backfills, daily reconciliation), latency adds up. A backfill of 100,000 users with 100ms round-trip per request takes \~3 hours even with no rate-limit throttling. Plan for this when scoping the backfill window.

The patterns on [<u>Sync Architecture Patterns</u>](https://markdowntorichtext.com/volunteer/best-practices/sync-architecture-patterns) cover backfill design for large datasets.

***

## URL construction patterns

A few practices that prevent common URL-construction bugs:

### Don't hand-concatenate query parameters

```javascript theme={null}
// ❌ Anti-pattern — breaks on special characters
const url = `https://api.vomo.org/v1/users?name_like=${searchTerm}`;

// ✅ Use URLSearchParams (or equivalent)
const params = new URLSearchParams({ name_like: searchTerm });
const url = `https://api.vomo.org/v1/users?${params}`;
```

Names with spaces, apostrophes, or other characters need URL encoding. Manual concatenation forgets this; `URLSearchParams` handles it correctly.

### Don't hand-construct paginated URLs

```javascript theme={null}
// ❌ Anti-pattern — breaks if pagination changes
const nextUrl = `${baseUrl}/users?page=${currentPage + 1}`;

// ✅ Follow links.next from the previous response
const nextUrl = previousResponse.links.next;
```

The API tells you the next page's URL in `links.next`. Use it. Manual page-number construction works today but can break if the pagination scheme evolves.

### Don't strip `/v1/` from URLs returned by the API

The `links` field in paginated responses includes the full URL with `/v1/`:

```json theme={null}
{
  "links": {
    "next": "https://api.vomo.org/v1/users?page=2"
  }
}
```

If your code strips the version segment ("I'll add it myself when I make the call"), you'll either double-prefix or miss it entirely. Use returned URLs as-is.

### Be cautious with trailing slashes

The Volunteer paths don't include trailing slashes in the spec:

```text theme={null}
✅ /users
✅ /users/12345
❌ /users/      (trailing slash — typically rejected or redirected)
```

Most HTTP clients handle this correctly, but if you're constructing URLs by string template, double-check that you're not appending a stray `/`.

***

## A reference base URL setup

A minimal, well-organized base URL setup for a partner integration:

```javascript theme={null}
// config.js
export const VOMO_API_BASE_URL = 'https://api.vomo.org/v1';

// vomo-client.js
import { VOMO_API_BASE_URL } from './config.js';

export class VomoClient {
  constructor({ token }) {
    this.token = token;
  }

  async _request(path, options = {}) {
    const url = path.startsWith('http') ? path : `${VOMO_API_BASE_URL}${path}`;

    const response = await fetch(url, {
      ...options,
      headers: {
        Authorization: `Bearer ${this.token}`,
        Accept: 'application/json',
        ...options.headers,
      },
    });

    if (!response.ok) {
      throw await this._buildError(response);
    }

    return response.json();
  }

  async getUsers(params = {}) {
    const query = new URLSearchParams(params).toString();
    return this._request(`/users${query ? `?${query}` : ''}`);
  }

  async getUser(userId) {
    return this._request(`/users/${userId}`);
  }

  async followLink(url) {
    // For pagination — accept the full URL from a previous response
    return this._request(url);
  }
}
```

This pattern:

* Defines the base URL in one place (easy to change for future v2).
* Auto-detects whether a path or full URL is passed (so `followLink` works with `links.next`).
* Centralizes auth headers (no per-call boilerplate).
* Throws structured errors (caller doesn't deal with raw `Response` objects).

***

## Where to go next

Walk through a slightly richer call now that the URL structure is clear.The Bearer token pattern used with this base URL.How the `links.next` URLs returned by the API work.The longer discussion of `/v1` and what a future `/v2` might mean.
