> ## 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.

# Quickstart

> A minimal end-to-end Volunteer API integration — obtain a token, make your first call, parse the response, and understand what just happened.

This quickstart walks through the smallest possible Volunteer API integration: obtain a Bearer token, list the customer's first page of users, and parse the response. The goal is to confirm the integration's plumbing works end-to-end before building anything substantial.

By the end you'll have a working API call, a parsed response with users in hand, and a clear picture of what each piece of the response means.

## Prerequisites

| Requirement                            | How to obtain                                                                       |
| -------------------------------------- | ----------------------------------------------------------------------------------- |
| **A VOMO customer account**            | The customer signs up at vomo.org or coordinates with their VOMO concierge          |
| **API access enabled for the account** | Coordinate with the VOMO concierge to enable API access (this is not on by default) |
| **A Bearer token**                     | The customer's VOMO administrator generates the token in the admin portal           |
| **A tool to make HTTP requests**       | curl, Postman, or any language with an HTTP client                                  |

If you're new to a Volunteer integration, the first three steps are typically done by the customer or their VOMO concierge — the partner integration team coordinates to receive the token. See [Authentication](/volunteer/authentication) for the full token issuance flow.

***

## Step 1: store the token securely

Once you receive the Bearer token from the customer, store it in a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.). For local development, an environment variable is acceptable:

```bash theme={null}
export VOMO_API_TOKEN="eyJhbGci.....yu5CSpyHI"
```

Don't paste the token directly into source code or commit it to a repository. The token grants full access to the customer's VOMO data; treat it like a password.

## Step 2: make the first request

The smallest useful call: list the first page of users in the customer's account.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.vomo.org/v1/users \
    -H "Authorization: Bearer $VOMO_API_TOKEN" \
    -H "Accept: application/json"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.vomo.org/v1/users', {
    headers: {
      Authorization: `Bearer ${process.env.VOMO_API_TOKEN}`,
      Accept: 'application/json',
    },
  });

  const result = await response.json();
  console.log(result);
  ```
</CodeGroup>

Three things to notice about the request:

| Element                     | Description                                                                      |
| --------------------------- | -------------------------------------------------------------------------------- |
| `Authorization: Bearer ...` | The token in the `Authorization` header. Required on every request.              |
| `Accept: application/json`  | Optional but explicit — confirms you want JSON. The API returns JSON regardless. |
| No query parameters         | Returns the first page with default page size.                                   |

## Step 3: read the response

A successful response looks like this (truncated for readability):

```json theme={null}
{
  "data": [
    {
      "type": "user",
      "id": 1234,
      "first_name": "Bruce",
      "last_name": "Wayne",
      "full_name": "Bruce Wayne",
      "email": "bruce@wayne.example",
      "address": "1007 Mountain Drive, Gotham",
      "phone": "+15551234567",
      "birthday": "1972-02-19",
      "gender": "M",
      "updated_at": "2025-03-15T14:22:10Z",
      "created_at": "2024-08-01T09:00:00Z",
      "user_status": "VERIFIED",
      "membership_status": "ACCEPTED",
      "membership_role": "VOLUNTEER"
    }
    /* ... more users ... */
  ],
  "links": {
    "first": "https://api.vomo.org/v1/users?page=1",
    "last": "https://api.vomo.org/v1/users?page=8",
    "prev": null,
    "next": "https://api.vomo.org/v1/users?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "to": 15,
    "per_page": 15,
    "last_page": 8,
    "total": 120
  }
}
```

Three top-level fields, each with a specific role:

| Field   | Purpose                                                                                                             |
| ------- | ------------------------------------------------------------------------------------------------------------------- |
| `data`  | The array of resources for this page. Each element is a User.                                                       |
| `links` | URLs for navigating between pages — `first`, `last`, `prev`, `next`. The `prev` and `next` are `null` at the edges. |
| `meta`  | Numeric metadata about the pagination state — current page, page size, total records, etc.                          |

For the first call, the most important field is `meta.total` — it tells you how many users the customer has in total. Paired with `meta.per_page`, you know how many pages you'll need to read to capture everything.

## Step 4: parse and iterate

A minimal pattern for processing the response:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function listAllUsers() {
    const allUsers = [];
    let url = 'https://api.vomo.org/v1/users';

    while (url) {
      const response = await fetch(url, {
        headers: {
          Authorization: `Bearer ${process.env.VOMO_API_TOKEN}`,
          Accept: 'application/json',
        },
      });

      if (!response.ok) {
        throw new Error(`Failed: ${response.status}`);
      }

      const page = await response.json();
      allUsers.push(...page.data);

      // Follow the next link, or stop if there isn't one
      url = page.links.next;
    }

    return allUsers;
  }

  const users = await listAllUsers();
  console.log(`Loaded ${users.length} users`);
  ```

  ```bash cURL theme={null}
  # First page
  curl "https://api.vomo.org/v1/users?page=1" \
    -H "Authorization: Bearer $VOMO_API_TOKEN"

  # Second page
  curl "https://api.vomo.org/v1/users?page=2" \
    -H "Authorization: Bearer $VOMO_API_TOKEN"

  # Continue until links.next is null
  ```
</CodeGroup>

