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

# REST API (v1)

> Public read-only REST endpoints authenticated with an API key.

# REST API (v1)

The VitaRelay public REST API exposes read-only endpoints for orders,
patients, prescriptions, and products. Every endpoint lives under
`/api/public/v1/*` and is authenticated with an API key issued to your
organization.

* **Base URL (production):** `https://vitarelay.com/api/public/v1`
* **Base URL (preview):** `https://project--{project-id}-dev.lovable.app/api/public/v1`
* **Auth header:** `Authorization: Bearer vr_live_...` (or `vr_test_...`)
* **Content type:** `application/json`

<Warning>
  This API returns rows scoped to the org that owns the key. It never
  returns wholesale cost, platform margin, commissions, or full patient PHI.
  Never share a key across organizations.
</Warning>

## Issuing keys

Keys are issued by VitaRelay. A VitaRelay administrator creates the key for
your organization and shares the raw value with you directly — there is no
self-serve key management. Every key belongs to exactly one org. To obtain,
rotate, or revoke a key, contact your VitaRelay representative.

The raw key is shared with you **once** — VitaRelay stores only its
SHA-256 hash. If a key is lost, ask VitaRelay to revoke it and issue a new one.

Scopes (each endpoint requires the matching scope):

| Scope                | Grants                                                     |
| -------------------- | ---------------------------------------------------------- |
| `orders:read`        | `GET /orders`, `GET /orders/{id}`                          |
| `patients:read`      | `GET /patients`, `GET /patients/{id}` (minimal projection) |
| `prescriptions:read` | `GET /prescriptions`, `GET /prescriptions/{id}`            |
| `products:read`      | `GET /products`                                            |

## Errors

Every error is a JSON envelope:

```json theme={null}
{ "error": { "code": "unauthorized", "message": "Invalid, revoked, or expired API key" } }
```

| Status | `code`               | When                                                  |
| ------ | -------------------- | ----------------------------------------------------- |
| 400    | `invalid_id`         | Path parameter is not a valid UUID                    |
| 401    | `unauthorized`       | Missing / malformed / invalid / revoked / expired key |
| 403    | `insufficient_scope` | Key does not carry the required scope                 |
| 404    | `not_found`          | Resource does not exist or belongs to another org     |
| 500    | `internal_error`     | Unexpected server failure                             |

## Pagination

List endpoints accept:

* `limit` — integer, 1–100 (default 25)
* `cursor` — ISO-8601 timestamp; return records **older than** this value

The response includes `next_cursor` when more rows may exist:

```json theme={null}
{
  "data": [ /* ... */ ],
  "next_cursor": "2026-06-14T18:22:41.019Z"
}
```

Pass that value back as `?cursor=` to fetch the next page. When
`next_cursor` is `null`, you have reached the end.

***

## Orders

`GET /api/public/v1/orders`

Query params: `limit`, `cursor`, `status` (e.g. `paid`, `shipped`,
`delivered`, `cancelled`).

Returns orders where the caller's org is any of `ordering_org_id`,
`affiliate_org_id`, or `fulfillment_pharmacy_org_id`.

Sample:

```bash theme={null}
curl -H "Authorization: Bearer vr_live_..." \
  "https://vitarelay.com/api/public/v1/orders?limit=25&status=shipped"
```

```json theme={null}
{
  "data": [
    {
      "id": "…",
      "order_number": "VR-2026-00184",
      "status": "shipped",
      "payment_status": "paid",
      "total_cents": 18400,
      "currency": "USD",
      "tracking_number": "1Z999AA10123456784",
      "tracking_carrier": "ups",
      "shipped_at": "2026-06-14T14:02:11Z",
      "patient_id": "…",
      "created_at": "2026-06-13T10:14:01Z"
    }
  ],
  "next_cursor": "2026-06-13T10:14:01Z"
}
```

`GET /api/public/v1/orders/{id}` returns the same shape for a single order.

Never returned: wholesale prices, commission splits, VitaRelay overhead,
supplier payout amounts, org attribution IDs.

***

## Patients

`GET /api/public/v1/patients`

Query params: `limit`, `cursor`.

Returns patients whose `org_id` or `branding_owner_org_id` matches the
caller's org. **Minimal projection** — never returns DOB, email, phone,
address, allergies, medications, or clinical notes.

```json theme={null}
{
  "data": [
    {
      "id": "…",
      "first_name": "Jane",
      "last_initial": "D",
      "assigned_physician_id": "…",
      "created_at": "2026-05-01T10:14:01Z",
      "updated_at": "2026-06-11T09:00:00Z"
    }
  ],
  "next_cursor": null
}
```

A future `patients:read_phi` scope (BAA-gated) will unlock a wider
projection.

***

## Prescriptions

`GET /api/public/v1/prescriptions`

Query params: `limit`, `cursor`, `status`.

Returns prescriptions where `provider_org_id` matches the caller's org.

```json theme={null}
{
  "data": [
    {
      "id": "…",
      "patient_id": "…",
      "provider_org_id": "…",
      "order_id": "…",
      "product_name": "Semaglutide 2mg/mL",
      "dosage": "0.25 mg weekly",
      "frequency": "weekly",
      "quantity": 4,
      "refills_authorized": 2,
      "status": "signed",
      "expires_at": "2027-06-01T00:00:00Z",
      "created_at": "2026-05-01T10:14:01Z"
    }
  ],
  "next_cursor": null
}
```

***

## Products

`GET /api/public/v1/products`

Query params: `limit`, `cursor`, `in_stock=true`.

Returns the caller pharmacy's catalog (rows from `pharmacy_products` with
`pharmacy_org_id = caller`). Wholesale cost and vendor cost are never
returned; consumer-facing pricing is exposed only through the storefront
or checkout flow.

```json theme={null}
{
  "data": [
    {
      "id": "…",
      "name": "BPC-157",
      "description": "…",
      "category": "peptide",
      "product_type": "compound",
      "strengths": ["5mg"],
      "dosage_forms": ["Vial"],
      "ndc_code": null,
      "in_stock": true,
      "requires_prescription": true,
      "ships_to_patient": true,
      "ships_to_office": true,
      "min_order_qty": 1,
      "status": "active",
      "created_at": "2026-01-11T09:00:00Z"
    }
  ],
  "next_cursor": null
}
```

***

## Rate limits

Public API traffic is subject to per-key rate limits (default 60
requests/minute). Bursts above the limit receive `429 Too Many Requests`
with a `Retry-After` header. Contact support if you need a higher tier.

## Rotating and revoking keys

Ask VitaRelay to rotate your key at least every 12 months, or immediately
after any suspected compromise. Revocation is also a request to VitaRelay and
takes effect on the next request — in-flight responses may still succeed for a
few seconds. See [API Keys](/getting-started/api-keys).
