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

# Authentication

> How to authenticate to the Volunteer API — Bearer token issuance through the VOMO admin portal, the Authorization header pattern, and the practices for storing, rotating, and revoking tokens.

The Volunteer API authenticates with a single mechanism: a Bearer token in the HTTP `Authorization` header. The pattern is straightforward and follows [IETF RFC 6750](https://tools.ietf.org/html/rfc6750), but a few specifics about how Volunteer's tokens are issued and managed matter for partner integrations.

This page covers obtaining the token, using it on every request, storing it securely, rotating it, and handling auth failures.

## At a glance

| Concern          | How Volunteer handles it                                           |
| ---------------- | ------------------------------------------------------------------ |
| Auth mechanism   | Bearer token in `Authorization` header                             |
| Token format     | JWT (per `eyJhbGci...` prefix in examples)                         |
| Token issuance   | Generated in the VOMO admin portal by the customer's administrator |
| Token scope      | A single VOMO organization                                         |
| Token expiration | Not documented in the spec                                         |
| Token rotation   | Manual — administrator regenerates and provides new token          |
| Revocation       | Done in the admin portal                                           |

***

## Obtaining a token

Volunteer tokens are issued by the customer (or their VOMO concierge) — not by the partner integration. The flow:

<Steps>
  <Step title="Confirm API access is enabled for the customer's VOMO account">
    API access is not on by default. The customer or partner contacts the customer's VOMO concierge to enable it. Until enabled, no tokens can be generated.
  </Step>

  <Step title="The customer's VOMO administrator generates a token in the admin portal">
    Logged into the VOMO admin UI, the administrator navigates to the API access section and generates a new token. The token displays once; the administrator copies it.
  </Step>

  <Step title="The customer provides the token to the partner integration">
    Through whatever secure-handoff mechanism the partner has built into onboarding — typically a settings UI in the partner's product where the customer pastes the token, transmitted over HTTPS.
  </Step>

  <Step title="The partner stores the token securely">
    In a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.). One token per customer; never share tokens across customers.
  </Step>
</Steps>

<Note>
  The exact UI path in the VOMO admin portal for token generation, and whether tokens can be named or scoped beyond "organization access," is configured per-customer. Coordinate with the customer's VOMO concierge if a specific token-management feature is needed.
</Note>

***

## Using the token

Every authenticated request includes the token in the `Authorization` header:

```http theme={null}
GET /v1/users HTTP/1.1
Host: api.vomo.org
Authorization: Bearer eyJhbGci.....yu5CSpyHI
Accept: application/json
```

The full header value is the literal string `Bearer ` (with a trailing space) followed by the token. Don't omit the space; don't include extra characters before or after.

### In code

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

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

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://api.vomo.org/v1/users',
      headers={'Authorization': f'Bearer {token}'},
  )
  ```
</CodeGroup>

The token goes on every request — there's no separate "login" flow that exchanges credentials for a session. Every API call independently authenticates.

***

## Storing the token

Tokens grant full access to the customer's VOMO data. Treat them with the same care as production database passwords.

### Production: secrets manager

| Service                           | Use                                       |
| --------------------------------- | ----------------------------------------- |
| AWS Secrets Manager               | Standard for AWS-deployed integrations    |
| HashiCorp Vault                   | Cloud-agnostic; works across environments |
| Google Secret Manager             | Standard for GCP-deployed integrations    |
| Azure Key Vault                   | Standard for Azure-deployed integrations  |
| Doppler, 1Password Business, etc. | Smaller-team alternatives                 |

A simple storage abstraction:

```javascript JavaScript theme={null}
class VomoCredentials {
  constructor(secretsManager) {
    this.secretsManager = secretsManager;
  }

  async getToken(customerId) {
    const secret = await this.secretsManager.getSecret(
      `vomo-integration/${customerId}/api-token`
    );
    return secret.value;
  }