Two things this pattern gets right:

* **Follow `links.next`** rather than constructing page URLs manually. The platform provides the link; use it.
* **Stop when `links.next` is `null`**. That's the signal there are no more pages.

For more on pagination patterns, see [Pagination](/volunteer/pagination).

***

## Step 5: handle errors

The spec is sparse on documented error responses, but partner code should still handle common failure modes:

```javascript JavaScript theme={null}
const response = await fetch('https://api.vomo.org/v1/users', {
  headers: { Authorization: `Bearer ${token}` },
});

if (response.status === 401) {
  // Token is invalid, expired, or revoked
  throw new Error('Authentication failed — check the token');
}

if (response.status === 403) {
  // Token doesn't have permission for this resource
  throw new Error('Access forbidden — token lacks permission');
}

if (response.status === 404) {
  // Resource not found
  throw new Error('Resource not found');
}

if (response.status === 429) {
  // Rate limited (not explicitly documented in spec but possible)
  const retryAfter = response.headers.get('Retry-After');
  throw new Error(`Rate limited — retry after ${retryAfter} seconds`);
}

if (response.status >= 500) {
  // Server error — likely transient, retry with backoff
  throw new Error('Server error — retry with backoff');
}

if (!response.ok) {
  throw new Error(`Unexpected error: ${response.status}`);
}
```

<Warning>
  **⚠️ Spec gap:** The Volunteer OpenAPI spec doesn't formally document `401`, `403`, `404`, or `500` responses on most endpoints. The handling above is defensive against what the live API likely returns. Confirm against actual responses for production-critical paths. See [Error Handling](/volunteer/error-handling) for the full pattern.
</Warning>

***

## Step 6: try a filter

The list endpoint accepts several filter parameters. Try filtering users by name:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.vomo.org/v1/users?name_like=wayne" \
    -H "Authorization: Bearer $VOMO_API_TOKEN"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.vomo.org/v1/users?name_like=wayne',
    { headers: { Authorization: `Bearer ${token}` } }
  );
  const result = await response.json();
  console.log(`Found ${result.meta.total} matching users`);
  ```
</CodeGroup>

The `name_like` parameter does a substring match against first and last name. Other available filters on `GET /users`:

| Parameter        | What it filters by                 |
| ---------------- | ---------------------------------- |
| `name_like`      | First or last name substring match |
| `email_like`     | Email substring match              |
| `created_before` | Users created on or before a date  |
| `created_after`  | Users created on or after a date   |
| `updated_before` | Users updated on or before a date  |
| `updated_after`  | Users updated on or after a date   |
| `page`           | Page number (default 1)            |

All parameters use `snake_case` and are documented in the spec for each endpoint.

***

## What you've accomplished

After this quickstart, you have:

* A working API call that lists users from a customer's VOMO account
* Confirmed the Bearer token is valid
* A pattern for iterating through paginated results
* Defensive error handling for common failure modes
* A working filter to narrow results

This is the foundation. Every other integration pattern builds on top of these basics.

***

## Common gotchas

A few things that trip up new Volunteer integrations:

### Forgetting the `/v1/` segment

Volunteer's base URL is `https://api.vomo.org/v1` — the `/v1/` is part of the path. Requests to `https://api.vomo.org/users` (without `/v1`) will fail. This differs from Raise, which has no version segment.

### Assuming camelCase

`createdAfter` and `nameLike` don't work — the parameters are `created_after` and `name_like` with underscores. The same applies to response properties: `firstName` doesn't appear in the response, but `first_name` does.

### Treating `meta.total` as a moving target

If new users are added between page reads, `meta.total` can change mid-iteration. For strict consistency, capture `meta.total` from the first page and use it as the iteration bound, accepting that the snapshot may not be perfectly current. For most analytics, this is acceptable.

### Treating `null` strings as null

The spec example for `links.prev` and `links.next` shows the value as the literal string `"null"`. The live API returns actual JSON `null` (no quotes). Code that checks for the string `"null"` will incorrectly treat all pages as having a previous and next page.

```javascript JavaScript theme={null}
// ❌ Incorrect — treats string "null" as truthy
if (page.links.next) { /* ... */ }
// Works correctly because actual API returns real null

// ❌ Incorrect — looking for the string
if (page.links.next !== 'null') { /* ... */ }
// Wrong — this checks for the literal four-character string
```

The live API returns `null`, not `"null"`. Check with `=== null` or simple truthiness.

### Tokens are per-organization

The Bearer token is scoped to a single VOMO organization. A token issued for "Wayne Foundation" can only read Wayne Foundation's data. For multi-customer partner integrations, store one token per customer.

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/volunteer/authentication">
    The full token issuance, storage, and rotation flow.
  </Card>

  <Card title="First API Call" icon="rocket" href="/volunteer/first-api-call">
    A deeper walkthrough of the request/response patterns introduced here.
  </Card>

  <Card title="Pagination" icon="list" href="/volunteer/pagination">
    The full pagination pattern with edge cases and performance considerations.
  </Card>

  <Card title="The Volunteer Data Model" icon="diagram-project" href="/volunteer/concepts/data-model">
    What's available beyond users — the full resource model.
  </Card>
</CardGroup>
