Skip to main content

Patient Shop Subscriptions

Use GET /shop to read the same storefront configuration shown in VitaRelay. Every product, custom item, and package returns: 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.
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.
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.

Endpoint examples

GET /subscriptions?patient_id=…&status=active → 200
POST /subscriptions → 201 When the item bills its first cycle at signup (first_order_immediate), the first charge is made during this call, using the patient’s saved card. Otherwise it is scheduled for next_charge_at. First charge succeeded:
Card declined (the subscription is still created and the charge is retried on the normal schedule):
first_charge.status is one of charged, declined (card refused), failed (the order could not be created; nothing was charged), not_charged (billing is switched off for the account), or scheduled (not due yet; no order is returned). decline_reason is a short, patient-safe message from the card network, or null. GET /subscriptions/{id} → 200
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.

Saving a card for auto-renew (embedded checkout)

When paying an order with POST /v1/orders/{id}/pay, add "save_card": true alongside opaque_data. If the charge is approved, VitaRelay saves the card to the patient’s wallet (it becomes their default card if they don’t have one), and future subscription renewals charge it automatically. The response includes saved_payment_method_id (null if nothing was saved, including sandbox/dry-run orders). Only send save_card after the patient has agreed to have their card kept on file.