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

# Authentication

> How to authenticate API requests using API keys.

# Authentication

Every VitaRelay API request is authenticated with an **API key** passed as a
bearer token in the `Authorization` header. Keys are issued to your organization
by a VitaRelay administrator and shared with you directly — there is no OAuth
flow, no token exchange, and no self-serve key creation. The key you receive is
the credential you send.

```bash theme={null}
Authorization: Bearer vr_live_xxxxxxxxxxxxxxxxxxxx
```

Requests without a valid key are rejected before any work is done.

```bash theme={null}
curl https://vitarelay.com/api/public/v1/orders \
  -H "Authorization: Bearer vr_live_xxxxxxxxxxxxxxxxxxxx"
```

## Live and test keys

Each key belongs to exactly one environment, encoded in its prefix:

| Prefix     | Environment | Behavior                                                   |
| ---------- | ----------- | ---------------------------------------------------------- |
| `vr_live_` | Live        | Acts on production data; orders are charged and routed     |
| `vr_test_` | Test        | Fully isolated sandbox; nothing real is created or charged |

Both use the **same base URL** — `https://vitarelay.com/api/public/v1`. There is
no separate sandbox host. See [Sandbox](/getting-started/sandbox) for how test
keys behave, and [API Keys](/getting-started/api-keys) for how keys are issued.

## Scopes

A key only grants the scopes VitaRelay selected when it was issued. A request
whose endpoint requires a scope the key does not carry is rejected with `403
insufficient_scope`.

| Scope                | Grants                                          |
| -------------------- | ----------------------------------------------- |
| `patients:read`      | `GET /patients`, `GET /patients/{id}`           |
| `orders:read`        | `GET /orders`, `GET /orders/{id}`               |
| `prescriptions:read` | `GET /prescriptions`, `GET /prescriptions/{id}` |
| `products:read`      | `GET /products`                                 |
| `patients:write`     | `POST /patients`                                |
| `intakes:write`      | `POST /intakes`                                 |
| `orders:write`       | `POST /orders`                                  |

<Warning>
  Follow the principle of least privilege. Ask VitaRelay for a separate key per
  integration, carrying only the scopes that integration needs, so one key can
  be revoked without affecting anything else.
</Warning>

## Write endpoints require a Vita Clinic

The three write endpoints have an additional requirement beyond scope: the
organization that owns the key must be an **active Vita Clinic**. A key held by
any other organization type is rejected with `403 not_vita_clinic`, even if the
`:write` scope is present.

## Keeping keys secure

* **Never commit API keys to source control.** Use environment variables.
* **One key per integration**, so one can be revoked without downtime elsewhere.
* **Ask VitaRelay to rotate periodically**, and immediately after any suspected
  compromise — rotation and revocation are requests to VitaRelay, not self-serve
  actions. See [API Keys](/getting-started/api-keys).
* **Develop against `vr_test_` keys** so mistakes never touch production data.

## Error responses

Authentication and authorization failures use the standard error envelope.

| Status | `code`               | Meaning                                        |
| ------ | -------------------- | ---------------------------------------------- |
| `401`  | `unauthorized`       | Key is missing, malformed, revoked, or expired |
| `403`  | `insufficient_scope` | Key is valid but lacks the required scope      |
| `403`  | `not_vita_clinic`    | Write endpoint called by a non-Vita-Clinic org |

```json theme={null}
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the patients:write scope"
  }
}
```

See [Errors](/getting-started/errors) for the full code table.