  async setToken(customerId, token) {
    await this.secretsManager.putSecret(
      `vomo-integration/${customerId}/api-token`,
      { value: token, updatedAt: new Date().toISOString() }
    );
  }
}
```

One key per customer. The hierarchical key path (`vomo-integration/{customerId}/api-token`) makes per-customer auditing and access control straightforward.

### Local development: environment variables

For a developer's local machine, an environment variable in a `.env` file is acceptable:

```bash theme={null}
# .env (do not commit to git)
VOMO_API_TOKEN=eyJhbGci.....yu5CSpyHI
```

Add `.env` to `.gitignore`. Never commit it to source control.

### Anti-patterns

Several token-handling patterns that look reasonable but cause production issues:

| Anti-pattern                                                        | Why it's bad                                                                           |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| Hardcoding the token in source code                                 | Anyone with repo access has the token. Hard to rotate.                                 |
| Storing the token in an unencrypted database column                 | If the DB is compromised, every customer's token is exposed.                           |
| Committing the token in a config file (even encrypted)              | The encrypted blob in git history is recoverable; assume the key will leak eventually. |
| Logging the full token in application logs                          | Logs frequently get aggregated, archived, and shared — extending exposure.             |
| Sharing the same token across customers                             | Defeats per-customer isolation; one compromised integration leaks everyone.            |
| Sending the token through unencrypted channels (Slack, email, etc.) | Hard to revoke once it's in someone's chat history.                                    |

***

## Per-customer isolation

The Volunteer token is scoped to a single VOMO organization — the organization whose administrator generated it. A token for "Wayne Foundation" can only read Wayne Foundation's data; it cannot read or write any other customer's data.

For partner integrations serving multiple customers, this means **one token per customer**. The integration code looks up the right token for the customer it's currently serving.

```javascript JavaScript theme={null}
async function listUsersForCustomer(customerId) {
  const token = await credentials.getToken(customerId);

  return fetch('https://api.vomo.org/v1/users', {
    headers: { Authorization: `Bearer ${token}` },
  }).then((r) => r.json());
}
```

The customer ID is the routing key for which token to use. Don't try to centralize tokens; the per-customer isolation is a security feature.

### What happens if you use the wrong token

If you accidentally use Customer A's token while calling endpoints scoped to Customer B's data, the response depends on how VOMO authorizes:

* For data in Customer A's organization, the call succeeds.
* For data outside Customer A's organization, the call typically returns `403 Forbidden` or `404 Not Found`.

The exact behavior isn't documented in the spec, but the practical implication is: token mismatches produce errors, not silent data leaks. Still, the right discipline is to never let token mismatches happen — keep the customer→token lookup tight.

***

## Token rotation

Tokens should be rotated periodically — typically every 90 days for high-stakes integrations, every 6–12 months for lower-stakes ones. The rotation flow:

<Steps>
  <Step title="The customer's VOMO administrator generates a new token">
    Through the admin portal. The old token typically remains valid during the rotation window.
  </Step>

  <Step title="The customer provides the new token to the partner">
    Same secure-handoff mechanism as initial setup.
  </Step>

  <Step title="The partner stores the new token alongside the old one">
    Both are marked as valid in the credential store.
  </Step>

  <Step title="The partner switches to the new token">
    New requests use the new token. The old token can still be used for in-flight requests if needed.
  </Step>

  <Step title="Confirm requests succeed with the new token">
    Monitor for any `401` errors that would indicate a problem.
  </Step>

  <Step title="The customer revokes the old token in VOMO">
    Once new-token requests are verified working, the administrator revokes the old token.
  </Step>

  <Step title="The partner removes the old token from the secrets manager">
    Completing the rotation cleanly.
  </Step>
</Steps>

For partner integrations with many customers, track rotation centrally:

```javascript JavaScript theme={null}
async function customersNeedingRotation(maxAgeDays = 90) {
  const all = await credentials.listAll();
  const cutoff = new Date(Date.now() - maxAgeDays * 24 * 60 * 60 * 1000);

  return all.filter((c) => c.tokenUpdatedAt < cutoff);
}

