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

# Patient Shop Subscriptions

> Create and manage product, custom-item, service, and package subscriptions through the public API.

# Patient Shop Subscriptions

Use `GET /shop` to read the same storefront configuration shown in VitaRelay. Every product, custom item, and package returns:

| Field                   | Meaning                                                              |
| ----------------------- | -------------------------------------------------------------------- |
| `item_kind`             | `product`, `custom`, or `bundle`                                     |
| `purchase_mode`         | `one_time`, `subscription`, or `both`                                |
| `available_cadences`    | `["weekly","monthly","quarterly"]` when subscribable, otherwise `[]` |
| `default_cadence`       | Cadence used when you omit one                                       |
| `first_order_immediate` | Bill and ship the first cycle right away                             |
| `requires_shipping`     | `false` for services (and service-only packages)                     |
| `requires_prescription` | Whether checkout requires a valid prescription                       |
| `patient_price_cents`   | What the patient pays                                                |

Cost and pharmacy fields are never returned.

## Create

Create subscriptions with `POST /subscriptions` and the `subscriptions:write` scope. Send the patient, item kind, storefront item id, quantity, cadence, saved payment method, and a saved address for shippable items.

```json theme={null}
{
  "patient_id": "3a2b1c0d-9e8f-4a7b-8c6d-5e4f3a2b1c0d",
  "item_kind": "product",
  "item_id": "0f1f2c9e-2f30-4a1b-8b1c-6f4e9a0c1d22",
  "quantity": 1,
  "cadence": "monthly",
  "payment_method_id": "9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b",
  "ship_to_address_id": "8d7e6f5a-4b3c-2d1e-0f9a-8b7c6d5e4f3a"
}
```

Services are charged each cycle without shipping or pharmacy dispatch. Rx validation applies only when the storefront item already requires a prescription. Prices are always resolved server-side from the live clinic storefront.

## Buy through POST /orders

Each order line may carry `purchase` (`one_time` default, or `subscription`), `cadence`, and `quantity`. Lines use either `product_id` (catalog product, unchanged behaviour) or `item_kind` + `item_id`. Subscription lines require a top-level `payment_method_id` (patient saved card) and use `ship_to_address_id` or the patient's preferred address.

```json theme={null}
{
  "patient_id": "3a2b1c0d-9e8f-4a7b-8c6d-5e4f3a2b1c0d",
  "payment_method_id": "9e8f7a6b-5c4d-3e2f-1a0b-9c8d7e6f5a4b",
  "items": [
    { "product_id": "aad522c1-a9fc-41c5-962e-a015f9cd4c95", "quantity": 1, "purchase": "subscription", "cadence": "monthly" },
    { "item_kind": "custom", "item_id": "5e4f3a2b-1c0d-4a7b-8c6d-9e8f7a6b5c4d", "quantity": 1, "purchase": "subscription" }
  ]
}
```

Response: `{ "data": { "id": null, "order_id": null, "status": "subscribed", "subscription_ids": ["…", "…"] } }`. When one-time lines are included, `order_id` is the order created for them and `subscription_ids` is returned alongside it.

| Error (422)                 | When                                                                                          |
| --------------------------- | --------------------------------------------------------------------------------------------- |
| `purchase_mode_not_allowed` | One-time purchase of a subscription-only item, or subscription to a one-time-only item        |
| `invalid_cadence`           | Cadence not weekly/monthly/quarterly, or none given and the item has no default               |
| `payment_method_required`   | Subscription line without a patient saved card you own                                        |
| `shipping_required`         | Shippable subscription item and the patient has no saved address (services never require one) |
| `unsupported_line`          | One-time custom items and packages; patients buy those in the storefront                      |

## Endpoint examples

`GET /subscriptions?patient_id=…&status=active` → `200`

```json theme={null}
{ "data": [{ "id": "0f1f2c9e-…", "item_kind": "product", "quantity": 1, "cadence": "monthly", "status": "active", "next_charge_at": "2026-10-25T04:06:14Z" }], "next_cursor": null }
```

`POST /subscriptions` → `201`

```json theme={null}
{ "data": { "subscription_id": "0f1f2c9e-…", "order_id": null, "status": "active", "next_charge_at": "2026-10-25T04:06:14Z" } }
```

`GET /subscriptions/{id}` → `200`

```json theme={null}
{ "subscription": { "id": "0f1f2c9e-…", "item_kind": "product", "quantity": 1, "cadence": "monthly", "status": "active", "paused_reason": null, "price_cents_snapshot": 6500, "next_charge_at": "2026-10-25T04:06:14Z", "card_brand": "Visa", "card_last4": "4242" }, "recent_cycles": [] }
```

`PATCH /subscriptions/{id}` with `{ "cadence": "quarterly" }` → `200`, same shape as GET.

`POST /subscriptions/{id}/pause`, `/resume`, `/cancel` → `200 { "ok": true }`

`POST /subscriptions/{id}/skip-next` → `200 { "ok": true, "next_charge_at": "2026-11-25T04:06:14Z" }`

## Manage

Use `GET /subscriptions`, `GET /subscriptions/{id}`, `PATCH /subscriptions/{id}`, and the `pause`, `resume`, `cancel`, and `skip-next` actions. Cross-organization resources return `404`.

Organizations operating under another white-label brand cannot mutate storefronts or subscriptions. Those writes return `403` with `white_label_restricted`.

## Events

Every subscription event's `data` contains `subscription_id`, `patient_id`, `item_kind`, `item_id`, `quantity`, `cadence`, `status`, `paused_reason`, and `next_charge_at`. `subscription.payment_failed` fires on every failed attempt with `attempt`, `max_attempts` (3), `next_retry_at`, and `final`; the third failure also emits `subscription.paused` with `reason: "payment_failed"`.

Subscribe to `subscription.created`, `subscription.updated`, `subscription.paused`, `subscription.resumed`, `subscription.canceled`, `subscription.renewed`, `subscription.payment_failed`, and `subscription.rx_hold`. Dry-run billing does not emit renewal or payment events.