// Run weekly — send reminders to customers approaching their rotation deadline
async function rotationReminderJob() {
  const dueSoon = await customersNeedingRotation(80); // 10-day warning
  for (const customer of dueSoon) {
    await emailService.send({
      to: customer.adminEmail,
      template: 'vomo-token-rotation-reminder',
      data: { lastRotated: customer.tokenUpdatedAt },
    });
  }
}
```

See [Security and Credential Management](/volunteer/best-practices/security-and-credential-management) for the broader credential management practices.

***

## Handling auth failures

A `401 Unauthorized` response from any endpoint means the token isn't being accepted. Common causes:

| Cause                                                                              | What to do                                                              |
| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| The token has been revoked                                                         | Coordinate with the customer to issue a new one.                        |
| The token has expired (if Volunteer enforces expiration)                           | Same — issue a new one.                                                 |
| The token is malformed in the request (whitespace, missing `Bearer ` prefix, etc.) | Inspect the actual request — most often the bug is on the partner side. |
| API access was disabled for the customer's account                                 | Contact the customer's VOMO concierge.                                  |
| The customer's VOMO account was suspended or deleted                               | Pause sync work for this customer and surface for human review.         |

### A defensive pattern

```javascript JavaScript theme={null}
async function callVolunteerApi(url, options, customerId) {
  const token = await credentials.getToken(customerId);

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

  if (response.status === 401) {
    // Don't retry — token issue, not a transient error
    await alertOps({
      severity: 'high',
      customerId,
      message: 'VOMO token rejected — credential needs refresh',
    });
    await pauseIntegrationForCustomer(customerId, 'auth_failed');
    throw new AuthError('VOMO token is not valid');
  }

  return response;
}
```

The critical detail: **don't retry on `401`.** Retries against an invalid token just produce more `401`s and noise. Pause the customer's work and alert.

See [Error Handling](/volunteer/error-handling) for the full error-classification pattern.

***

## Handling permission errors

A `403 Forbidden` response indicates the token is valid but doesn't have permission for the specific endpoint or resource. Causes:

| Cause                                                                  | Action                                                                 |
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| The token's organization doesn't have access to the requested resource | The resource belongs to a different organization; don't retry.         |
| The customer's plan doesn't include the requested feature              | Coordinate with the customer's VOMO concierge to check feature access. |
| The endpoint requires a higher permission tier than the token has      | Same — confirm the token's permission scope.                           |

Like `401`, **`403` should not be retried.** It's a permission issue, not a transient failure.

```javascript JavaScript theme={null}
if (response.status === 403) {
  await alertOps({
    severity: 'medium',
    customerId,
    endpoint: url,
    message: 'VOMO token lacks permission for this endpoint',
  });
  throw new ForbiddenError(`No permission for ${url}`);
}
```

***

## Local development setup

For developers building or debugging integrations locally:

<Steps>
  <Step title="Coordinate with the customer to set up a development environment">
    Many customers have a dedicated test or sandbox VOMO instance for partner development. If yours doesn't, use the production environment with care.
  </Step>

  <Step title="Receive a dedicated development token">
    Either a separate token from a sandbox account, or a clearly-marked development token from the production account.
  </Step>

  <Step title="Store the token in a `.env` file (gitignored)">
    Never commit dev tokens to source control.
  </Step>

  <Step title="Make a test call to confirm everything works">
    A `GET /v1/users?per_page=1` is a minimal validation request.
  </Step>

  <Step title="When the work is done, revoke the development token">
    Coordinate with the customer's administrator. Don't leave development tokens active indefinitely.
  </Step>
</Steps>

### A simple dev test

```bash theme={null}
# Confirm token works
curl https://api.vomo.org/v1/users \
  -H "Authorization: Bearer $VOMO_API_TOKEN" \
  | jq '.meta.total'

# Expected: a number (the total user count)
# If you get 401: token issue
# If you get an HTML response: hit a wrong URL (perhaps missing /v1)
```

***

## Compliance considerations

For partner integrations operating in regulated jurisdictions:

| Concern                   | Implication                                                                                                                                       |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **GDPR / data privacy**   | The token grants access to PII. Document its access in your data processing agreement with the customer.                                          |
| **SOC 2**                 | Token storage, rotation, and access auditing are SOC 2 control areas.                                                                             |
| **HIPAA (if applicable)** | If the customer's VOMO usage touches healthcare data, the integration may inherit HIPAA obligations. Confirm with the customer's compliance team. |

The token-handling practices on this page support most common compliance frameworks. For specific requirements, consult your compliance lead.

***

## Auth checklist

Walk through this when integrating with a new customer:

* API access is enabled for the customer's VOMO account
* The customer's administrator has issued a Bearer token through the admin portal
* The token is stored in a secrets manager (production) or `.env` file (dev)
* The token is never logged in plaintext
* One token per customer — no sharing across customers
* HTTPS is used for every request (not HTTP)
* `401` and `403` handling pauses work rather than retrying
* Token rotation is scheduled and tracked
* Customer offboarding includes token revocation
* Audit logs capture token read operations

These practices map directly to the security best-practices pattern documented in [Security and Credential Management](/volunteer/best-practices/security-and-credential-management).

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Base URLs" icon="link" href="/volunteer/base-urls">
    The base URL details and the `/v1/` versioning convention.
  </Card>

  <Card title="First API Call" icon="rocket" href="/volunteer/first-api-call">
    A guided first call now that authentication is set up.
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/volunteer/error-handling">
    The full error-handling pattern, including auth and permission failures.
  </Card>

  <Card title="Security and Credential Management" icon="lock" href="/volunteer/best-practices/security-and-credential-management">
    The deeper-dive on token storage, rotation, and operational practices.
  </Card>
</CardGroup>
