Download OpenAPI specification:
Partner API v2 for managing orders, subscriptions, customers, checkout, and fulfillment.
This API uses the OAuth 2.0 Client Credentials flow with a v2 API key
minted in the partner portal. v2 credentials are separate from v1: a v1
key cannot call any /v2/* endpoint and a v2 key cannot call /v1/*.
Scopes. Read endpoints require order/v2.read (product catalog
endpoints: order/v2.product.read). Write endpoints (POST, PUT, PATCH,
DELETE) require both the read scope and its .write counterpart on
the token. Each operation below lists its exact scopes.
Token Endpoints:
https://auth.fulfillment.sandbox.juniperhealth.com/oauth2/tokenhttps://auth.fulfillment.juniperhealth.com/oauth2/tokenRequest:
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600,
"token_type": "Bearer"
}
Include the token in API requests using the Authorization header:
Authorization: Bearer YOUR_ACCESS_TOKEN
A missing or unparseable token returns 401; a token that lacks a required scope, is expired, or belongs to a suspended partner returns 403.
payload objects, and the order shipping-address
update body. In amendment and scheduled-change payloads the older
snake_case spellings (product_id, take_with) are still accepted on
input; responses and webhooks always use productId and takeWith.{"message": "...", "code": "..."} bodies, plus a
details object when the refusal names specifics. Branch on code,
never on message — messages are reworded without notice, and details
is omitted rather than sent as null. Validation failures are 422
with code: VALIDATION_FAILED; field locations and rejected values are
never sent back. Two bodies differ by design: partner-hosted (Elements)
checkout refusals carry the code in error ({"message", "error"}),
and a 401 from the API gateway itself carries message only.cursor on most lists, nextCursor on checkout sessions, and
customer lists also carry hasMore). Filtered pages can be short or
empty while more results remain.Submit a customer order with product line items and shipping details. Pricing is computed and frozen at creation (the settlement snapshot); later catalog or discount changes never affect an existing order.
shippingMethod must match one of your configured shipping methods —
otherwise the request fails with a 400 whose body lists
availableShippingMethods.
There is no idempotency key: retrying a successful create makes a
duplicate order (never a duplicate customer). Creating an order emits
the order.created webhook event and — unlike v1 — sends your
customer a branded order confirmation email (toggleable in partner
settings).
The order is resolved to a Customer record in your account, first
step that yields one wins: a top-level customerId (404 and no
order if it does not resolve), else customer.sourceId, else
customer.email — accepted only when exactly one active record
matches, it is neither a caregiver-managed dependent nor a caregiver
with dependents, and the names agree — else a record is created
from the snapshot when it carries an email. A created record has no
sourceId. The linked id is echoed as customerId; it is null only
when no usable email was sent or the email match was refused. Only a
customerId or sourceId match applies the customer's
discountOverride and stamps isFirstOrder; an email match or a
created record prices at your partner rate.
Order details to be created in the system
required | object Customer information for the order | ||||||||||||||||||||||||
| |||||||||||||||||||||||||
| daysSupply required | integer [ 14 .. 120 ] Number of days the order is intended to supply (any value 14–120) | ||||||||||||||||||||||||
required | Array of objects (LineItemRequest) [ 1 .. 99 ] items Products included in the order | ||||||||||||||||||||||||
Array ([ 1 .. 99 ] items)
| |||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method for the order. Must also be one of the methods configured for your account, or the request fails with 400 listing | ||||||||||||||||||||||||
| orderType required | string Enum: "ONE_TIME" "SUBSCRIPTION" Type of order | ||||||||||||||||||||||||
| customerId | string or null Existing Customer id to link this order to. Takes precedence over | ||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who was charged for this order, frozen when it was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| id | string Unique order identifier | ||||||||||||||||||||||||||||||||||||||||||||||
| status | string Enum: "NEW" "PROCESSING" "DISPENSING" "FILLED" "FULFILLED" "AWAITING_SHIPMENT" "SHIPPED" "DELIVERED" "EXCEPTION" "CANCELLED" Current status. | ||||||||||||||||||||||||||||||||||||||||||||||
| isFirstOrder | boolean Whether this is the linked customer's first order with your account. Computed at creation. False when the order is not linked, and false for a customer the order itself created or matched by email — only a customer referenced by | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId | string or null Internal id of the Customer record this order is linked to; null when the order could not be linked. | ||||||||||||||||||||||||||||||||||||||||||||||
| subscriptionId | string or null The subscription that generated this order; null for one-time and API orders. A customer with more than one subscription cannot be attributed from | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId | string or null The protocol this order was built from; null for API orders. It survives a subscription being replaced, so it is the stabler key for grouping a customer's recurring orders. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null The card that paid for this order, recorded once when the payment settled; reading the order never asks the payment provider. null when no card is recorded: orders paid before this field existed, payments that were not by card, orders with no customer payment (such as those created with | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| discount | string Deprecated Vestigial; always "0.00" | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply | integer Number of days the order supplies | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType | string Enum: "ONE_TIME" "SUBSCRIPTION" | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" | ||||||||||||||||||||||||||||||||||||||||||||||
object (FulfillmentInfo) Details about order fulfillment and shipping. All fields are nullable and populate as fulfillment progresses. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
Array of objects (LineItem) Products included in the order | |||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
object Embedded order customer snapshot (not the full Customer resource) | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| retailSubtotal | string Sum of retail (MSRP) line totals | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerSubtotal | string Wholesale subtotal the partner pays | ||||||||||||||||||||||||||||||||||||||||||||||
| customerSubtotal | string Customer subtotal | ||||||||||||||||||||||||||||||||||||||||||||||
| totalDiscount | string Order-level customer discount amount | ||||||||||||||||||||||||||||||||||||||||||||||
| totalFees | string Platform handling fees on external products. Under Juniper-managed billing these reduce commission and are not charged to the customer; under partner-managed billing they are part of the total. | ||||||||||||||||||||||||||||||||||||||||||||||
| totalShipping | string Shipping charged | ||||||||||||||||||||||||||||||||||||||||||||||
| totalTax | string Tax charged ("0.00" for API-created orders) | ||||||||||||||||||||||||||||||||||||||||||||||
| totalVolumeDiscount | string Volume discount applied automatically from your configured tiers | ||||||||||||||||||||||||||||||||||||||||||||||
| customerTotal | string Final total charged to the customer (frozen at creation) | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerCommission | string Frozen commission; may be negative | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
{- "customer": {
- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "address": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}
}, - "customerId": "string",
- "daysSupply": 30,
- "lineItems": [
- {
- "productId": 77123129891,
- "quantity": 2,
- "packDescription": "Whole Body Wellness",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "templateId": "string"
}
], - "shippingMethod": "STANDARD",
- "orderType": "ONE_TIME"
}{- "payer": "customer",
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "isFirstOrder": true,
- "customerId": "string",
- "subscriptionId": null,
- "protocolId": null,
- "paymentMethod": {
- "brand": "visa",
- "last4": "4242",
- "expMonth": 8,
- "expYear": 2030
}, - "discount": "string",
- "daysSupply": 30,
- "orderType": "SUBSCRIPTION",
- "shippingMethod": "STANDARD",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "lineItems": [
- {
- "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "productId": 77123129891,
- "name": "Elite EPA",
- "quantity": 2,
- "packDescription": "Whole Body Wellness",
- "price": "0.39",
- "discountedPrice": "string",
- "totalPrice": "29.70",
- "totalRetailPrice": "string",
- "totalPartnerPrice": "string",
- "totalCustomerPrice": "string",
- "external": false,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "templateId": "string"
}
], - "customer": {
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}
}, - "retailSubtotal": "60.00",
- "partnerSubtotal": "36.00",
- "customerSubtotal": "30.00",
- "totalDiscount": "27.00",
- "totalFees": "5.00",
- "totalShipping": "9.99",
- "totalTax": "0.00",
- "totalVolumeDiscount": "12.10",
- "customerTotal": "36.99",
- "partnerCommission": "5.00",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Retrieve a paginated list of orders, or search them.
limit values outside 1–100 are clamped, not rejected. With search,
the status and direction parameters are ignored, and matching is
fuzzy over customer name, order ID, customer source ID, and product
names; search is served from a secondary index that is populated
asynchronously (eventually consistent). Cursors from search and
non-search responses are not interchangeable. Filtering by status
returns results in index order rather than newest-first.
| limit | integer [ 1 .. 100 ] Default: 10 Maximum number of results per page |
| cursor | string Pagination cursor for retrieving next/previous page |
| direction | string Enum: "next" "previous" Direction for pagination (next or previous) |
| status | string Enum: "NEW" "PROCESSING" "DISPENSING" "FILLED" "FULFILLED" "AWAITING_SHIPMENT" "SHIPPED" "DELIVERED" "EXCEPTION" "CANCELLED" Filter orders by status. |
| search | string Example: search=John Doe Fuzzy search over customer name, order ID, customer source ID, and product names |
Array of objects (Order) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cursor | string or null (Cursor) Pagination cursor for retrieving next/previous page; null when there are no more pages | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "orders": [
- {
- "payer": "customer",
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "isFirstOrder": true,
- "customerId": "string",
- "subscriptionId": null,
- "protocolId": null,
- "paymentMethod": {
- "brand": "visa",
- "last4": "4242",
- "expMonth": 8,
- "expYear": 2030
}, - "discount": "string",
- "daysSupply": 30,
- "orderType": "SUBSCRIPTION",
- "shippingMethod": "STANDARD",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "lineItems": [
- {
- "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "productId": 77123129891,
- "name": "Elite EPA",
- "quantity": 2,
- "packDescription": "Whole Body Wellness",
- "price": "0.39",
- "discountedPrice": "string",
- "totalPrice": "29.70",
- "totalRetailPrice": "string",
- "totalPartnerPrice": "string",
- "totalCustomerPrice": "string",
- "external": false,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "templateId": "string"
}
], - "customer": {
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}
}, - "retailSubtotal": "60.00",
- "partnerSubtotal": "36.00",
- "customerSubtotal": "30.00",
- "totalDiscount": "27.00",
- "totalFees": "5.00",
- "totalShipping": "9.99",
- "totalTax": "0.00",
- "totalVolumeDiscount": "12.10",
- "customerTotal": "36.99",
- "partnerCommission": "5.00",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
], - "cursor": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Get detailed information for a specific order
| orderId required | string Example: 2vSGym0bH8qVEwCIGlyFoRgJq1A |
| payer | string or null Enum: "customer" "practice" Who was charged for this order, frozen when it was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| id | string Unique order identifier | ||||||||||||||||||||||||||||||||||||||||||||||
| status | string Enum: "NEW" "PROCESSING" "DISPENSING" "FILLED" "FULFILLED" "AWAITING_SHIPMENT" "SHIPPED" "DELIVERED" "EXCEPTION" "CANCELLED" Current status. | ||||||||||||||||||||||||||||||||||||||||||||||
| isFirstOrder | boolean Whether this is the linked customer's first order with your account. Computed at creation. False when the order is not linked, and false for a customer the order itself created or matched by email — only a customer referenced by | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId | string or null Internal id of the Customer record this order is linked to; null when the order could not be linked. | ||||||||||||||||||||||||||||||||||||||||||||||
| subscriptionId | string or null The subscription that generated this order; null for one-time and API orders. A customer with more than one subscription cannot be attributed from | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId | string or null The protocol this order was built from; null for API orders. It survives a subscription being replaced, so it is the stabler key for grouping a customer's recurring orders. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null The card that paid for this order, recorded once when the payment settled; reading the order never asks the payment provider. null when no card is recorded: orders paid before this field existed, payments that were not by card, orders with no customer payment (such as those created with | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| discount | string Deprecated Vestigial; always "0.00" | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply | integer Number of days the order supplies | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType | string Enum: "ONE_TIME" "SUBSCRIPTION" | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" | ||||||||||||||||||||||||||||||||||||||||||||||
object (FulfillmentInfo) Details about order fulfillment and shipping. All fields are nullable and populate as fulfillment progresses. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
Array of objects (LineItem) Products included in the order | |||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
object Embedded order customer snapshot (not the full Customer resource) | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| retailSubtotal | string Sum of retail (MSRP) line totals | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerSubtotal | string Wholesale subtotal the partner pays | ||||||||||||||||||||||||||||||||||||||||||||||
| customerSubtotal | string Customer subtotal | ||||||||||||||||||||||||||||||||||||||||||||||
| totalDiscount | string Order-level customer discount amount | ||||||||||||||||||||||||||||||||||||||||||||||
| totalFees | string Platform handling fees on external products. Under Juniper-managed billing these reduce commission and are not charged to the customer; under partner-managed billing they are part of the total. | ||||||||||||||||||||||||||||||||||||||||||||||
| totalShipping | string Shipping charged | ||||||||||||||||||||||||||||||||||||||||||||||
| totalTax | string Tax charged ("0.00" for API-created orders) | ||||||||||||||||||||||||||||||||||||||||||||||
| totalVolumeDiscount | string Volume discount applied automatically from your configured tiers | ||||||||||||||||||||||||||||||||||||||||||||||
| customerTotal | string Final total charged to the customer (frozen at creation) | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerCommission | string Frozen commission; may be negative | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
{- "payer": "customer",
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "isFirstOrder": true,
- "customerId": "string",
- "subscriptionId": null,
- "protocolId": null,
- "paymentMethod": {
- "brand": "visa",
- "last4": "4242",
- "expMonth": 8,
- "expYear": 2030
}, - "discount": "string",
- "daysSupply": 30,
- "orderType": "SUBSCRIPTION",
- "shippingMethod": "STANDARD",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "lineItems": [
- {
- "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "productId": 77123129891,
- "name": "Elite EPA",
- "quantity": 2,
- "packDescription": "Whole Body Wellness",
- "price": "0.39",
- "discountedPrice": "string",
- "totalPrice": "29.70",
- "totalRetailPrice": "string",
- "totalPartnerPrice": "string",
- "totalCustomerPrice": "string",
- "external": false,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "templateId": "string"
}
], - "customer": {
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}
}, - "retailSubtotal": "60.00",
- "partnerSubtotal": "36.00",
- "customerSubtotal": "30.00",
- "totalDiscount": "27.00",
- "totalFees": "5.00",
- "totalShipping": "9.99",
- "totalTax": "0.00",
- "totalVolumeDiscount": "12.10",
- "customerTotal": "36.99",
- "partnerCommission": "5.00",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}The documents you can show the order's customer, each with a signed link: the supplement facts panel, once the fill has rendered it, then every document frozen onto the order when it was created, in that order. The panel prints the customer's name and address.
Links are private, never cached, and expire (the panel's after 15 minutes, the others after one hour). Fetch a fresh list each time a customer opens a document; never store a link.
A new order has no panel until the fill renders it, so documents can
be empty. A document whose per-order copy failed is left out.
| orderId required | string Example: 2vSGym0bH8qVEwCIGlyFoRgJq1A |
| orderId required | string | ||||||||
required | Array of objects (OrderDocument) | ||||||||
Array
| |||||||||
{- "orderId": "string",
- "documents": [
- {
- "id": "string",
- "kind": "supplement_facts",
- "name": "Supplement facts panel",
- "url": "string"
}
]
}Cancel a partner-managed order that has not been dispensed. Cancelling
never refunds. Cancellation propagates to fulfillment (the fill order
and any shipping label are cancelled) and emits the order.cancelled
webhook event.
An order can be cancelled only when all of these hold; otherwise the
answer is 409 with code: ORDER_NOT_CANCELLABLE and the first
failing rule in details.reason:
PARTNER_MANAGED (the mode frozen on the order
at creation; a legacy order without one uses your account's current
mode), else juniper_managed: contact DailyStak support to cancel it;CANCELLED (already_cancelled);NEW or PROCESSING, else past_processing;labeled);invoiced);already_dispensed);shopify_paid) and has no DailyStak
payment (paid_through_dailystak). The externalPaymentReference you
give when confirming a renewal is your own reference, not a DailyStak
payment.| orderId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| orderId | string ID of the cancelled order |
| status | string Order status |
{- "orderId": "2vTYQiGzufR4cn1cloW97DtSOYF",
- "status": "CANCELLED"
}Replace the shipping address on an order that has not yet been filled
(NEW or PROCESSING status). Amounts do not change — the order
is not re-priced; only the delivery address is updated. The full order
is returned.
| orderId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
The new shipping address.
| line1 required | string |
| city required | string |
| state required | string Two-letter US state code |
| zipCode required | string^\d{5}$ |
| line2 | string or null |
| payer | string or null Enum: "customer" "practice" Who was charged for this order, frozen when it was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| id | string Unique order identifier | ||||||||||||||||||||||||||||||||||||||||||||||
| status | string Enum: "NEW" "PROCESSING" "DISPENSING" "FILLED" "FULFILLED" "AWAITING_SHIPMENT" "SHIPPED" "DELIVERED" "EXCEPTION" "CANCELLED" Current status. | ||||||||||||||||||||||||||||||||||||||||||||||
| isFirstOrder | boolean Whether this is the linked customer's first order with your account. Computed at creation. False when the order is not linked, and false for a customer the order itself created or matched by email — only a customer referenced by | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId | string or null Internal id of the Customer record this order is linked to; null when the order could not be linked. | ||||||||||||||||||||||||||||||||||||||||||||||
| subscriptionId | string or null The subscription that generated this order; null for one-time and API orders. A customer with more than one subscription cannot be attributed from | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId | string or null The protocol this order was built from; null for API orders. It survives a subscription being replaced, so it is the stabler key for grouping a customer's recurring orders. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null The card that paid for this order, recorded once when the payment settled; reading the order never asks the payment provider. null when no card is recorded: orders paid before this field existed, payments that were not by card, orders with no customer payment (such as those created with | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| discount | string Deprecated Vestigial; always "0.00" | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply | integer Number of days the order supplies | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType | string Enum: "ONE_TIME" "SUBSCRIPTION" | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" | ||||||||||||||||||||||||||||||||||||||||||||||
object (FulfillmentInfo) Details about order fulfillment and shipping. All fields are nullable and populate as fulfillment progresses. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
Array of objects (LineItem) Products included in the order | |||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
object Embedded order customer snapshot (not the full Customer resource) | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| retailSubtotal | string Sum of retail (MSRP) line totals | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerSubtotal | string Wholesale subtotal the partner pays | ||||||||||||||||||||||||||||||||||||||||||||||
| customerSubtotal | string Customer subtotal | ||||||||||||||||||||||||||||||||||||||||||||||
| totalDiscount | string Order-level customer discount amount | ||||||||||||||||||||||||||||||||||||||||||||||
| totalFees | string Platform handling fees on external products. Under Juniper-managed billing these reduce commission and are not charged to the customer; under partner-managed billing they are part of the total. | ||||||||||||||||||||||||||||||||||||||||||||||
| totalShipping | string Shipping charged | ||||||||||||||||||||||||||||||||||||||||||||||
| totalTax | string Tax charged ("0.00" for API-created orders) | ||||||||||||||||||||||||||||||||||||||||||||||
| totalVolumeDiscount | string Volume discount applied automatically from your configured tiers | ||||||||||||||||||||||||||||||||||||||||||||||
| customerTotal | string Final total charged to the customer (frozen at creation) | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerCommission | string Frozen commission; may be negative | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
{- "line1": "string",
- "line2": "string",
- "city": "string",
- "state": "string",
- "zipCode": "string"
}{- "payer": "customer",
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "isFirstOrder": true,
- "customerId": "string",
- "subscriptionId": null,
- "protocolId": null,
- "paymentMethod": {
- "brand": "visa",
- "last4": "4242",
- "expMonth": 8,
- "expYear": 2030
}, - "discount": "string",
- "daysSupply": 30,
- "orderType": "SUBSCRIPTION",
- "shippingMethod": "STANDARD",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "lineItems": [
- {
- "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "productId": 77123129891,
- "name": "Elite EPA",
- "quantity": 2,
- "packDescription": "Whole Body Wellness",
- "price": "0.39",
- "discountedPrice": "string",
- "totalPrice": "29.70",
- "totalRetailPrice": "string",
- "totalPartnerPrice": "string",
- "totalCustomerPrice": "string",
- "external": false,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "templateId": "string"
}
], - "customer": {
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}
}, - "retailSubtotal": "60.00",
- "partnerSubtotal": "36.00",
- "customerSubtotal": "30.00",
- "totalDiscount": "27.00",
- "totalFees": "5.00",
- "totalShipping": "9.99",
- "totalTax": "0.00",
- "totalVolumeDiscount": "12.10",
- "customerTotal": "36.99",
- "partnerCommission": "5.00",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}List every refund for the order (all statuses, including FAILED),
oldest first, as a JSON array. Unpaginated. An unknown order ID returns
an empty array, not 404.
| orderId required | string |
| refundId required | string Unique refund identifier | ||||||
| orderId required | string Order the refund belongs to | ||||||
| partnerId required | string Owning partner | ||||||
| status required | string Enum: "PENDING" "PROCESSING" "COMPLETED" "FAILED" Lifecycle status. Create responses always show | ||||||
| refundType required | string Enum: "FULL" "PARTIAL"
| ||||||
| amount required | integer Refund amount in cents (server-computed for full and line-item refunds) | ||||||
| currency required | string Currency the payment processor refunded in | ||||||
| paymentIntentId required | string The server-resolved payment that was refunded | ||||||
| reason required | string Partner-supplied reason (stored and echoed in webhooks/emails; not forwarded to the payment processor) | ||||||
| initiatedBy required | string OAuth client ID that created the refund | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| stripeRefundId | string or null Payment-processor refund identifier | ||||||
Array of objects or null (RefundLineItem) Itemized breakdown when the refund was created by line items | |||||||
Array
| |||||||
| errorMessage | string or null Failure reason; null unless the refund | ||||||
[- {
- "refundId": "2vT4kQpRefundKsuid00000000",
- "orderId": "string",
- "partnerId": "string",
- "status": "PENDING",
- "refundType": "FULL",
- "amount": 2500,
- "currency": "usd",
- "stripeRefundId": "re_3RqXYZabc123",
- "paymentIntentId": "string",
- "lineItems": [
- {
- "lineItemId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "quantity": 1,
- "amount": 1
}
], - "reason": "string",
- "initiatedBy": "string",
- "errorMessage": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
]Fetch a single refund. Poll this to observe the transition from
PROCESSING to COMPLETED (or FAILED with errorMessage). A refund
belonging to another partner, or looked up under the wrong order, is
indistinguishable from a missing one (404).
| orderId required | string |
| refundId required | string |
| refundId required | string Unique refund identifier | ||||||
| orderId required | string Order the refund belongs to | ||||||
| partnerId required | string Owning partner | ||||||
| status required | string Enum: "PENDING" "PROCESSING" "COMPLETED" "FAILED" Lifecycle status. Create responses always show | ||||||
| refundType required | string Enum: "FULL" "PARTIAL"
| ||||||
| amount required | integer Refund amount in cents (server-computed for full and line-item refunds) | ||||||
| currency required | string Currency the payment processor refunded in | ||||||
| paymentIntentId required | string The server-resolved payment that was refunded | ||||||
| reason required | string Partner-supplied reason (stored and echoed in webhooks/emails; not forwarded to the payment processor) | ||||||
| initiatedBy required | string OAuth client ID that created the refund | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| stripeRefundId | string or null Payment-processor refund identifier | ||||||
Array of objects or null (RefundLineItem) Itemized breakdown when the refund was created by line items | |||||||
Array
| |||||||
| errorMessage | string or null Failure reason; null unless the refund | ||||||
{- "refundId": "2vT4kQpRefundKsuid00000000",
- "orderId": "string",
- "partnerId": "string",
- "status": "PENDING",
- "refundType": "FULL",
- "amount": 2500,
- "currency": "usd",
- "stripeRefundId": "re_3RqXYZabc123",
- "paymentIntentId": "string",
- "lineItems": [
- {
- "lineItemId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "quantity": 1,
- "amount": 1
}
], - "reason": "string",
- "initiatedBy": "string",
- "errorMessage": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Create a customer record. sourceId and email are each unique per
partner — a duplicate returns 409. Email uniqueness is enforced
atomically; the sourceId check is eventually consistent, so use
your own sourceId as the idempotency key when retrying. Emits the
customer.created webhook event.
Set managedBy to create a customer looked after by a caregiver (for
example a child or a dependent adult). A managed customer may omit
email; notifications go to their caregiver instead.
| firstName required | string Customer's first name | ||||||||||
| lastName required | string Customer's last name | ||||||||||
| sourceId | string Unique customer identifier in your system | ||||||||||
string <email> Customer's email address. Required unless | |||||||||||
| phone | string Customer's phone number | ||||||||||
| managedBy | string Customer ID of the managing caregiver. The caregiver must belong to your partner and must not itself be managed — relationships are one level deep. | ||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
| birthYear | integer or null [ 1900 .. 2100 ] Customer's birth year (YYYY). Used for age-based checks. | ||||||||||
| discountOverride | integer or null [ 0 .. 100 ] Customer-specific discount override (0-100% off MSRP). Must not exceed partner discount. Null or omit to use partner's default discount. | ||||||||||
| id | string Unique customer identifier | ||||||||||
| partnerId | string Partner identifier | ||||||||||
| sourceId | string Unique customer identifier in the partner system | ||||||||||
| firstName | string Customer's first name | ||||||||||
| lastName | string Customer's last name | ||||||||||
| fullName | string Customer's full name (firstName + lastName) | ||||||||||
string or null <email> Customer's email address. Null only for managed customers, who are reached through their caregiver's address instead. | |||||||||||
| phone | string Customer's phone number | ||||||||||
| managedBy | string or null Customer ID of the managing caregiver, or null when this customer manages itself. | ||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
| discountOverride | integer or null [ 0 .. 100 ] Customer-specific discount override (0-100% off MSRP). Must not exceed partner discount. Null means use partner's default discount. | ||||||||||
| partnerConsent | boolean Whether the customer has consented to partner account management | ||||||||||
| payer | string Default: "customer" Enum: "customer" "practice" Who pays for this customer's orders. | ||||||||||
| createdAt | string <date-time> Customer creation timestamp | ||||||||||
| updatedAt | string <date-time> Customer last update timestamp | ||||||||||
{- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "birthYear": 1985,
- "discountOverride": 15
}{- "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "fullName": "John Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "discountOverride": 15,
- "partnerConsent": true,
- "payer": "customer",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Retrieve a paginated list of customers, look one up by exact
sourceId, or search them.
sourceId is an exact, index-backed lookup over non-deleted
customers: it returns zero or one customer, always with cursor null
and hasMore false. It cannot be combined with search.
Search is best-effort contains-matching over name, email, phone, source
ID, and customer ID (case-insensitive for name and email; exact-case
for the rest), bounded to a limited scan — treat it as a convenience,
not an exhaustive query. When search is provided, cursor pagination
is disabled (cursor is always null) and results are capped at
limit.
Pages can be shorter than limit while hasMore is still true — keep
paging until hasMore is false.
| limit | integer [ 1 .. 100 ] Default: 50 Page size, 1–100. Out-of-range values reset to the default. |
| cursor | string Opaque pagination cursor from a previous response |
| sourceId | string Example: sourceId=crm-88231 Exact, case-sensitive lookup of the non-deleted customer with this partner-supplied source ID. Returns zero or one customer; |
| search | string Example: search=john Contains-match search across first name, last name, email, phone, source ID, and customer ID. Disables cursor pagination. Cannot be combined with |
required | Array of objects (Customer) | ||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||
| hasMore required | boolean | ||||||||||||||||||||||||||||||||
| cursor | string or null (Cursor) Pagination cursor for retrieving next/previous page; null when there are no more pages | ||||||||||||||||||||||||||||||||
{- "customers": [
- {
- "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "fullName": "John Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "discountOverride": 15,
- "partnerConsent": true,
- "payer": "customer",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
], - "cursor": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "hasMore": true
}Retrieve detailed information for a specific customer
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| id | string Unique customer identifier | ||||||||||
| partnerId | string Partner identifier | ||||||||||
| sourceId | string Unique customer identifier in the partner system | ||||||||||
| firstName | string Customer's first name | ||||||||||
| lastName | string Customer's last name | ||||||||||
| fullName | string Customer's full name (firstName + lastName) | ||||||||||
string or null <email> Customer's email address. Null only for managed customers, who are reached through their caregiver's address instead. | |||||||||||
| phone | string Customer's phone number | ||||||||||
| managedBy | string or null Customer ID of the managing caregiver, or null when this customer manages itself. | ||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
| discountOverride | integer or null [ 0 .. 100 ] Customer-specific discount override (0-100% off MSRP). Must not exceed partner discount. Null means use partner's default discount. | ||||||||||
| partnerConsent | boolean Whether the customer has consented to partner account management | ||||||||||
| payer | string Default: "customer" Enum: "customer" "practice" Who pays for this customer's orders. | ||||||||||
| createdAt | string <date-time> Customer creation timestamp | ||||||||||
| updatedAt | string <date-time> Customer last update timestamp | ||||||||||
{- "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "fullName": "John Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "discountOverride": 15,
- "partnerConsent": true,
- "payer": "customer",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Update a customer with merge semantics: omitted or null fields are left
unchanged, except that an explicit null clears discountOverride,
birthYear and managedBy. Addresses are replaced wholesale when
supplied and cannot be cleared. partnerConsent must be a boolean when
supplied — never null.
Assigning managedBy puts the customer under a caregiver; clearing it
promotes them back to top-level, which requires them to have an email
address of their own. Either way the relationship stays one level deep:
the caregiver must not itself be managed, and a customer who manages
others cannot be given a caregiver. Violations return 400.
Updating the billing address also syncs it to the customer's payment
profile. Emits the customer.updated webhook event.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| partnerConsent | boolean Whether the customer consents to partner account management. Must be a boolean when supplied — never null. | ||||||||||
| firstName | string Customer's first name | ||||||||||
| lastName | string Customer's last name | ||||||||||
string <email> Customer's email address | |||||||||||
| phone | string Customer's phone number | ||||||||||
| managedBy | string or null Customer ID of the managing caregiver. The caregiver must belong to your partner and must not itself be managed, and the customer being updated must not manage anyone — relationships are one level deep. Set to null to promote the customer to top-level, which requires them to have (or be given, in the same request) an email address. | ||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
| birthYear | integer or null [ 1900 .. 2100 ] Customer's birth year (YYYY). Used for age-based checks. Set to null to clear. | ||||||||||
| discountOverride | integer or null [ 0 .. 100 ] Customer-specific discount override (0-100% off MSRP). Must not exceed partner discount. Set to null to clear override and use partner's default discount. | ||||||||||
| id | string Unique customer identifier | ||||||||||
| partnerId | string Partner identifier | ||||||||||
| sourceId | string Unique customer identifier in the partner system | ||||||||||
| firstName | string Customer's first name | ||||||||||
| lastName | string Customer's last name | ||||||||||
| fullName | string Customer's full name (firstName + lastName) | ||||||||||
string or null <email> Customer's email address. Null only for managed customers, who are reached through their caregiver's address instead. | |||||||||||
| phone | string Customer's phone number | ||||||||||
| managedBy | string or null Customer ID of the managing caregiver, or null when this customer manages itself. | ||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
| discountOverride | integer or null [ 0 .. 100 ] Customer-specific discount override (0-100% off MSRP). Must not exceed partner discount. Null means use partner's default discount. | ||||||||||
| partnerConsent | boolean Whether the customer has consented to partner account management | ||||||||||
| payer | string Default: "customer" Enum: "customer" "practice" Who pays for this customer's orders. | ||||||||||
| createdAt | string <date-time> Customer creation timestamp | ||||||||||
| updatedAt | string <date-time> Customer last update timestamp | ||||||||||
{- "partnerConsent": true,
- "firstName": "John",
- "lastName": "Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "birthYear": 1985,
- "discountOverride": 15
}{- "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "fullName": "John Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "discountOverride": 15,
- "partnerConsent": true,
- "payer": "customer",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Soft-delete a customer, with a cascade: their active, paused, pending,
and awaiting-confirmation subscriptions are cancelled, and their
not-yet-processed orders are cancelled (each cancelled order emits an
order.cancelled webhook event; the subscription cancellations emit no
events). Orders already in fulfillment are unaffected. The customer's
sourceId and email become reusable. Emits customer.deleted.
A caregiver who still manages other customers cannot be deleted: the
request returns 409 and nothing is cancelled. Delete or reassign the
dependents (GET /v2/customers/{customerId}/managed) first.
Deleting an already-deleted customer returns 404.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
{- "message": "One or more requested products were not found",
- "code": "PRODUCTS_NOT_FOUND",
- "details": {
- "missingProductIds": [
- 1001
]
}
}The customer with every list an account page renders — protocols, subscriptions and recent orders — in one read.
A composite of GET /v2/customers/{customerId},
GET /v2/protocols?customerId=, GET /v2/subscriptions?customerId=
and GET /v2/orders. Each entry is the same object those endpoints
return, so one model per resource serves both.
protocols and subscriptions are complete and unpaged — every
status, newest first. recentOrders holds at most the 20 most recent
orders, newest first; a customer with more sets
recentOrdersTruncated to true, and the rest are read from
GET /v2/orders. Protocol line items carry a null image here.
An unknown, deleted or other partner's customer is 404, exactly as
GET /v2/customers/{customerId} answers them.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
required | object (Customer) Customer information | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (Protocol) Every protocol for this customer, any status, newest first | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (Subscription) Every subscription for this customer, any status, newest first (each with | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (Order) The customer's 20 most recent orders, newest first | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| recentOrdersTruncated required | boolean True when the customer has more orders than | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "customer": {
- "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "fullName": "John Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "discountOverride": 15,
- "partnerConsent": true,
- "payer": "customer",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}, - "protocols": [
- {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
], - "subscriptions": [
- {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}
], - "recentOrders": [
- {
- "payer": "customer",
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "isFirstOrder": true,
- "customerId": "string",
- "subscriptionId": null,
- "protocolId": null,
- "paymentMethod": {
- "brand": "visa",
- "last4": "4242",
- "expMonth": 8,
- "expYear": 2030
}, - "discount": "string",
- "daysSupply": 30,
- "orderType": "SUBSCRIPTION",
- "shippingMethod": "STANDARD",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "lineItems": [
- {
- "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "productId": 77123129891,
- "name": "Elite EPA",
- "quantity": 2,
- "packDescription": "Whole Body Wellness",
- "price": "0.39",
- "discountedPrice": "string",
- "totalPrice": "29.70",
- "totalRetailPrice": "string",
- "totalPartnerPrice": "string",
- "totalCustomerPrice": "string",
- "external": false,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "templateId": "string"
}
], - "customer": {
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "address": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}
}, - "retailSubtotal": "60.00",
- "partnerSubtotal": "36.00",
- "customerSubtotal": "30.00",
- "totalDiscount": "27.00",
- "totalFees": "5.00",
- "totalShipping": "9.99",
- "totalTax": "0.00",
- "totalVolumeDiscount": "12.10",
- "customerTotal": "36.99",
- "partnerCommission": "5.00",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
], - "recentOrdersTruncated": true
}List the customers this customer looks after as their caregiver.
Caregiver relationships are one level deep, so the result is returned
whole rather than paged: cursor is always null and hasMore always
false. Deleted dependents are omitted, and a customer who manages
nobody returns an empty list.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
required | Array of objects (Customer) | ||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||
| hasMore required | boolean | ||||||||||||||||||||||||||||||||
| cursor | string or null (Cursor) Pagination cursor for retrieving next/previous page; null when there are no more pages | ||||||||||||||||||||||||||||||||
{- "customers": [
- {
- "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "sourceId": "1234567890",
- "firstName": "John",
- "lastName": "Doe",
- "fullName": "John Doe",
- "email": "john.doe@example.com",
- "phone": "1234567890",
- "managedBy": "2vTgd30JFgnwLrgb383PcV9Rken",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "discountOverride": 15,
- "partnerConsent": true,
- "payer": "customer",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
], - "cursor": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "hasMore": true
}Create a Stripe Billing Portal session for customer self-service.
The portal allows customers to:
If the customer doesn't have a payment profile yet, one is created automatically on first use.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| returnUrl required | string <uri> URL to redirect the customer after they exit the billing portal |
| url | string <uri> Stripe Billing Portal URL. Redirect the customer to this URL to manage their payment methods. |
{
}{
}Start collecting a replacement card on your own page. Returns a Stripe
SetupIntent clientSecret: mount a Payment Element in setup mode with
it and confirm with stripe.confirmSetup(...). The card it collects is
saved to the customer's payment profile; call
POST /v2/customers/{customerId}/payment-method with the returned
setupIntentId afterwards to make it the renewal default. The
SetupIntent is created for the card payment method type; which
wallets the Payment Element offers in setup mode follows Stripe's
behaviour for the card type.
If the customer doesn't have a payment profile yet, one is created automatically on first use.
Available to partner-hosted (Elements) checkout partners only — gated
exactly like uiMode: elements sessions. No request body.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| clientSecret required | string Stripe SetupIntent client secret for the Payment Element (setup mode) |
| setupIntentId required | string Stripe SetupIntent id |
| stripePublishableKey required | string Publishable key for the Stripe account and mode this SetupIntent belongs to. Load Stripe.js with it rather than keeping your own copy, which would go stale at a test-to-live switch. Never null on a 200 — the same gate that 503s an unconfigured environment runs first. |
{- "clientSecret": "seti_1Pxyz_secret_abc123",
- "setupIntentId": "seti_1Pxyz",
- "stripePublishableKey": "pk_live_51Abc"
}The card subscription renewals charge, so your account page can show it
before the customer replaces it (POST .../setup-intent, then
POST .../payment-method). Returns brand, last four and expiry only —
never the payment method id.
paymentMethod is null when the customer has no payment profile or
no default card. Not gated like the Elements routes: reading the card
on file needs no Payment Element.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
required | object or null The card subscription renewals charge, or null when the customer has no payment profile or no default card | ||||||||
| |||||||||
{- "paymentMethod": {
- "brand": "visa",
- "last4": "4242",
- "expMonth": 8,
- "expYear": 2030
}
}Promote the card collected by a confirmed SetupIntent (from
POST /v2/customers/{customerId}/setup-intent) to the customer's
default payment method, so subscription renewals charge it. The
previous card stays saved on the customer; only the default changes.
Call this only after Stripe has returned to your return_url with
redirect_status=succeeded. The SetupIntent must have succeeded and
the payment method it collected must already belong to this customer.
Gated exactly like the setup-intent route.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| setupIntentId required | string non-empty A confirmed SetupIntent (from |
| paymentMethodId required | string Stripe PaymentMethod id |
| isDefault required | boolean The payment method is now the customer's default for renewals (always true) |
{- "setupIntentId": "seti_1Pxyz"
}{- "paymentMethodId": "pm_1Pxyz",
- "isDefault": true
}Tells DailyStak that this customer signed in to your own app, so the
sign-in appears on the customer's activity timeline beside sign-ins to
a DailyStak-hosted store. No request body: DailyStak writes the entry
itself and timestamps it when it records it, normally within seconds
of your call, so you send no timestamp. 202 once the entry is
queued. A customer that is not yours is a 404.
| customerId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
{ }List the partner's subscriptions (cursor-paginated), or search them.
Without search, results come back in creation order with an opaque
cursor for the next page. With search, results are ranked fuzzy
matches over customer name, subscription ID, protocol ID, and status —
search is served from a secondary index that is populated
asynchronously, so a just-created subscription may briefly not be
searchable. Cursors from search and non-search responses are not
interchangeable.
List items always have protocol: null; the linked protocol is
embedded only on the single-subscription GET.
| limit | integer [ 1 .. 100 ] Default: 50 Page size, 1–100. Out-of-range values reset to the default. |
| cursor | string Opaque pagination cursor from a previous response |
| search | string Fuzzy search over customer name, subscription ID, protocol ID, and status. Cannot be combined with |
| customerId | string Example: customerId=2vTgd30JFgnwLrgb383PcV9Rken Exact match on the customer id, served from the primary store; cannot be combined with |
required | Array of objects (Subscription) Page of subscriptions (each with | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cursor required | string or null Opaque cursor for the next page; null when exhausted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hasMore required | boolean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "subscriptions": [
- {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}
], - "cursor": "string",
- "hasMore": true
}Create a subscription for an existing protocol and customer. Available to partner-managed-billing partners only — with Juniper-managed billing, subscriptions are created automatically by the hosted checkout flow after payment is captured (400 otherwise).
A subscription is a billing schedule that references a Protocol: the
protocol owns the regimen (products and customer), the subscription
owns the cadence (daysSupply — renewal orders are generated every
daysSupply days) and delivery configuration. The customer must have a
shipping address and must be the protocol's customer. One active
subscription per protocol (409 otherwise). The subscription is created
ACTIVE immediately.
generateInitialOrder requires the subscription to be due: it runs
the same generation path as generate-order, which only fires when
nextBillingDate is now or earlier. With the default nextBillingDate
(one cycle in the future) the order generation fails with 400 — and the
subscription has already been created. To create a subscription and its
first order in one call, set nextBillingDate explicitly to now or
earlier; generation then advances it by one cycle.
No payment is collected by this API — you bill your customer yourself.
Emits the subscription.created webhook event.
| protocolId required | string Protocol that defines the products and pricing |
| customerId required | string Must be the protocol's customer, with a shipping address on file |
| startDate | string <date-time> Defaults to now |
| nextBillingDate | string <date-time> First renewal instant. Defaults to |
| endDate | string or null <date-time> |
| daysSupply | integer [ 1 .. 365 ] Default: 30 Renewal cadence in days (any value 1–365) |
| shippingMethod | string Default: "STANDARD" Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" |
| notes | string or null |
| generateInitialOrder | boolean Default: false Generate the first order in this call. Requires |
required | object (Subscription) A billing schedule that references a Protocol. The protocol owns the
regimen (products and customer); the subscription owns the renewal
cadence and delivery configuration. Renewal orders are generated every
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| orderId required | string or null The initial order's ID when | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "protocolId": "string",
- "customerId": "string",
- "startDate": "2019-08-24T14:15:22Z",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "notes": "string",
- "generateInitialOrder": false
}{- "subscription": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}, - "orderId": "string"
}Fetch one subscription with its linked protocol embedded under
protocol (this is the only response that populates it), and the
address its renewals ship to under shippingAddress.
Since 2026-10-21 a subscription keeps the address it was bought with. Updating a customer's address does not move their existing subscriptions; update each subscription's shippingAddress.
| subscriptionId required | string |
| id required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerId required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId required | string Linked protocol that defines the regimen and customer | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId required | string Customer this subscription bills; always the linked protocol's customer | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply required | integer [ 1 .. 365 ] Renewal cadence in days, and the days of product each renewal order supplies. Set at creation; change it with | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method applied to renewal orders | ||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "PENDING" "ACTIVE" "PAST_DUE" "PAUSED" "CANCELLED" "FAILED" "AWAITING_PARTNER" Lifecycle status. | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| totalOrdersGenerated required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ScheduleChange) Audit trail of schedule changes | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| isActive required | boolean
| ||||||||||||||||||||||||||||||||||||||||||||||
| isDueForBilling required | boolean Active and | ||||||||||||||||||||||||||||||||||||||||||||||
| paymentFailureCount required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingAddressSource required | string Enum: "subscription" "customer"
| ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object (EmbeddedProtocol) Protocol summary embedded in the single-subscription GET response | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| nextBillingDate | string or null <date-time> Next renewal instant; renewals fire on the scheduler tick at or after this time | ||||||||||||||||||||||||||||||||||||||||||||||
| startDate | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| endDate | string or null <date-time> From this instant on (inclusive) no renewal is charged or flagged: when its next renewal comes due, the subscription is cancelled ( | ||||||||||||||||||||||||||||||||||||||||||||||
| lastOrderId | string or null Most recently generated order | ||||||||||||||||||||||||||||||||||||||||||||||
| notes | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| lastPaymentError | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedReason | string or null Enum: "customer_not_found" "no_provider_customer" "provider_customer_missing" "no_payment_method" "no_shipping_address" "zero_total" "protocol_not_found" null Set when a due platform-billed renewal can't be charged until its cause is fixed; null otherwise. Cleared when a renewal is charged, or the subscription is paused or cancelled. See | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedAt | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedMessage | string or null Human-readable explanation of | ||||||||||||||||||||||||||||||||||||||||||||||
| cadenceTermsAcceptedAt | string or null <date-time> When you asserted, via | ||||||||||||||||||||||||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who is charged on each renewal, frozen when the subscription was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null What the next renewal is expected to cost. A stored snapshot, not a live quote — see the "Renewal amount" section of the Subscriptions guide, and use | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object or null Where this subscription's renewals ship: its own address when it has one, else the customer's. Null when neither has one. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "shippingAddressSource": "subscription"
}Update a subscription's daysSupply, shippingMethod, endDate,
notes and/or shippingAddress. These are the only five fields
this endpoint applies — the regimen and its pricing are managed through the
protocol and amendment flow. A request that supplies none of them
returns 400.
Changing daysSupply requires termsAcceptedAt on a subscription
DailyStak bills: a new cadence means a new recurring amount, so you
assert the customer accepted both. Price it first with
GET /v2/subscriptions/{subscriptionId}/renewal-estimate so they can
see the amount. Without the assertion the call returns 400 with
TERMS_ACCEPTANCE_REQUIRED and nothing changes. Partner-managed
billing needs no assertion (you charge your own customer).
termsAcceptedAt must carry a UTC offset, be at most 5 minutes in the
future and at most 24 hours old; otherwise the call returns 400 with
TERMS_ACCEPTED_AT_TIMEZONE_REQUIRED, TERMS_ACCEPTED_AT_IN_FUTURE or
TERMS_ACCEPTED_AT_TOO_OLD and nothing changes.
The paid cycle is never shortened. nextBillingDate is untouched:
30 → 90 on day 10 still charges on day 30, for a 90-day supply, and
every 90 days after that. Nothing is prorated. nextRenewalEstimate is
re-priced for the new cadence, and the customer is sent a notice (gated
by the partner's subscription-renewal email toggle).
shippingMethod buys that class for the NEXT renewal only. Renewals
ship STANDARD; EXPEDITED or OVERNIGHT is charged at the
partner's configured price for it, and the subscription returns to
STANDARD once that renewal's order is generated. STANDARD gives
the upgrade back. A class with no active shipping method is refused.
No field can be cleared back to null (null means "not provided"). Last write wins; there is no status guard or optimistic locking.
| subscriptionId required | string |
| endDate | string <date-time> New end date (cannot be cleared back to null) | ||||||||||
| notes | string New notes (cannot be cleared back to null) | ||||||||||
object or null Where this subscription's renewals ship from now on. Changes this subscription only; the customer's address and other subscriptions are untouched. Cannot be cleared back to null. Refused with 409 | |||||||||||
| |||||||||||
| daysSupply | integer [ 1 .. 365 ] New renewal cadence, in days. Requires | ||||||||||
| shippingMethod | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" How the NEXT renewal ships. Needs no acceptance. Renewals ship STANDARD; EXPEDITED or OVERNIGHT buys that class for one renewal at the partner's configured price, then reverts to STANDARD. | ||||||||||
| termsAcceptedAt | string <date-time> When the customer accepted the new cadence AND the new recurring amount (priced via | ||||||||||
| id required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerId required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId required | string Linked protocol that defines the regimen and customer | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId required | string Customer this subscription bills; always the linked protocol's customer | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply required | integer [ 1 .. 365 ] Renewal cadence in days, and the days of product each renewal order supplies. Set at creation; change it with | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method applied to renewal orders | ||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "PENDING" "ACTIVE" "PAST_DUE" "PAUSED" "CANCELLED" "FAILED" "AWAITING_PARTNER" Lifecycle status. | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| totalOrdersGenerated required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ScheduleChange) Audit trail of schedule changes | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| isActive required | boolean
| ||||||||||||||||||||||||||||||||||||||||||||||
| isDueForBilling required | boolean Active and | ||||||||||||||||||||||||||||||||||||||||||||||
| paymentFailureCount required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object (EmbeddedProtocol) Protocol summary embedded in the single-subscription GET response | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| nextBillingDate | string or null <date-time> Next renewal instant; renewals fire on the scheduler tick at or after this time | ||||||||||||||||||||||||||||||||||||||||||||||
| startDate | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| endDate | string or null <date-time> From this instant on (inclusive) no renewal is charged or flagged: when its next renewal comes due, the subscription is cancelled ( | ||||||||||||||||||||||||||||||||||||||||||||||
| lastOrderId | string or null Most recently generated order | ||||||||||||||||||||||||||||||||||||||||||||||
| notes | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| lastPaymentError | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedReason | string or null Enum: "customer_not_found" "no_provider_customer" "provider_customer_missing" "no_payment_method" "no_shipping_address" "zero_total" "protocol_not_found" null Set when a due platform-billed renewal can't be charged until its cause is fixed; null otherwise. Cleared when a renewal is charged, or the subscription is paused or cancelled. See | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedAt | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedMessage | string or null Human-readable explanation of | ||||||||||||||||||||||||||||||||||||||||||||||
| cadenceTermsAcceptedAt | string or null <date-time> When you asserted, via | ||||||||||||||||||||||||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who is charged on each renewal, frozen when the subscription was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null What the next renewal is expected to cost. A stored snapshot, not a live quote — see the "Renewal amount" section of the Subscriptions guide, and use | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "endDate": "2019-08-24T14:15:22Z",
- "notes": "string",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "daysSupply": 90,
- "shippingMethod": "STANDARD",
- "termsAcceptedAt": "2026-07-25T18:05:44Z"
}{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}Price what a renewal of this subscription would cost at a given
cadence, live. Show this to the customer before asking them to
accept a cadence change, then send their acceptance as
termsAcceptedAt on PUT /v2/subscriptions/{subscriptionId}.
Same shape as the subscription's nextRenewalEstimate, always
basis: "quote", and priced through the same code path that charges
the renewal — no promotion, the renewal shipping threshold ($9.99
under $50 of product, free at $50 or more, or a bought upgrade's own
price), tax at the customer's saved address. Omit daysSupply to price the current cadence.
This is the only subscription read that prices live (every other read
serves the stored snapshot), so call it when the customer is choosing,
not on every render. It is an estimate, not a held quote: catalogue
prices and tax rates move, so render it with its quotedAt.
If the amount cannot be priced in full — the tax provider is unreachable, the regimen cannot be priced — this returns 503 rather than an amount missing its tax. A partial figure is one the customer would accept and then be over-charged against.
| subscriptionId required | string |
| daysSupply | integer [ 1 .. 365 ] Example: daysSupply=90 Cadence to price, in days. Omitted means the subscription's current |
| subtotal required | string Product total after partner, customer and volume discounts |
| shipping required | string Shipping this renewal pays: "9.99" under $50 of product, "0.00" at $50 or more, or a bought upgrade's own price. Included in |
| tax required | string Tax at the saved shipping address; "0.00" when no address is on file |
| total required | string subtotal + shipping + tax |
| currency required | string |
| daysSupply required | integer The cadence this estimate was priced for. Compare it with the subscription's |
| quotedAt required | string <date-time> When this estimate was produced |
| basis required | string Enum: "quote" "last_charge"
|
{- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}Pause an active subscription. nextBillingDate is not changed —
the renewal scheduler simply skips paused subscriptions. If the billing
date passes while paused, the subscription becomes due immediately on
resume; reschedule first if that is not what you want.
| subscriptionId required | string |
| id required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerId required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId required | string Linked protocol that defines the regimen and customer | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId required | string Customer this subscription bills; always the linked protocol's customer | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply required | integer [ 1 .. 365 ] Renewal cadence in days, and the days of product each renewal order supplies. Set at creation; change it with | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method applied to renewal orders | ||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "PENDING" "ACTIVE" "PAST_DUE" "PAUSED" "CANCELLED" "FAILED" "AWAITING_PARTNER" Lifecycle status. | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| totalOrdersGenerated required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ScheduleChange) Audit trail of schedule changes | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| isActive required | boolean
| ||||||||||||||||||||||||||||||||||||||||||||||
| isDueForBilling required | boolean Active and | ||||||||||||||||||||||||||||||||||||||||||||||
| paymentFailureCount required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object (EmbeddedProtocol) Protocol summary embedded in the single-subscription GET response | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| nextBillingDate | string or null <date-time> Next renewal instant; renewals fire on the scheduler tick at or after this time | ||||||||||||||||||||||||||||||||||||||||||||||
| startDate | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| endDate | string or null <date-time> From this instant on (inclusive) no renewal is charged or flagged: when its next renewal comes due, the subscription is cancelled ( | ||||||||||||||||||||||||||||||||||||||||||||||
| lastOrderId | string or null Most recently generated order | ||||||||||||||||||||||||||||||||||||||||||||||
| notes | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| lastPaymentError | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedReason | string or null Enum: "customer_not_found" "no_provider_customer" "provider_customer_missing" "no_payment_method" "no_shipping_address" "zero_total" "protocol_not_found" null Set when a due platform-billed renewal can't be charged until its cause is fixed; null otherwise. Cleared when a renewal is charged, or the subscription is paused or cancelled. See | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedAt | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedMessage | string or null Human-readable explanation of | ||||||||||||||||||||||||||||||||||||||||||||||
| cadenceTermsAcceptedAt | string or null <date-time> When you asserted, via | ||||||||||||||||||||||||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who is charged on each renewal, frozen when the subscription was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null What the next renewal is expected to cost. A stored snapshot, not a live quote — see the "Renewal amount" section of the Subscriptions guide, and use | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}Resume a paused subscription. If nextBillingDate passed while
paused, the subscription is due immediately and bills on the next
scheduler run.
| subscriptionId required | string |
| id required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerId required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId required | string Linked protocol that defines the regimen and customer | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId required | string Customer this subscription bills; always the linked protocol's customer | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply required | integer [ 1 .. 365 ] Renewal cadence in days, and the days of product each renewal order supplies. Set at creation; change it with | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method applied to renewal orders | ||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "PENDING" "ACTIVE" "PAST_DUE" "PAUSED" "CANCELLED" "FAILED" "AWAITING_PARTNER" Lifecycle status. | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| totalOrdersGenerated required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ScheduleChange) Audit trail of schedule changes | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| isActive required | boolean
| ||||||||||||||||||||||||||||||||||||||||||||||
| isDueForBilling required | boolean Active and | ||||||||||||||||||||||||||||||||||||||||||||||
| paymentFailureCount required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object (EmbeddedProtocol) Protocol summary embedded in the single-subscription GET response | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| nextBillingDate | string or null <date-time> Next renewal instant; renewals fire on the scheduler tick at or after this time | ||||||||||||||||||||||||||||||||||||||||||||||
| startDate | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| endDate | string or null <date-time> From this instant on (inclusive) no renewal is charged or flagged: when its next renewal comes due, the subscription is cancelled ( | ||||||||||||||||||||||||||||||||||||||||||||||
| lastOrderId | string or null Most recently generated order | ||||||||||||||||||||||||||||||||||||||||||||||
| notes | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| lastPaymentError | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedReason | string or null Enum: "customer_not_found" "no_provider_customer" "provider_customer_missing" "no_payment_method" "no_shipping_address" "zero_total" "protocol_not_found" null Set when a due platform-billed renewal can't be charged until its cause is fixed; null otherwise. Cleared when a renewal is charged, or the subscription is paused or cancelled. See | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedAt | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedMessage | string or null Human-readable explanation of | ||||||||||||||||||||||||||||||||||||||||||||||
| cadenceTermsAcceptedAt | string or null <date-time> When you asserted, via | ||||||||||||||||||||||||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who is charged on each renewal, frozen when the subscription was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null What the next renewal is expected to cost. A stored snapshot, not a live quote — see the "Renewal amount" section of the Subscriptions guide, and use | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}Cancel a subscription from any state. Cancellation is terminal — there is no un-cancel; create a new subscription to restart. Orders already generated are unaffected.
Idempotent: cancelling an already-cancelled subscription returns 200 with the current record.
| subscriptionId required | string |
| id required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerId required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId required | string Linked protocol that defines the regimen and customer | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId required | string Customer this subscription bills; always the linked protocol's customer | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply required | integer [ 1 .. 365 ] Renewal cadence in days, and the days of product each renewal order supplies. Set at creation; change it with | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method applied to renewal orders | ||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "PENDING" "ACTIVE" "PAST_DUE" "PAUSED" "CANCELLED" "FAILED" "AWAITING_PARTNER" Lifecycle status. | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| totalOrdersGenerated required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ScheduleChange) Audit trail of schedule changes | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| isActive required | boolean
| ||||||||||||||||||||||||||||||||||||||||||||||
| isDueForBilling required | boolean Active and | ||||||||||||||||||||||||||||||||||||||||||||||
| paymentFailureCount required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object (EmbeddedProtocol) Protocol summary embedded in the single-subscription GET response | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| nextBillingDate | string or null <date-time> Next renewal instant; renewals fire on the scheduler tick at or after this time | ||||||||||||||||||||||||||||||||||||||||||||||
| startDate | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| endDate | string or null <date-time> From this instant on (inclusive) no renewal is charged or flagged: when its next renewal comes due, the subscription is cancelled ( | ||||||||||||||||||||||||||||||||||||||||||||||
| lastOrderId | string or null Most recently generated order | ||||||||||||||||||||||||||||||||||||||||||||||
| notes | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| lastPaymentError | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedReason | string or null Enum: "customer_not_found" "no_provider_customer" "provider_customer_missing" "no_payment_method" "no_shipping_address" "zero_total" "protocol_not_found" null Set when a due platform-billed renewal can't be charged until its cause is fixed; null otherwise. Cleared when a renewal is charged, or the subscription is paused or cancelled. See | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedAt | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedMessage | string or null Human-readable explanation of | ||||||||||||||||||||||||||||||||||||||||||||||
| cadenceTermsAcceptedAt | string or null <date-time> When you asserted, via | ||||||||||||||||||||||||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who is charged on each renewal, frozen when the subscription was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null What the next renewal is expected to cost. A stored snapshot, not a live quote — see the "Renewal amount" section of the Subscriptions guide, and use | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}Confirm a renewal for a subscription in AWAITING_PARTNER state, using
the one-time confirmation token delivered by the
subscription.renewal_pending webhook event. The token expires 48
hours after issuance; three consecutive expired confirmations move the
subscription to FAILED.
On success the renewal order is generated (no payment is collected —
you have already charged your customer), the subscription returns to
ACTIVE, and nextBillingDate advances by one cycle. Emits the
subscription.order_created webhook event.
Confirmation is idempotent per token, but a retry may return either
200 (idempotent: true) or 201 with the same orderId — treat
orderId as the deduplication key, not the status code.
| subscriptionId required | string |
| confirmationToken required | string One-time token from the |
| externalPaymentReference | string Your payment reference for the renewal charge; stored on the subscription and threaded onto the generated order |
| orderId required | string The generated (or previously generated, on replay) order — fetch it via the Orders API |
| message required | string |
| idempotent required | boolean True when this response replayed an earlier confirmation instead of generating a new order |
| orderId required | string The generated (or previously generated, on replay) order — fetch it via the Orders API |
| message required | string |
| idempotent required | boolean True when this response replayed an earlier confirmation instead of generating a new order |
{- "confirmationToken": "string",
- "externalPaymentReference": "string"
}{- "orderId": "string",
- "message": "string",
- "idempotent": true
}Manually generate the next renewal order. The subscription must be
active and due (nextBillingDate now or earlier — use reschedule
to pull the date forward first). Line items and pricing come from the
linked protocol (due scheduled changes are applied first); delivery
settings come from the subscription. nextBillingDate advances by one
cycle.
Partner-managed subscriptions only. No payment is collected — this is an order-generation mechanism for partner-managed billing, not a charge mechanism. A subscription DailyStak bills returns 409 and nothing changes; its renewal orders are created when the renewal payment succeeds.
The response carries the new orderId, not the full order — fetch it
via the Orders API. Not idempotent: each successful call generates an
order and advances the schedule.
| subscriptionId required | string |
| orderId required | string The generated (or previously generated, on replay) order — fetch it via the Orders API |
| message required | string |
| idempotent required | boolean True when this response replayed an earlier confirmation instead of generating a new order |
{- "orderId": "string",
- "message": "string",
- "idempotent": true
}Skip the next delivery/billing cycle of an active subscription:
nextBillingDate advances by one cycle (daysSupply days) without
generating an order. Call repeatedly to skip multiple cycles. The skip
is recorded in scheduleHistory with your optional reason.
| subscriptionId required | string |
Send {} when no reason is given.
| reason | string Recorded in the schedule-history entry |
| id required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerId required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId required | string Linked protocol that defines the regimen and customer | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId required | string Customer this subscription bills; always the linked protocol's customer | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply required | integer [ 1 .. 365 ] Renewal cadence in days, and the days of product each renewal order supplies. Set at creation; change it with | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method applied to renewal orders | ||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "PENDING" "ACTIVE" "PAST_DUE" "PAUSED" "CANCELLED" "FAILED" "AWAITING_PARTNER" Lifecycle status. | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| totalOrdersGenerated required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ScheduleChange) Audit trail of schedule changes | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| isActive required | boolean
| ||||||||||||||||||||||||||||||||||||||||||||||
| isDueForBilling required | boolean Active and | ||||||||||||||||||||||||||||||||||||||||||||||
| paymentFailureCount required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object (EmbeddedProtocol) Protocol summary embedded in the single-subscription GET response | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| nextBillingDate | string or null <date-time> Next renewal instant; renewals fire on the scheduler tick at or after this time | ||||||||||||||||||||||||||||||||||||||||||||||
| startDate | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| endDate | string or null <date-time> From this instant on (inclusive) no renewal is charged or flagged: when its next renewal comes due, the subscription is cancelled ( | ||||||||||||||||||||||||||||||||||||||||||||||
| lastOrderId | string or null Most recently generated order | ||||||||||||||||||||||||||||||||||||||||||||||
| notes | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| lastPaymentError | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedReason | string or null Enum: "customer_not_found" "no_provider_customer" "provider_customer_missing" "no_payment_method" "no_shipping_address" "zero_total" "protocol_not_found" null Set when a due platform-billed renewal can't be charged until its cause is fixed; null otherwise. Cleared when a renewal is charged, or the subscription is paused or cancelled. See | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedAt | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedMessage | string or null Human-readable explanation of | ||||||||||||||||||||||||||||||||||||||||||||||
| cadenceTermsAcceptedAt | string or null <date-time> When you asserted, via | ||||||||||||||||||||||||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who is charged on each renewal, frozen when the subscription was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null What the next renewal is expected to cost. A stored snapshot, not a live quote — see the "Renewal amount" section of the Subscriptions guide, and use | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "reason": "string"
}{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}Set nextBillingDate to an exact future instant. Valid for active or
paused subscriptions. Subsequent cycles advance from the new date.
Recorded in scheduleHistory.
| subscriptionId required | string |
| newBillingDate required | string <date-time> New |
| reason | string Recorded in the schedule-history entry |
| id required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| partnerId required | string | ||||||||||||||||||||||||||||||||||||||||||||||
| protocolId required | string Linked protocol that defines the regimen and customer | ||||||||||||||||||||||||||||||||||||||||||||||
| customerId required | string Customer this subscription bills; always the linked protocol's customer | ||||||||||||||||||||||||||||||||||||||||||||||
| daysSupply required | integer [ 1 .. 365 ] Renewal cadence in days, and the days of product each renewal order supplies. Set at creation; change it with | ||||||||||||||||||||||||||||||||||||||||||||||
| shippingMethod required | string Enum: "STANDARD" "EXPEDITED" "OVERNIGHT" Shipping method applied to renewal orders | ||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "PENDING" "ACTIVE" "PAST_DUE" "PAUSED" "CANCELLED" "FAILED" "AWAITING_PARTNER" Lifecycle status. | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| totalOrdersGenerated required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ScheduleChange) Audit trail of schedule changes | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| isActive required | boolean
| ||||||||||||||||||||||||||||||||||||||||||||||
| isDueForBilling required | boolean Active and | ||||||||||||||||||||||||||||||||||||||||||||||
| paymentFailureCount required | integer | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object (EmbeddedProtocol) Protocol summary embedded in the single-subscription GET response | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| nextBillingDate | string or null <date-time> Next renewal instant; renewals fire on the scheduler tick at or after this time | ||||||||||||||||||||||||||||||||||||||||||||||
| startDate | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| endDate | string or null <date-time> From this instant on (inclusive) no renewal is charged or flagged: when its next renewal comes due, the subscription is cancelled ( | ||||||||||||||||||||||||||||||||||||||||||||||
| lastOrderId | string or null Most recently generated order | ||||||||||||||||||||||||||||||||||||||||||||||
| notes | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| lastPaymentError | string or null | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedReason | string or null Enum: "customer_not_found" "no_provider_customer" "provider_customer_missing" "no_payment_method" "no_shipping_address" "zero_total" "protocol_not_found" null Set when a due platform-billed renewal can't be charged until its cause is fixed; null otherwise. Cleared when a renewal is charged, or the subscription is paused or cancelled. See | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedAt | string or null <date-time> | ||||||||||||||||||||||||||||||||||||||||||||||
| renewalBlockedMessage | string or null Human-readable explanation of | ||||||||||||||||||||||||||||||||||||||||||||||
| cadenceTermsAcceptedAt | string or null <date-time> When you asserted, via | ||||||||||||||||||||||||||||||||||||||||||||||
| payer | string or null Enum: "customer" "practice" Who is charged on each renewal, frozen when the subscription was created. | ||||||||||||||||||||||||||||||||||||||||||||||
object or null What the next renewal is expected to cost. A stored snapshot, not a live quote — see the "Renewal amount" section of the Subscriptions guide, and use | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "newBillingDate": "2019-08-24T14:15:22Z",
- "reason": "string"
}{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}
}Create a hosted checkout session: a tokenized payment page URL, valid for 24 hours, that you send your customer to complete payment.
Three mutually exclusive input modes (exactly one required):
protocolId — line items come from the protocol. Customer
identity (email, name, phone) and shipping address auto-populate from
the protocol's linked customer; request values override them, except
that a mismatched customerId is ignored — the protocol's customer
always wins. Creating a session for a protocol automatically cancels
any older pending sessions for the same protocol (and their payment
intents), so at most one session per protocol is active.products — an explicit product list. customerEmail,
firstName, lastName, and shippingAddress are then required.cartId — lines and cadence come from a cart your shopper built
with the Carts API (POST /v2/carts). Customer fields are required
as in products mode, and daysSupply must be omitted
(422 DAYS_SUPPLY_NOT_ALLOWED). Lines are priced at your storefront
discount; in subscription mode that's the first charge only, and
renewals are priced at your default discount, so a renewal can cost
more than the first charge (nextRenewalEstimate shows it).
Creating a session from a cart cancels that cart's previous open
session and its payment intent, so a cart has at most one open
session. When checkout completes, the purchased lines are removed
from the cart. A missing, expired or malformed cartId, or another
partner's cart, is 404 CART_NOT_FOUND; an empty cart is
422 CART_EMPTY; a cart holding a product that's no longer for sale
is 422 CART_NOT_PURCHASABLE. If another session is being created
from the same cart at the same moment, the call is
503 CART_BUSY with Retry-After: 1 and the other session stands;
a retry creates a new session that cancels it, so if you already
hold the other call's 201, keep using that session. Re-sending a
request with the same Idempotency-Key returns the original
session, even after payment has cleared the cart.Creating a session prices the cart immediately (subtotal plus sales tax
for the shipping address; volume and standing discounts applied — the
quoted amount equals what will be charged) and creates the payment
intent. Shipping is not included at creation: the customer selects a
shipping method on the hosted page, which updates totalAmount.
In subscription mode, completing checkout also creates a
Juniper-managed subscription with the card saved for renewals;
daysSupply is required (except with cartId, where the cart's
cadence is used), and subscription mode must come from a
protocolId or a cartId — a products-mode subscription checkout
is refused with 400.
Completion is asynchronous — after the customer pays, the order (and
subscription) are created within seconds. The canonical completion
signals are the order.created webhook event and, in subscription
mode, subscription.created — which carries the subscriptionId, so
you no longer need to poll the session for it. You may still poll
until status is completed and orderId (and subscriptionId)
populate.
The returned checkoutUrl has the form
{origin}/checkout/{accessToken} — origin is your active, site-linked
custom domain if you have one, otherwise the platform domain. The
embedded access token is the only credential for the payment page:
treat the URL as sensitive.
Elements mode. Send uiMode: "elements" to render the checkout on
your own site with Stripe's Payment Element instead of the hosted
page: the response carries clientSecret (the PaymentIntent client
secret), stripePublishableKey and customerSessionClientSecret
(which lets the Payment Element redisplay the customer's saved
cards), and checkoutUrl is null. A
shipping address is required at creation on this API, so the
PaymentIntent — and therefore clientSecret — is present on the 201.
Elements mode is gated per partner: 403 ELEMENTS_CHECKOUT_DISABLED
unless DailyStak has enabled partner-hosted checkout for you, 409
ELEMENTS_REQUIRES_JUNIPER_MANAGED unless you are on JUNIPER_MANAGED
billing, 503 ELEMENTS_CHECKOUT_NOT_CONFIGURED if the environment is
not configured for it. The gate is evaluated at creation and again on
every reprice call; if the switch is turned off later, an existing
elements session can still be fetched and paid (and completes
normally) but can no longer be repriced. Reprice the session through
the /address, /shipping and /promo sub-resources.
Recurring terms on an elements subscription. A new subscription
releases clientSecret only once the recurring terms are accepted.
Either send the customer to checkoutUrl to accept them on our page,
or present the terms on your own page and send termsAcceptedAt: your
affirmation that the customer accepted them, and when. The 201 then
carries recurringTerms.status: accepted and a clientSecret, and the
acceptance carries over the /address, /shipping and /promo calls.
Send an Idempotency-Key header to make the call safe to retry;
without one, every call creates a new session and a new payment
intent.
| Idempotency-Key | string [ 1 .. 255 ] characters Example: 5f1e8b6c-3c1e-4f5a-9d2b-7a0c4e2f81aa Opaque key that makes this POST safe to retry. A retry carrying the same key and the same body returns the resource the first call created (201, plus |
| protocolId | string or null Build the cart from this protocol's line items. Mutually exclusive with | ||||||||||
Array of objects or null (CheckoutProductRequest) Explicit product list. Mutually exclusive with | |||||||||||
Array
| |||||||||||
| cartId | string or null^crt_[A-Za-z0-9_-]{22}$ Check out a cart built with the Carts API. Lines and cadence come from the cart, so | ||||||||||
| customerEmail | string or null <email> Required unless | ||||||||||
| firstName | string or null Required unless auto-populated from the protocol's customer | ||||||||||
| lastName | string or null Required unless auto-populated from the protocol's customer | ||||||||||
| phone | string or null 10 digits (11 accepted with a leading 1) | ||||||||||
object (Address) Physical address information | |||||||||||
| |||||||||||
| mode | string Default: "one_time" Enum: "one_time" "subscription" | ||||||||||
| customerId | string or null Existing platform customer, enabling saved-payment reuse and automatic first-order promotions. Ignored on protocol checkouts when it does not match the protocol's customer. Sending it vouches that the shopper IS this customer: a name sent later on | ||||||||||
| daysSupply | integer or null Required in | ||||||||||
| successUrl | string or null Accepted and stored, but not currently used for redirection: the hosted page ends on the DailyStak confirmation page. Not applicable in elements mode (your | ||||||||||
| cancelUrl | string or null Accepted and stored; not currently used for redirection | ||||||||||
| source | string or null Enum: "link" "widget" "redirect" "api" "storefront" Where this checkout originated | ||||||||||
object or null Arbitrary metadata. Values may be strings, integers, booleans, null, or nested objects/arrays of those; decimal numbers (e.g. | |||||||||||
| |||||||||||
| partnerConsent | boolean Default: false Customer consent for partner account management; synced onto the customer record at completion | ||||||||||
| uiMode | string Default: "hosted" Enum: "hosted" "elements"
| ||||||||||
| termsAcceptedAt | string or null <date-time> Elements subscriptions only ( | ||||||||||
| Idempotent-Replayed | string Example: "true"
|
| sessionId required | string Session identifier | ||||||||||||||||
| partnerId required | string | ||||||||||||||||
| customerEmail required | string <email> | ||||||||||||||||
| mode required | string Enum: "one_time" "subscription" | ||||||||||||||||
| status required | string Enum: "pending" "viewed" "processing" "completed" "failed" "expired" "cancelled" Session lifecycle. | ||||||||||||||||
required | Array of objects (CheckoutLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount as a decimal string, with volume and standing discounts applied. Pack products price per day of supply; external products count once per cycle. | ||||||||||||||||
| shippingCost required | string Shipping charged on this purchase, as a decimal string; | ||||||||||||||||
| taxAmount required | string Sales tax on this purchase, as a decimal string; | ||||||||||||||||
| totalAmount required | string Amount to be charged, as a decimal string: always | ||||||||||||||||
| currency required | string | ||||||||||||||||
| checkoutUrl required | string or null Hosted payment page URL ( | ||||||||||||||||
| uiMode required | string Enum: "hosted" "elements" Who renders the checkout — the hosted page, or your own site | ||||||||||||||||
| expiresAt required | string <date-time> Creation time + 24 hours; expired sessions cannot complete | ||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
| partnerConsent required | boolean | ||||||||||||||||
| customerFirstName | string or null | ||||||||||||||||
| customerLastName | string or null | ||||||||||||||||
| customerPhone | string or null Normalized to 10 digits | ||||||||||||||||
| daysSupply | integer or null | ||||||||||||||||
object or null Subscription mode only: what the FIRST renewal is expected to cost. Show this — not | |||||||||||||||||
| |||||||||||||||||
| shippingMethod | string or null For generic sessions, null until the customer selects a method on the hosted page. Protocol-checkout sessions are created with the method already resolved and echo its name. | ||||||||||||||||
object (Address) Physical address information | |||||||||||||||||
| |||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret. Populated only for | ||||||||||||||||
| stripePublishableKey | string or null DailyStak's Stripe publishable key; populated only for | ||||||||||||||||
| customerSessionClientSecret | string or null Stripe CustomerSession client secret, which lets the Payment Element redisplay the cards already saved for this customer. Pass it to | ||||||||||||||||
| stripeCheckoutSessionUrl | string or null Legacy field; always null | ||||||||||||||||
| orderId | string or null Set when the session completes | ||||||||||||||||
| subscriptionId | string or null Set when a subscription-mode session completes | ||||||||||||||||
| source | string or null Enum: "link" "widget" "redirect" "api" "storefront" | ||||||||||||||||
| viewedAt | string or null <date-time> May remain null; page-view tracking is not consistently populated | ||||||||||||||||
{- "protocolId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "mode": "subscription",
- "daysSupply": 30,
- "source": "api"
}{- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "customerEmail": "user@example.com",
- "customerFirstName": "string",
- "customerLastName": "string",
- "customerPhone": "string",
- "mode": "one_time",
- "status": "pending",
- "lineItems": [
- {
- "id": "string",
- "productId": "77123129891",
- "name": "string",
- "quantity": 0,
- "unitPrice": "0.37",
- "packDescription": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}
}
], - "daysSupply": 0,
- "subtotal": "22.20",
- "shippingCost": "0.00",
- "taxAmount": "1.83",
- "totalAmount": "24.03",
- "currency": "usd",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "shippingMethod": "string",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "clientSecret": "string",
- "stripePublishableKey": "string",
- "customerSessionClientSecret": "string",
- "uiMode": "hosted",
- "stripeCheckoutSessionUrl": "string",
- "expiresAt": "2019-08-24T14:15:22Z",
- "orderId": "string",
- "subscriptionId": "string",
- "source": "link",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "viewedAt": "2019-08-24T14:15:22Z",
- "partnerConsent": true
}List your checkout sessions, newest first, cursor-paginated.
A filtered page can contain fewer than limit items (even zero) while
more results remain — keep paging until nextCursor is null, not until
an empty page.
| limit | integer [ 1 .. 100 ] Default: 50 Page size, 1–100 |
| cursor | string Opaque cursor from a previous response's |
| status | string Enum: "pending" "viewed" "processing" "completed" "failed" "expired" "cancelled" Filter by session status |
| protocolId | string Only sessions created from this protocol |
required | Array of objects (CheckoutSession) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nextCursor required | string or null Cursor for the next page; null when exhausted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "sessions": [
- {
- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "customerEmail": "user@example.com",
- "customerFirstName": "string",
- "customerLastName": "string",
- "customerPhone": "string",
- "mode": "one_time",
- "status": "pending",
- "lineItems": [
- {
- "id": "string",
- "productId": "77123129891",
- "name": "string",
- "quantity": 0,
- "unitPrice": "0.37",
- "packDescription": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}
}
], - "daysSupply": 0,
- "subtotal": "22.20",
- "shippingCost": "0.00",
- "taxAmount": "1.83",
- "totalAmount": "24.03",
- "currency": "usd",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "shippingMethod": "string",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "clientSecret": "string",
- "stripePublishableKey": "string",
- "customerSessionClientSecret": "string",
- "uiMode": "hosted",
- "stripeCheckoutSessionUrl": "string",
- "expiresAt": "2019-08-24T14:15:22Z",
- "orderId": "string",
- "subscriptionId": "string",
- "source": "link",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "viewedAt": "2019-08-24T14:15:22Z",
- "partnerConsent": true
}
], - "nextCursor": "string"
}Fetch one session. Poll this after sending the customer to the checkout
page: on completion status becomes completed and orderId (and
subscriptionId in subscription mode) populate.
An expired session is returned with its stored status — expiry is not
eagerly applied, so compare expiresAt yourself. Another partner's
session is indistinguishable from a missing one (404).
| sessionId required | string |
| sessionId required | string Session identifier | ||||||||||||||||
| partnerId required | string | ||||||||||||||||
| customerEmail required | string <email> | ||||||||||||||||
| mode required | string Enum: "one_time" "subscription" | ||||||||||||||||
| status required | string Enum: "pending" "viewed" "processing" "completed" "failed" "expired" "cancelled" Session lifecycle. | ||||||||||||||||
required | Array of objects (CheckoutLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount as a decimal string, with volume and standing discounts applied. Pack products price per day of supply; external products count once per cycle. | ||||||||||||||||
| shippingCost required | string Shipping charged on this purchase, as a decimal string; | ||||||||||||||||
| taxAmount required | string Sales tax on this purchase, as a decimal string; | ||||||||||||||||
| totalAmount required | string Amount to be charged, as a decimal string: always | ||||||||||||||||
| currency required | string | ||||||||||||||||
| checkoutUrl required | string or null Hosted payment page URL ( | ||||||||||||||||
| uiMode required | string Enum: "hosted" "elements" Who renders the checkout — the hosted page, or your own site | ||||||||||||||||
| expiresAt required | string <date-time> Creation time + 24 hours; expired sessions cannot complete | ||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
| partnerConsent required | boolean | ||||||||||||||||
| customerFirstName | string or null | ||||||||||||||||
| customerLastName | string or null | ||||||||||||||||
| customerPhone | string or null Normalized to 10 digits | ||||||||||||||||
| daysSupply | integer or null | ||||||||||||||||
object or null Subscription mode only: what the FIRST renewal is expected to cost. Show this — not | |||||||||||||||||
| |||||||||||||||||
| shippingMethod | string or null For generic sessions, null until the customer selects a method on the hosted page. Protocol-checkout sessions are created with the method already resolved and echo its name. | ||||||||||||||||
object (Address) Physical address information | |||||||||||||||||
| |||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret. Populated only for | ||||||||||||||||
| stripePublishableKey | string or null DailyStak's Stripe publishable key; populated only for | ||||||||||||||||
| customerSessionClientSecret | string or null Stripe CustomerSession client secret, which lets the Payment Element redisplay the cards already saved for this customer. Pass it to | ||||||||||||||||
| stripeCheckoutSessionUrl | string or null Legacy field; always null | ||||||||||||||||
| orderId | string or null Set when the session completes | ||||||||||||||||
| subscriptionId | string or null Set when a subscription-mode session completes | ||||||||||||||||
| source | string or null Enum: "link" "widget" "redirect" "api" "storefront" | ||||||||||||||||
| viewedAt | string or null <date-time> May remain null; page-view tracking is not consistently populated | ||||||||||||||||
{- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "customerEmail": "user@example.com",
- "customerFirstName": "string",
- "customerLastName": "string",
- "customerPhone": "string",
- "mode": "one_time",
- "status": "pending",
- "lineItems": [
- {
- "id": "string",
- "productId": "77123129891",
- "name": "string",
- "quantity": 0,
- "unitPrice": "0.37",
- "packDescription": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}
}
], - "daysSupply": 0,
- "subtotal": "22.20",
- "shippingCost": "0.00",
- "taxAmount": "1.83",
- "totalAmount": "24.03",
- "currency": "usd",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "shippingMethod": "string",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "clientSecret": "string",
- "stripePublishableKey": "string",
- "customerSessionClientSecret": "string",
- "uiMode": "hosted",
- "stripeCheckoutSessionUrl": "string",
- "expiresAt": "2019-08-24T14:15:22Z",
- "orderId": "string",
- "subscriptionId": "string",
- "source": "link",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "viewedAt": "2019-08-24T14:15:22Z",
- "partnerConsent": true
}Cancel a session in any non-completed state and cancel its payment intent, so the checkout link stops working. A completed session cannot be cancelled (409) — the money has moved and the order exists; refunds are issued by DailyStak.
| sessionId required | string |
| sessionId required | string Session identifier | ||||||||||||||||
| partnerId required | string | ||||||||||||||||
| customerEmail required | string <email> | ||||||||||||||||
| mode required | string Enum: "one_time" "subscription" | ||||||||||||||||
| status required | string Enum: "pending" "viewed" "processing" "completed" "failed" "expired" "cancelled" Session lifecycle. | ||||||||||||||||
required | Array of objects (CheckoutLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount as a decimal string, with volume and standing discounts applied. Pack products price per day of supply; external products count once per cycle. | ||||||||||||||||
| shippingCost required | string Shipping charged on this purchase, as a decimal string; | ||||||||||||||||
| taxAmount required | string Sales tax on this purchase, as a decimal string; | ||||||||||||||||
| totalAmount required | string Amount to be charged, as a decimal string: always | ||||||||||||||||
| currency required | string | ||||||||||||||||
| checkoutUrl required | string or null Hosted payment page URL ( | ||||||||||||||||
| uiMode required | string Enum: "hosted" "elements" Who renders the checkout — the hosted page, or your own site | ||||||||||||||||
| expiresAt required | string <date-time> Creation time + 24 hours; expired sessions cannot complete | ||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
| partnerConsent required | boolean | ||||||||||||||||
| customerFirstName | string or null | ||||||||||||||||
| customerLastName | string or null | ||||||||||||||||
| customerPhone | string or null Normalized to 10 digits | ||||||||||||||||
| daysSupply | integer or null | ||||||||||||||||
object or null Subscription mode only: what the FIRST renewal is expected to cost. Show this — not | |||||||||||||||||
| |||||||||||||||||
| shippingMethod | string or null For generic sessions, null until the customer selects a method on the hosted page. Protocol-checkout sessions are created with the method already resolved and echo its name. | ||||||||||||||||
object (Address) Physical address information | |||||||||||||||||
| |||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret. Populated only for | ||||||||||||||||
| stripePublishableKey | string or null DailyStak's Stripe publishable key; populated only for | ||||||||||||||||
| customerSessionClientSecret | string or null Stripe CustomerSession client secret, which lets the Payment Element redisplay the cards already saved for this customer. Pass it to | ||||||||||||||||
| stripeCheckoutSessionUrl | string or null Legacy field; always null | ||||||||||||||||
| orderId | string or null Set when the session completes | ||||||||||||||||
| subscriptionId | string or null Set when a subscription-mode session completes | ||||||||||||||||
| source | string or null Enum: "link" "widget" "redirect" "api" "storefront" | ||||||||||||||||
| viewedAt | string or null <date-time> May remain null; page-view tracking is not consistently populated | ||||||||||||||||
{- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "customerEmail": "user@example.com",
- "customerFirstName": "string",
- "customerLastName": "string",
- "customerPhone": "string",
- "mode": "one_time",
- "status": "pending",
- "lineItems": [
- {
- "id": "string",
- "productId": "77123129891",
- "name": "string",
- "quantity": 0,
- "unitPrice": "0.37",
- "packDescription": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}
}
], - "daysSupply": 0,
- "subtotal": "22.20",
- "shippingCost": "0.00",
- "taxAmount": "1.83",
- "totalAmount": "24.03",
- "currency": "usd",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "shippingMethod": "string",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "clientSecret": "string",
- "stripePublishableKey": "string",
- "customerSessionClientSecret": "string",
- "uiMode": "hosted",
- "stripeCheckoutSessionUrl": "string",
- "expiresAt": "2019-08-24T14:15:22Z",
- "orderId": "string",
- "subscriptionId": "string",
- "source": "link",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "viewedAt": "2019-08-24T14:15:22Z",
- "partnerConsent": true
}Replace the delivery address (and optionally the billing address) of
an uiMode: elements session and reprice it: tax is recalculated for
the new address and the PaymentIntent amount is updated in place.
Then call elements.fetchUpdates() on your page so the Payment
Element shows the new amount.
Elements sessions only — a hosted session is refused with 409
ELEMENTS_SESSION_REQUIRED. The partner-hosted checkout gate is
evaluated on every call, before the session is read.
| sessionId required | string |
| line1 required | string non-empty Street address line 1 | ||||||||||
| city required | string non-empty | ||||||||||
| state required | string = 2 characters Two-letter US state code (the 50 states, DC, and PR; territories like GU/VI and military codes are not accepted) | ||||||||||
| zipCode required | string = 5 characters ^\d{5}$ 5-digit ZIP code | ||||||||||
| line2 | string or null Street address line 2 | ||||||||||
object or null Optional billing address when it differs from the delivery address | |||||||||||
| |||||||||||
| firstName | string or null non-empty New recipient first name; the order ships to this name. Unlike the address, which this call replaces, an omitted or | ||||||||||
| lastName | string or null non-empty New recipient last name; same rules as | ||||||||||
| sessionId required | string | ||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount with discounts applied | ||||||||||||||||
| shippingCost required | string | ||||||||||||||||
| taxAmount required | string | ||||||||||||||||
| totalAmount required | string The amount the PaymentIntent now charges | ||||||||||||||||
| effectiveDiscount required | integer Effective discount percent applied to the cart | ||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| checkoutUrl | string or null Customer acceptance URL for new subscription checkouts. | ||||||||||||||||
object or null Subscription mode only: the current estimated cost of the first renewal, so one response refreshes both numbers on your page. An address change re-estimates it; changing THIS order's shipping method or promo does not move it (a renewal ships Standard and carries no promotion). Never show | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret; null while new recurring terms require acceptance or the session can no longer be paid | ||||||||||||||||
| appliedPromoCode | string or null The promo code currently applied, if any | ||||||||||||||||
| appliedPromoType | string or null Enum: "percent" "flat" Type of the applied promo, if any | ||||||||||||||||
{- "line1": "123 Main St",
- "line2": "string",
- "city": "Austin",
- "state": "TX",
- "zipCode": "78701",
- "billingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "firstName": "Ada",
- "lastName": "Lovelace"
}{- "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "subtotal": "22.20",
- "shippingCost": "9.99",
- "taxAmount": "1.83",
- "totalAmount": "34.02",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "clientSecret": "string",
- "appliedPromoCode": "string",
- "appliedPromoType": "percent",
- "effectiveDiscount": 10
}Select one of your active shipping methods on an uiMode: elements
session and reprice it: shippingCost is set (the free-shipping
threshold rule applies) and the PaymentIntent amount is updated in
place. Then call elements.fetchUpdates() on your page.
Elements sessions only — a hosted session is refused with 409
ELEMENTS_SESSION_REQUIRED.
| sessionId required | string |
| shippingMethodId required | string non-empty Id of one of your active shipping methods — use the |
| sessionId required | string | ||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount with discounts applied | ||||||||||||||||
| shippingCost required | string | ||||||||||||||||
| taxAmount required | string | ||||||||||||||||
| totalAmount required | string The amount the PaymentIntent now charges | ||||||||||||||||
| effectiveDiscount required | integer Effective discount percent applied to the cart | ||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| checkoutUrl | string or null Customer acceptance URL for new subscription checkouts. | ||||||||||||||||
object or null Subscription mode only: the current estimated cost of the first renewal, so one response refreshes both numbers on your page. An address change re-estimates it; changing THIS order's shipping method or promo does not move it (a renewal ships Standard and carries no promotion). Never show | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret; null while new recurring terms require acceptance or the session can no longer be paid | ||||||||||||||||
| appliedPromoCode | string or null The promo code currently applied, if any | ||||||||||||||||
| appliedPromoType | string or null Enum: "percent" "flat" Type of the applied promo, if any | ||||||||||||||||
{- "shippingMethodId": "string"
}{- "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "subtotal": "22.20",
- "shippingCost": "9.99",
- "taxAmount": "1.83",
- "totalAmount": "34.02",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "clientSecret": "string",
- "appliedPromoCode": "string",
- "appliedPromoType": "percent",
- "effectiveDiscount": 10
}Validate a promo code against your promotions (case-insensitive;
surrounding whitespace ignored), apply it to an uiMode: elements
session and reprice it, updating the PaymentIntent amount in place.
Then call elements.fetchUpdates() on your page. Applying a second
code replaces the first.
Elements sessions only — a hosted session is refused with 409
ELEMENTS_SESSION_REQUIRED.
| sessionId required | string |
| code required | string non-empty Promo code. Surrounding whitespace is stripped and matching is case-insensitive. |
| sessionId required | string | ||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount with discounts applied | ||||||||||||||||
| shippingCost required | string | ||||||||||||||||
| taxAmount required | string | ||||||||||||||||
| totalAmount required | string The amount the PaymentIntent now charges | ||||||||||||||||
| effectiveDiscount required | integer Effective discount percent applied to the cart | ||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| checkoutUrl | string or null Customer acceptance URL for new subscription checkouts. | ||||||||||||||||
object or null Subscription mode only: the current estimated cost of the first renewal, so one response refreshes both numbers on your page. An address change re-estimates it; changing THIS order's shipping method or promo does not move it (a renewal ships Standard and carries no promotion). Never show | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret; null while new recurring terms require acceptance or the session can no longer be paid | ||||||||||||||||
| appliedPromoCode | string or null The promo code currently applied, if any | ||||||||||||||||
| appliedPromoType | string or null Enum: "percent" "flat" Type of the applied promo, if any | ||||||||||||||||
{- "code": "WELCOME10"
}{- "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "subtotal": "22.20",
- "shippingCost": "9.99",
- "taxAmount": "1.83",
- "totalAmount": "34.02",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "clientSecret": "string",
- "appliedPromoCode": "string",
- "appliedPromoType": "percent",
- "effectiveDiscount": 10
}Clear the promo code applied to an uiMode: elements session and
reprice it back, updating the PaymentIntent amount in place. Then call
elements.fetchUpdates() on your page. If the session was created
with your automatic first-order promotion, that promotion is restored
rather than repricing to full price. Succeeds (200) even when no code
is applied.
Elements sessions only — a hosted session is refused with 409
ELEMENTS_SESSION_REQUIRED.
| sessionId required | string |
| sessionId required | string | ||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount with discounts applied | ||||||||||||||||
| shippingCost required | string | ||||||||||||||||
| taxAmount required | string | ||||||||||||||||
| totalAmount required | string The amount the PaymentIntent now charges | ||||||||||||||||
| effectiveDiscount required | integer Effective discount percent applied to the cart | ||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| checkoutUrl | string or null Customer acceptance URL for new subscription checkouts. | ||||||||||||||||
object or null Subscription mode only: the current estimated cost of the first renewal, so one response refreshes both numbers on your page. An address change re-estimates it; changing THIS order's shipping method or promo does not move it (a renewal ships Standard and carries no promotion). Never show | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret; null while new recurring terms require acceptance or the session can no longer be paid | ||||||||||||||||
| appliedPromoCode | string or null The promo code currently applied, if any | ||||||||||||||||
| appliedPromoType | string or null Enum: "percent" "flat" Type of the applied promo, if any | ||||||||||||||||
{- "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "subtotal": "22.20",
- "shippingCost": "9.99",
- "taxAmount": "1.83",
- "totalAmount": "34.02",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "clientSecret": "string",
- "appliedPromoCode": "string",
- "appliedPromoType": "percent",
- "effectiveDiscount": 10
}Create a hosted checkout session for a recurring subscription built
from a protocol's current regimen. Send the customer to the returned
checkoutUrl (valid 24 hours); on completion of payment a
subscription is created and linked to this protocol, with the card
saved for renewals. Older pending sessions for the same protocol are
automatically cancelled.
daysSupply and shippingMethod are supplied at checkout time (they
are not stored on the protocol) and determine the resulting
subscription's billing cadence and shipping. shippingMethod is
matched case-insensitively against your configured shipping-method
names.
Completion is asynchronous — listen for the order.created webhook
event or poll the session.
| protocolId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| daysSupply | integer [ 1 .. 365 ] Default: 30 Number of days each order supplies (determines billing frequency) | ||
| shippingMethod | string Default: "STANDARD" Shipping method name (canonically STANDARD, EXPEDITED, or OVERNIGHT), matched case-insensitively against your configured shipping methods | ||
| successUrl | string or null Redirect URL after successful checkout | ||
| cancelUrl | string or null Redirect URL when checkout is cancelled | ||
object or null Additional metadata to attach to the checkout session | |||
| |||
| sessionId required | string Session identifier | ||||||||||||||||
| partnerId required | string | ||||||||||||||||
| customerEmail required | string <email> | ||||||||||||||||
| mode required | string Enum: "one_time" "subscription" | ||||||||||||||||
| status required | string Enum: "pending" "viewed" "processing" "completed" "failed" "expired" "cancelled" Session lifecycle. | ||||||||||||||||
required | Array of objects (CheckoutLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount as a decimal string, with volume and standing discounts applied. Pack products price per day of supply; external products count once per cycle. | ||||||||||||||||
| shippingCost required | string Shipping charged on this purchase, as a decimal string; | ||||||||||||||||
| taxAmount required | string Sales tax on this purchase, as a decimal string; | ||||||||||||||||
| totalAmount required | string Amount to be charged, as a decimal string: always | ||||||||||||||||
| currency required | string | ||||||||||||||||
| checkoutUrl required | string or null Hosted payment page URL ( | ||||||||||||||||
| uiMode required | string Enum: "hosted" "elements" Who renders the checkout — the hosted page, or your own site | ||||||||||||||||
| expiresAt required | string <date-time> Creation time + 24 hours; expired sessions cannot complete | ||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
| partnerConsent required | boolean | ||||||||||||||||
| customerFirstName | string or null | ||||||||||||||||
| customerLastName | string or null | ||||||||||||||||
| customerPhone | string or null Normalized to 10 digits | ||||||||||||||||
| daysSupply | integer or null | ||||||||||||||||
object or null Subscription mode only: what the FIRST renewal is expected to cost. Show this — not | |||||||||||||||||
| |||||||||||||||||
| shippingMethod | string or null For generic sessions, null until the customer selects a method on the hosted page. Protocol-checkout sessions are created with the method already resolved and echo its name. | ||||||||||||||||
object (Address) Physical address information | |||||||||||||||||
| |||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret. Populated only for | ||||||||||||||||
| stripePublishableKey | string or null DailyStak's Stripe publishable key; populated only for | ||||||||||||||||
| customerSessionClientSecret | string or null Stripe CustomerSession client secret, which lets the Payment Element redisplay the cards already saved for this customer. Pass it to | ||||||||||||||||
| stripeCheckoutSessionUrl | string or null Legacy field; always null | ||||||||||||||||
| orderId | string or null Set when the session completes | ||||||||||||||||
| subscriptionId | string or null Set when a subscription-mode session completes | ||||||||||||||||
| source | string or null Enum: "link" "widget" "redirect" "api" "storefront" | ||||||||||||||||
| viewedAt | string or null <date-time> May remain null; page-view tracking is not consistently populated | ||||||||||||||||
{- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "successUrl": "string",
- "cancelUrl": "string",
- "metadata": { }
}{- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "customerEmail": "user@example.com",
- "customerFirstName": "string",
- "customerLastName": "string",
- "customerPhone": "string",
- "mode": "one_time",
- "status": "pending",
- "lineItems": [
- {
- "id": "string",
- "productId": "77123129891",
- "name": "string",
- "quantity": 0,
- "unitPrice": "0.37",
- "packDescription": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}
}
], - "daysSupply": 0,
- "subtotal": "22.20",
- "shippingCost": "0.00",
- "taxAmount": "1.83",
- "totalAmount": "24.03",
- "currency": "usd",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "shippingMethod": "string",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "clientSecret": "string",
- "stripePublishableKey": "string",
- "customerSessionClientSecret": "string",
- "uiMode": "hosted",
- "stripeCheckoutSessionUrl": "string",
- "expiresAt": "2019-08-24T14:15:22Z",
- "orderId": "string",
- "subscriptionId": "string",
- "source": "link",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "viewedAt": "2019-08-24T14:15:22Z",
- "partnerConsent": true
}Create a hosted checkout session for a single (one-time) order built from a protocol's current regimen. Unlike the subscription checkout, no recurring billing is set up — completing checkout produces a single order. Sessions expire after 24 hours; older pending sessions for the same protocol are automatically cancelled.
| protocolId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| daysSupply | integer [ 1 .. 365 ] Default: 30 Number of days each order supplies (determines billing frequency) | ||
| shippingMethod | string Default: "STANDARD" Shipping method name (canonically STANDARD, EXPEDITED, or OVERNIGHT), matched case-insensitively against your configured shipping methods | ||
| successUrl | string or null Redirect URL after successful checkout | ||
| cancelUrl | string or null Redirect URL when checkout is cancelled | ||
object or null Additional metadata to attach to the checkout session | |||
| |||
| sessionId required | string Session identifier | ||||||||||||||||
| partnerId required | string | ||||||||||||||||
| customerEmail required | string <email> | ||||||||||||||||
| mode required | string Enum: "one_time" "subscription" | ||||||||||||||||
| status required | string Enum: "pending" "viewed" "processing" "completed" "failed" "expired" "cancelled" Session lifecycle. | ||||||||||||||||
required | Array of objects (CheckoutLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| subtotal required | string Pre-shipping, pre-tax amount as a decimal string, with volume and standing discounts applied. Pack products price per day of supply; external products count once per cycle. | ||||||||||||||||
| shippingCost required | string Shipping charged on this purchase, as a decimal string; | ||||||||||||||||
| taxAmount required | string Sales tax on this purchase, as a decimal string; | ||||||||||||||||
| totalAmount required | string Amount to be charged, as a decimal string: always | ||||||||||||||||
| currency required | string | ||||||||||||||||
| checkoutUrl required | string or null Hosted payment page URL ( | ||||||||||||||||
| uiMode required | string Enum: "hosted" "elements" Who renders the checkout — the hosted page, or your own site | ||||||||||||||||
| expiresAt required | string <date-time> Creation time + 24 hours; expired sessions cannot complete | ||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
| partnerConsent required | boolean | ||||||||||||||||
| customerFirstName | string or null | ||||||||||||||||
| customerLastName | string or null | ||||||||||||||||
| customerPhone | string or null Normalized to 10 digits | ||||||||||||||||
| daysSupply | integer or null | ||||||||||||||||
object or null Subscription mode only: what the FIRST renewal is expected to cost. Show this — not | |||||||||||||||||
| |||||||||||||||||
| shippingMethod | string or null For generic sessions, null until the customer selects a method on the hosted page. Protocol-checkout sessions are created with the method already resolved and echo its name. | ||||||||||||||||
object (Address) Physical address information | |||||||||||||||||
| |||||||||||||||||
object or null Customer authorization for this frozen purchase; new subscriptions require explicit checkout-token acceptance before a client secret is available. | |||||||||||||||||
| |||||||||||||||||
| clientSecret | string or null Stripe PaymentIntent client secret. Populated only for | ||||||||||||||||
| stripePublishableKey | string or null DailyStak's Stripe publishable key; populated only for | ||||||||||||||||
| customerSessionClientSecret | string or null Stripe CustomerSession client secret, which lets the Payment Element redisplay the cards already saved for this customer. Pass it to | ||||||||||||||||
| stripeCheckoutSessionUrl | string or null Legacy field; always null | ||||||||||||||||
| orderId | string or null Set when the session completes | ||||||||||||||||
| subscriptionId | string or null Set when a subscription-mode session completes | ||||||||||||||||
| source | string or null Enum: "link" "widget" "redirect" "api" "storefront" | ||||||||||||||||
| viewedAt | string or null <date-time> May remain null; page-view tracking is not consistently populated | ||||||||||||||||
{- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "successUrl": "string",
- "cancelUrl": "string",
- "metadata": { }
}{- "sessionId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "customerEmail": "user@example.com",
- "customerFirstName": "string",
- "customerLastName": "string",
- "customerPhone": "string",
- "mode": "one_time",
- "status": "pending",
- "lineItems": [
- {
- "id": "string",
- "productId": "77123129891",
- "name": "string",
- "quantity": 0,
- "unitPrice": "0.37",
- "packDescription": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}
}
], - "daysSupply": 0,
- "subtotal": "22.20",
- "shippingCost": "0.00",
- "taxAmount": "1.83",
- "totalAmount": "24.03",
- "currency": "usd",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "shippingMethod": "string",
- "shippingAddress": {
- "line1": "437 Lytton",
- "line2": "Suite 100",
- "city": "Palo Alto",
- "state": "CA",
- "zipCode": "94301"
}, - "recurringTerms": {
- "required": true,
- "status": "acceptance_required",
- "purchaseRevision": "string",
- "policyVersion": "string",
- "disclosureHash": "string",
- "disclosureText": "string",
- "acceptedAt": "2019-08-24T14:15:22Z"
}, - "checkoutUrl": "string",
- "clientSecret": "string",
- "stripePublishableKey": "string",
- "customerSessionClientSecret": "string",
- "uiMode": "hosted",
- "stripeCheckoutSessionUrl": "string",
- "expiresAt": "2019-08-24T14:15:22Z",
- "orderId": "string",
- "subscriptionId": "string",
- "source": "link",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "viewedAt": "2019-08-24T14:15:22Z",
- "partnerConsent": true
}Create an empty cart for your own storefront or app: a 30-day cadence,
no lines, revision: 0. The response carries cartSecret, which lets
the shopper's browser or app read and edit this one cart on the public
cart host. The secret is returned only here and by
POST /v2/carts/{cartId}/secret, so store it wherever you store the
cartId.
A cart expires after 90 days without a change (expiresAt); after
that it reads as 404 CART_NOT_FOUND. There is no idempotency on this
call: a retried create leaves an empty cart that expires on its own.
Responses are sent with Cache-Control: no-store.
| cartId required | string The cart's id, | ||||||||||||||||||||
| revision required | integer >= 0 Increments on every change, including checkout completion removing the purchased lines; | ||||||||||||||||||||
| daysSupply required | integer Enum: 30 60 90 Days of supply per shipment; a checkout from the cart uses it | ||||||||||||||||||||
required | Array of objects (CartLine) The cart's lines, in product ID order | ||||||||||||||||||||
Array
| |||||||||||||||||||||
| subtotal required | string The cart's total for one shipment at your storefront rates, as a decimal string in the same format as | ||||||||||||||||||||
| currency required | string | ||||||||||||||||||||
| updatedAt required | string or null <date-time> When the cart or any of its lines last changed | ||||||||||||||||||||
| expiresAt required | string or null <date-time> When the cart expires: 90 days after its last change. Changes move it out; reads don't. | ||||||||||||||||||||
| doseWarning required | boolean
| ||||||||||||||||||||
| cartSecret required | string The bearer credential for this cart on the public cart host. Returned only here and on rotation, so store it with the | ||||||||||||||||||||
{- "cartId": "crt_2mH3kQ7vX9pL4nR8sT1uWy",
- "revision": 7,
- "daysSupply": 30,
- "items": [
- {
- "productId": "11234",
- "name": "Magnesium Glycinate",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "unitsPerCycle": 1,
- "unitsPerShipment": 60,
- "customerPrice": "0.425",
- "retailPrice": "0.50",
- "maxDailyUnits": 4,
- "protocolTemplateId": null
}
], - "subtotal": "25.50",
- "currency": "usd",
- "updatedAt": "2026-09-23T18:00:00Z",
- "expiresAt": "2026-12-22T18:00:00Z",
- "doseWarning": false,
- "cartSecret": "crt_2mH3kQ7vX9pL4nR8sT1uWy.Zk8pQ2wR7xT4nV1bY6cM3hJ9dF5gL0sA2eU8iO4kW7r"
}Read the whole priced cart. Two ways to call it:
https://api.fulfillment.juniperhealth.com, sandbox
https://api.fulfillment.sandbox.juniperhealth.com), with your
access token (order/v2.read). No secret needed; another partner's
cart, or a cartId that isn't a cart id, is 404 CART_NOT_FOUND.
Token failures are the usual gateway 401/403.https://cart.fulfillment.juniperhealth.com, sandbox
https://cart.fulfillment.sandbox.juniperhealth.com), with
Authorization: Bearer <cartSecret>. Any origin may call it
(CORS *). A secret for a different cart than cartId is
401 CART_SECRET_INVALID.Reading never changes revision or extends expiresAt. The response
never carries cartSecret, and is sent with Cache-Control: no-store.
| cartId required | string^crt_[A-Za-z0-9_-]{22}$ Example: crt_2mH3kQ7vX9pL4nR8sT1uWy The cart's id ( |
| cartId required | string The cart's id, | ||||||||||||||||||||
| revision required | integer >= 0 Increments on every change, including checkout completion removing the purchased lines; | ||||||||||||||||||||
| daysSupply required | integer Enum: 30 60 90 Days of supply per shipment; a checkout from the cart uses it | ||||||||||||||||||||
required | Array of objects (CartLine) The cart's lines, in product ID order | ||||||||||||||||||||
Array
| |||||||||||||||||||||
| subtotal required | string The cart's total for one shipment at your storefront rates, as a decimal string in the same format as | ||||||||||||||||||||
| currency required | string | ||||||||||||||||||||
| updatedAt required | string or null <date-time> When the cart or any of its lines last changed | ||||||||||||||||||||
| expiresAt required | string or null <date-time> When the cart expires: 90 days after its last change. Changes move it out; reads don't. | ||||||||||||||||||||
| doseWarning required | boolean
| ||||||||||||||||||||
{- "cartId": "crt_2mH3kQ7vX9pL4nR8sT1uWy",
- "revision": 7,
- "daysSupply": 30,
- "items": [
- {
- "productId": "11234",
- "name": "Magnesium Glycinate",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "unitsPerCycle": 1,
- "unitsPerShipment": 60,
- "customerPrice": "0.425",
- "retailPrice": "0.50",
- "maxDailyUnits": 4,
- "protocolTemplateId": null
}
], - "subtotal": "25.50",
- "currency": "usd",
- "updatedAt": "2026-09-23T18:00:00Z",
- "expiresAt": "2026-12-22T18:00:00Z",
- "doseWarning": false
}Set the cart's cadence: 30, 60 or 90 days of supply per shipment. This
is the cadence a checkout from the cart uses. It always writes and
increments revision, even when the value is unchanged.
Shopper route: public cart host, Authorization: Bearer <cartSecret>.
A value other than 30, 60 or 90, a number that isn't a plain integer
(60.0, "60", true), or any other key in the body is
422 VALIDATION_FAILED.
| cartId required | string^crt_[A-Za-z0-9_-]{22}$ Example: crt_2mH3kQ7vX9pL4nR8sT1uWy The cart's id ( |
| daysSupply required | integer Enum: 30 60 90 Days of supply per shipment |
| cartId required | string The cart's id, | ||||||||||||||||||||
| revision required | integer >= 0 Increments on every change, including checkout completion removing the purchased lines; | ||||||||||||||||||||
| daysSupply required | integer Enum: 30 60 90 Days of supply per shipment; a checkout from the cart uses it | ||||||||||||||||||||
required | Array of objects (CartLine) The cart's lines, in product ID order | ||||||||||||||||||||
Array
| |||||||||||||||||||||
| subtotal required | string The cart's total for one shipment at your storefront rates, as a decimal string in the same format as | ||||||||||||||||||||
| currency required | string | ||||||||||||||||||||
| updatedAt required | string or null <date-time> When the cart or any of its lines last changed | ||||||||||||||||||||
| expiresAt required | string or null <date-time> When the cart expires: 90 days after its last change. Changes move it out; reads don't. | ||||||||||||||||||||
| doseWarning required | boolean
| ||||||||||||||||||||
{- "daysSupply": 60
}{- "cartId": "crt_2mH3kQ7vX9pL4nR8sT1uWy",
- "revision": 7,
- "daysSupply": 30,
- "items": [
- {
- "productId": "11234",
- "name": "Magnesium Glycinate",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "unitsPerCycle": 1,
- "unitsPerShipment": 60,
- "customerPrice": "0.425",
- "retailPrice": "0.50",
- "maxDailyUnits": 4,
- "protocolTemplateId": null
}
], - "subtotal": "25.50",
- "currency": "usd",
- "updatedAt": "2026-09-23T18:00:00Z",
- "expiresAt": "2026-12-22T18:00:00Z",
- "doseWarning": false
}Set one line, replacing it if the product is already in the cart. The write is absolute ("set this line to 3"), so repeating it is safe and overlapping writes end in the state of the last one.
Send exactly one of:
administration for a product dispensed by dose: morning,
afternoon, evening and sleep counts, at least one above zero. The
daily total is checked against the product's dose cap
(maxDailyUnits on the line); above the recommended amount but
within the cap is allowed and sets doseWarning: true on the
response.unitsPerCycle for a pre-packaged product: 1-50 units per shipment.Sending the one that doesn't match the product is
422 LINE_MODE_MISMATCH. A cart holds at most 50 distinct products.
Rewriting a line that came from a template keeps its
protocolTemplateId.
The body is strict: an unknown key (such as a misspelled daypart) or
a number that isn't a plain integer (true, "2", 2.0) is
422 VALIDATION_FAILED. A productId that isn't a plain integer of
at most 20 digits is 422 PRODUCT_NOT_FOUND.
Shopper route: public cart host, Authorization: Bearer <cartSecret>.
| cartId required | string^crt_[A-Za-z0-9_-]{22}$ Example: crt_2mH3kQ7vX9pL4nR8sT1uWy The cart's id ( |
| productId required | string Example: 11234 Catalog product ID, written as a plain integer of at most 20 digits with no sign or leading zeros |
object or null The daily dose, for a product dispensed by dose | |||||||||
| |||||||||
| unitsPerCycle | integer or null [ 1 .. 50 ] Units per shipment, for a pre-packaged product. A plain integer: | ||||||||
| cartId required | string The cart's id, | ||||||||||||||||||||
| revision required | integer >= 0 Increments on every change, including checkout completion removing the purchased lines; | ||||||||||||||||||||
| daysSupply required | integer Enum: 30 60 90 Days of supply per shipment; a checkout from the cart uses it | ||||||||||||||||||||
required | Array of objects (CartLine) The cart's lines, in product ID order | ||||||||||||||||||||
Array
| |||||||||||||||||||||
| subtotal required | string The cart's total for one shipment at your storefront rates, as a decimal string in the same format as | ||||||||||||||||||||
| currency required | string | ||||||||||||||||||||
| updatedAt required | string or null <date-time> When the cart or any of its lines last changed | ||||||||||||||||||||
| expiresAt required | string or null <date-time> When the cart expires: 90 days after its last change. Changes move it out; reads don't. | ||||||||||||||||||||
| doseWarning required | boolean
| ||||||||||||||||||||
{- "administration": {
- "morning": 1,
- "evening": 1
}
}{- "cartId": "crt_2mH3kQ7vX9pL4nR8sT1uWy",
- "revision": 7,
- "daysSupply": 30,
- "items": [
- {
- "productId": "11234",
- "name": "Magnesium Glycinate",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "unitsPerCycle": 1,
- "unitsPerShipment": 60,
- "customerPrice": "0.425",
- "retailPrice": "0.50",
- "maxDailyUnits": 4,
- "protocolTemplateId": null
}
], - "subtotal": "25.50",
- "currency": "usd",
- "updatedAt": "2026-09-23T18:00:00Z",
- "expiresAt": "2026-12-22T18:00:00Z",
- "doseWarning": false
}Remove one line. Removing a line that isn't in the cart changes
nothing: the response is the current cart at its current revision.
A productId that isn't a plain integer of at most 20 digits can't be
in the cart, so it's the same no-op.
Shopper route: public cart host, Authorization: Bearer <cartSecret>.
| cartId required | string^crt_[A-Za-z0-9_-]{22}$ Example: crt_2mH3kQ7vX9pL4nR8sT1uWy The cart's id ( |
| productId required | string Example: 11234 Catalog product ID, written as a plain integer of at most 20 digits with no sign or leading zeros |
| cartId required | string The cart's id, | ||||||||||||||||||||
| revision required | integer >= 0 Increments on every change, including checkout completion removing the purchased lines; | ||||||||||||||||||||
| daysSupply required | integer Enum: 30 60 90 Days of supply per shipment; a checkout from the cart uses it | ||||||||||||||||||||
required | Array of objects (CartLine) The cart's lines, in product ID order | ||||||||||||||||||||
Array
| |||||||||||||||||||||
| subtotal required | string The cart's total for one shipment at your storefront rates, as a decimal string in the same format as | ||||||||||||||||||||
| currency required | string | ||||||||||||||||||||
| updatedAt required | string or null <date-time> When the cart or any of its lines last changed | ||||||||||||||||||||
| expiresAt required | string or null <date-time> When the cart expires: 90 days after its last change. Changes move it out; reads don't. | ||||||||||||||||||||
| doseWarning required | boolean
| ||||||||||||||||||||
{- "cartId": "crt_2mH3kQ7vX9pL4nR8sT1uWy",
- "revision": 7,
- "daysSupply": 30,
- "items": [
- {
- "productId": "11234",
- "name": "Magnesium Glycinate",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "unitsPerCycle": 1,
- "unitsPerShipment": 60,
- "customerPrice": "0.425",
- "retailPrice": "0.50",
- "maxDailyUnits": 4,
- "protocolTemplateId": null
}
], - "subtotal": "25.50",
- "currency": "usd",
- "updatedAt": "2026-09-23T18:00:00Z",
- "expiresAt": "2026-12-22T18:00:00Z",
- "doseWarning": false
}Add every line of one of your ACTIVE protocol templates in one write. Publishing the template to a hosted storefront isn't required; DailyStak library templates can't be added (copy one first).
Each added line records protocolTemplateId. A product already in the
cart is replaced by the template's line. All or nothing: if any
template line can't be bought, nothing is added
(422 TEMPLATE_NOT_PURCHASABLE, the lines in details.lines), and a
dose over a product's cap is 422 DOSE_CAP_EXCEEDED naming
details.productId. The 50-product limit counts the cart's products
and the template's together. A templateId that isn't one of your
ACTIVE templates (including any id over 128 characters), or a
template with no lines, is 404 PROTOCOL_TEMPLATE_NOT_FOUND.
Shopper route: public cart host, Authorization: Bearer <cartSecret>.
| cartId required | string^crt_[A-Za-z0-9_-]{22}$ Example: crt_2mH3kQ7vX9pL4nR8sT1uWy The cart's id ( |
| templateId required | string One of your protocol templates ( |
| cartId required | string The cart's id, | ||||||||||||||||||||
| revision required | integer >= 0 Increments on every change, including checkout completion removing the purchased lines; | ||||||||||||||||||||
| daysSupply required | integer Enum: 30 60 90 Days of supply per shipment; a checkout from the cart uses it | ||||||||||||||||||||
required | Array of objects (CartLine) The cart's lines, in product ID order | ||||||||||||||||||||
Array
| |||||||||||||||||||||
| subtotal required | string The cart's total for one shipment at your storefront rates, as a decimal string in the same format as | ||||||||||||||||||||
| currency required | string | ||||||||||||||||||||
| updatedAt required | string or null <date-time> When the cart or any of its lines last changed | ||||||||||||||||||||
| expiresAt required | string or null <date-time> When the cart expires: 90 days after its last change. Changes move it out; reads don't. | ||||||||||||||||||||
| doseWarning required | boolean
| ||||||||||||||||||||
{- "cartId": "crt_2mH3kQ7vX9pL4nR8sT1uWy",
- "revision": 7,
- "daysSupply": 30,
- "items": [
- {
- "productId": "11234",
- "name": "Magnesium Glycinate",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "unitsPerCycle": 1,
- "unitsPerShipment": 60,
- "customerPrice": "0.425",
- "retailPrice": "0.50",
- "maxDailyUnits": 4,
- "protocolTemplateId": null
}
], - "subtotal": "25.50",
- "currency": "usd",
- "updatedAt": "2026-09-23T18:00:00Z",
- "expiresAt": "2026-12-22T18:00:00Z",
- "doseWarning": false
}Replace the cart's secret and return the new one. The old secret stops
working immediately (401 CART_SECRET_INVALID), so hand the new one
to the shopper's browser or app. Rotating doesn't change the cart,
its revision or its expiry.
Don't rotate the same cart's secret concurrently. The last
rotation wins, and the other caller still gets a 200 whose secret
is already dead.
The response is sent with Cache-Control: no-store.
| cartId required | string^crt_[A-Za-z0-9_-]{22}$ Example: crt_2mH3kQ7vX9pL4nR8sT1uWy The cart's id ( |
| cartId required | string |
| cartSecret required | string The new secret. The previous one no longer works. |
{- "cartId": "crt_2mH3kQ7vX9pL4nR8sT1uWy",
- "cartSecret": "crt_2mH3kQ7vX9pL4nR8sT1uWy.Hn5tB8vC1xZ4mQ7wE2rT9yU6iO3pA0sD5fG8hJ1kL4z"
}Manage customer regimens (protocols) — the customer reference plus their assigned product line items; checkout and subscriptions build on them
Create a protocol: a customer's regimen — the customer reference plus the product line items a practitioner has assigned them. Shipping and billing cadence are not part of a protocol; they are chosen at checkout or on the subscription.
A customer may hold several active protocols. Once a protocol has a live subscription, direct edits are blocked — use the amendment flow.
A protocol can include each product only once: lineItems that list a
product more than once are rejected with 400
(<name> is listed more than once. A protocol can include each product only once.).
Emits the protocol.created webhook event.
Send an Idempotency-Key header to make the call safe to retry;
without one, a timeout retry creates a second active protocol.
| Idempotency-Key | string [ 1 .. 255 ] characters Example: 5f1e8b6c-3c1e-4f5a-9d2b-7a0c4e2f81aa Opaque key that makes this POST safe to retry. A retry carrying the same key and the same body returns the resource the first call created (201, plus |
| customerId required | string ID of an existing customer | ||||||||||||||
required | Array of objects (LineItemRequest) non-empty Products included in the protocol | ||||||||||||||
Array (non-empty)
| |||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null for storefront-created protocols. | ||||||||||||||
| Idempotent-Replayed | string Example: "true"
|
| id required | string Unique protocol identifier | ||||||||||||||||||||||||||||||||||||||||||||||
required | object Customer this protocol belongs to. Fields other than | ||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ProtocolLineItem) Products included in the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "ACTIVE" "INACTIVE" "ARCHIVED" Current status of the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType required | string or null Enum: "SUBSCRIPTION" "ONE_TIME" Intended order type for the protocol. | ||||||||||||||||||||||||||||||||||||||||||||||
| hasExternal required | boolean Whether the regimen includes any external (non-pack) products | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> Protocol creation timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> Protocol last update timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null for storefront-created protocols. | ||||||||||||||||||||||||||||||||||||||||||||||
| inactivatedAt | string or null <date-time> When the protocol last went INACTIVE; null otherwise. | ||||||||||||||||||||||||||||||||||||||||||||||
| archivesAt | string or null <date-time> When the daily job will archive an INACTIVE protocol: updatedAt + 60 days. Any edit moves it. Null unless INACTIVE. | ||||||||||||||||||||||||||||||||||||||||||||||
{- "customerId": "2OKpLhTlpCrQHabz5X9qRbH4abc",
- "lineItems": [
- {
- "productId": 12345,
- "quantity": 2,
- "administration": {
- "morning": 1,
- "evening": 1
}
}
]
}{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Retrieve a paginated list of protocols, or search them. Search matches
customer name, protocol ID, line-item product names, and practitioner
pack labels; pack-label-only matches rank below any product-name
substring match. Search ignores direction, uses its own cursor format
(not interchangeable with the plain list's), and is served from an
asynchronously populated index.
| limit | integer [ 1 .. 100 ] Default: 10 Maximum number of results per page |
| cursor | string Pagination cursor for retrieving next/previous page |
| direction | string Enum: "next" "previous" Direction for pagination (next or previous) |
| search | string Example: search=John Doe Fuzzy search over customer name, protocol ID, product names, and practitioner pack labels; pack-label-only matches rank below any product-name substring match. Cannot be combined with |
| customerId | string Example: customerId=2vTgd30JFgnwLrgb383PcV9Rken Exact match on the customer id, served from the primary store; cannot be combined with |
Array of objects (Protocol) | |||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||
| cursor | string or null (Cursor) Pagination cursor for retrieving next/previous page; null when there are no more pages | ||||||||||||||||||||||||
{- "protocols": [
- {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
], - "cursor": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Retrieve detailed information for a specific protocol
| protocolId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| id required | string Unique protocol identifier | ||||||||||||||||||||||||||||||||||||||||||||||
required | object Customer this protocol belongs to. Fields other than | ||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ProtocolLineItem) Products included in the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "ACTIVE" "INACTIVE" "ARCHIVED" Current status of the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType required | string or null Enum: "SUBSCRIPTION" "ONE_TIME" Intended order type for the protocol. | ||||||||||||||||||||||||||||||||||||||||||||||
| hasExternal required | boolean Whether the regimen includes any external (non-pack) products | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> Protocol creation timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> Protocol last update timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null for storefront-created protocols. | ||||||||||||||||||||||||||||||||||||||||||||||
| inactivatedAt | string or null <date-time> When the protocol last went INACTIVE; null otherwise. | ||||||||||||||||||||||||||||||||||||||||||||||
| archivesAt | string or null <date-time> When the daily job will archive an INACTIVE protocol: updatedAt + 60 days. Any edit moves it. Null unless INACTIVE. | ||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Replace a protocol's line items. Only lineItems can be updated —
the customer link is fixed, and shipping/cadence live on the
subscription. Omitting lineItems is a no-op that returns the current
protocol.
Once the protocol has a live subscription, direct edits are blocked (409) — propose an amendment instead, which routes regimen changes through customer consent.
A protocol can include each product only once: lineItems that list a
product more than once are rejected with 400, naming the product.
Emits the protocol.updated webhook event.
| protocolId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
Array of objects (LineItemRequest) non-empty Products included in the protocol | |||||||||||||||
Array (non-empty)
| |||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null clears it. | ||||||||||||||
| id required | string Unique protocol identifier | ||||||||||||||||||||||||||||||||||||||||||||||
required | object Customer this protocol belongs to. Fields other than | ||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ProtocolLineItem) Products included in the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "ACTIVE" "INACTIVE" "ARCHIVED" Current status of the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType required | string or null Enum: "SUBSCRIPTION" "ONE_TIME" Intended order type for the protocol. | ||||||||||||||||||||||||||||||||||||||||||||||
| hasExternal required | boolean Whether the regimen includes any external (non-pack) products | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> Protocol creation timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> Protocol last update timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null for storefront-created protocols. | ||||||||||||||||||||||||||||||||||||||||||||||
| inactivatedAt | string or null <date-time> When the protocol last went INACTIVE; null otherwise. | ||||||||||||||||||||||||||||||||||||||||||||||
| archivesAt | string or null <date-time> When the daily job will archive an INACTIVE protocol: updatedAt + 60 days. Any edit moves it. Null unless INACTIVE. | ||||||||||||||||||||||||||||||||||||||||||||||
{- "lineItems": [
- {
- "productId": 12345,
- "quantity": 3,
- "administration": {
- "morning": 2,
- "evening": 1
}
}
]
}{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Archive an INACTIVE protocol. Archived is terminal — it cannot be
reactivated. A protocol that is still ACTIVE returns 409: deactivate it
first (POST /v2/protocols/{protocolId}/deactivate), which cancels its
subscriptions, open checkout sessions, and pending changes.
Emits the protocol.archived webhook event with the full protocol.
| protocolId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| id required | string Unique protocol identifier | ||||||||||||||||||||||||||||||||||||||||||||||
required | object Customer this protocol belongs to. Fields other than | ||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ProtocolLineItem) Products included in the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "ACTIVE" "INACTIVE" "ARCHIVED" Current status of the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType required | string or null Enum: "SUBSCRIPTION" "ONE_TIME" Intended order type for the protocol. | ||||||||||||||||||||||||||||||||||||||||||||||
| hasExternal required | boolean Whether the regimen includes any external (non-pack) products | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> Protocol creation timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> Protocol last update timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null for storefront-created protocols. | ||||||||||||||||||||||||||||||||||||||||||||||
| inactivatedAt | string or null <date-time> When the protocol last went INACTIVE; null otherwise. | ||||||||||||||||||||||||||||||||||||||||||||||
| archivesAt | string or null <date-time> When the daily job will archive an INACTIVE protocol: updatedAt + 60 days. Any edit moves it. Null unless INACTIVE. | ||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}ACTIVE → INACTIVE. Cancels every subscription on the protocol that is not
already cancelled (the customer will not be charged again), cancels
pending/viewed checkout sessions, and cancels draft/pending amendments and
open scheduled steps. Orders already placed are untouched. The customer
receives one consolidated notice (gated by the partner's
sendSubscriptionCancelledEmails setting).
Reversible via reactivate. Left untouched, an INACTIVE protocol is
archived automatically after 60 days (archivesAt). Any edit restarts that clock.
Emits protocol.deactivated carrying the protocol plus sweep counts.
| protocolId required | string |
| id required | string Unique protocol identifier | ||||||||||||||||||||||||||||||||||||||||||||||
required | object Customer this protocol belongs to. Fields other than | ||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ProtocolLineItem) Products included in the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "ACTIVE" "INACTIVE" "ARCHIVED" Current status of the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType required | string or null Enum: "SUBSCRIPTION" "ONE_TIME" Intended order type for the protocol. | ||||||||||||||||||||||||||||||||||||||||||||||
| hasExternal required | boolean Whether the regimen includes any external (non-pack) products | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> Protocol creation timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> Protocol last update timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
required | object (DeactivationSweep) What a deactivation cancelled | ||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null for storefront-created protocols. | ||||||||||||||||||||||||||||||||||||||||||||||
| inactivatedAt | string or null <date-time> When the protocol last went INACTIVE; null otherwise. | ||||||||||||||||||||||||||||||||||||||||||||||
| archivesAt | string or null <date-time> When the daily job will archive an INACTIVE protocol: updatedAt + 60 days. Any edit moves it. Null unless INACTIVE. | ||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z",
- "sweep": {
- "subscriptionsCancelled": 0,
- "checkoutSessionsCancelled": 0,
- "amendmentsCancelled": 0,
- "scheduledChangesCancelled": 0
}
}INACTIVE → ACTIVE, status only. No subscription is resumed: create a new
checkout session for the protocol and the customer's purchase creates the
new subscription. Emits protocol.reactivated.
| protocolId required | string |
| id required | string Unique protocol identifier | ||||||||||||||||||||||||||||||||||||||||||||||
required | object Customer this protocol belongs to. Fields other than | ||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
required | Array of objects (ProtocolLineItem) Products included in the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
| status required | string Enum: "ACTIVE" "INACTIVE" "ARCHIVED" Current status of the protocol | ||||||||||||||||||||||||||||||||||||||||||||||
| orderType required | string or null Enum: "SUBSCRIPTION" "ONE_TIME" Intended order type for the protocol. | ||||||||||||||||||||||||||||||||||||||||||||||
| hasExternal required | boolean Whether the regimen includes any external (non-pack) products | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt required | string <date-time> Protocol creation timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt required | string <date-time> Protocol last update timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| name | string or null <= 80 characters Optional practitioner-facing label. Null for storefront-created protocols. | ||||||||||||||||||||||||||||||||||||||||||||||
| inactivatedAt | string or null <date-time> When the protocol last went INACTIVE; null otherwise. | ||||||||||||||||||||||||||||||||||||||||||||||
| archivesAt | string or null <date-time> When the daily job will archive an INACTIVE protocol: updatedAt + 60 days. Any edit moves it. Null unless INACTIVE. | ||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Dry-run the cost impact of a proposed set of regimen edits without persisting anything. The edits are folded onto an in-memory copy of the protocol's active regimen and re-priced, returning current vs. proposed totals and partner commission, plus a per-line breakdown.
Scope is the regimen subtotal and commission only — promo, fees,
shipping, and tax are applied at order time and are excluded here. An
empty changes list yields a no-op preview (proposed == current).
| protocolId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| daysSupply | integer or null [ 1 .. 365 ] Read-only supply estimate; defaults to the live subscription, protocol, then 30 days. | ||||||
Array of objects (AmendmentChange) <= 99 items Default: [] | |||||||
Array (<= 99 items)
| |||||||
| currentTotal required | string Customer-facing subtotal of the current active regimen | ||||||||||||||||||||||||||||||||||||||||||||||
| proposedTotal required | string Customer-facing subtotal of the proposed active regimen | ||||||||||||||||||||||||||||||||||||||||||||||
| priceChange required | string proposedTotal - currentTotal (signed) | ||||||||||||||||||||||||||||||||||||||||||||||
| commissionCurrent required | string Partner earnings on the current regimen | ||||||||||||||||||||||||||||||||||||||||||||||
| commissionProposed required | string Partner earnings on the proposed regimen | ||||||||||||||||||||||||||||||||||||||||||||||
| commissionChange required | string commissionProposed - commissionCurrent (signed) | ||||||||||||||||||||||||||||||||||||||||||||||
object or null Practitioner view of a quote or receipt; never send to patients. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
object or null Practitioner view of a quote or receipt; never send to patients. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
Array of objects (AmendmentCostPreviewLine) Per-line breakdown of the proposed active regimen | |||||||||||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||||||||||
{- "daysSupply": 1,
- "changes": [ ]
}{- "currentPricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "proposedPricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "currentTotal": "120.00",
- "proposedTotal": "150.00",
- "priceChange": "30.00",
- "commissionCurrent": "30.00",
- "commissionProposed": "38.00",
- "commissionChange": "8.00",
- "proposedLines": [
- {
- "productId": 0,
- "quantity": 0,
- "lineSubtotal": "45.00",
- "lineCommission": "12.00"
}
]
}Retrieve a paginated list of available products, or search the catalog.
Search is fuzzy over product name, search terms, description, and
ingredients, and returns ranked results with totalHits. In search
mode the direction, updated_after, and updated_before parameters
are ignored. Note the time-range filters use snake_case parameter
names.
Products this partner has disabled are excluded unless
include_disabled=true; every item carries enabled.
Filters: form and categories match ANY listed value; certifications and allergens (free-from claims) match ALL. Parameters AND together, and with status and the updated range. Unknown values are ignored. A filtered page holds limit products unless the catalog runs out, and cursor is returned only when more products match. In search mode the filters apply inside the
search.
| limit | integer [ 1 .. 100 ] Default: 10 Maximum number of results per page |
| cursor | string Pagination cursor for retrieving next/previous page |
| direction | string Enum: "next" "previous" Direction for pagination (next or previous) |
| status | string Default: "ACTIVE" Enum: "ACTIVE" "INACTIVE" "DRAFT" "OUT_OF_STOCK" "DISCONTINUED" Filter products by status (default ACTIVE) |
| form | string Example: form=Capsule,Veg Capsule Comma-separated product forms; returns products in ANY of them. A value is the stored form (Capsule, Quick-Dissolve Tablet, Powder, Liquid, Serum, Softgel, Tablet, Veg Capsule) or its name (CAPSULE, QUICK_DISSOLVE_TABLET, POWDER, LIQUID, SERUM, SOFTGEL, TABLET, VEG_CAPSULE), in any case. Unknown values are ignored. |
| certifications | string Example: certifications=vegan,kosher Comma-separated certifications; returns products carrying ALL of them. Values: certified_gmp, organic_certified, halal, kosher, lab_tested, manufactured_in_usa, non_gmo, sustainably_sourced, vegan, vegetarian, or their names (CERTIFIED_ORGANIC means organic_certified), in any case. Unknown values are ignored. |
| allergens | string Example: allergens=gluten_free,dairy_free Comma-separated free-from claims; returns products carrying ALL of them, so gluten_free,dairy_free means both labels are present (a must-match, not an exclusion). Values: animal_free, corn_free, dairy_free, egg_free, fish_free, gluten_free, hexane_free, nut_free, peanut_free, sesame_free, shellfish_free, soy_free, treenut_free, wheat_free, yeast_free, or their names (TREE_NUT_FREE means treenut_free), in any case. Unknown values are ignored. |
| categories | string Example: categories=Heart health,Vitamins Comma-separated shop categories; returns products in ANY of them. Values: Heart health, Metabolic health, Immune support, Inflammation & antioxidants, Brain & mood, Stress & sleep, Gut health, Liver & detox, Energy & performance, Healthy aging, Bones & joints, Hair, skin & nails, Hormones & thyroid, Women's health, Men's health, Eye health, Vitamins, Minerals, Herbs, or their names (HAIR_SKIN_AND_NAILS), in any case. "Hair, skin & nails" contains a comma and is still read as one value; URL-encode the "&". Unknown values are ignored. |
| updatedAfter | string Example: updatedAfter=2026-01-01T00:00:00Z Only products updated at or after this ISO 8601 timestamp (snake_case parameter name) |
| updatedBefore | string Example: updatedBefore=2026-06-30T23:59:59Z Only products updated at or before this ISO 8601 timestamp (snake_case parameter name) |
| search | string Example: search=omega Fuzzy search products by name, ingredients, description, or search terms |
| customerId | string Example: customerId=2QKRPxjjaxbGjOPF8TfCQb7YOAZ Optional customer ID for customer-specific pricing. When provided, uses the customer's discount override (if set) instead of the partner's default discount. |
| includeDisabled | boolean Default: false Include products this partner has disabled (snake_case parameter name). Disabled products are excluded from this listing by default. Filtering is applied after pagination, so a page containing disabled products returns fewer than |
Array of objects (Product) | |||||||||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||||||||
| cursor | string or null (Cursor) Pagination cursor for retrieving next/previous page; null when there are no more pages | ||||||||||||||||||||||||||||||||||||||
| totalHits | integer or null Total number of results (populated only in search mode).
This is the pre-filter search total and may exceed the
number of items returned once products the partner has
disabled are excluded; use | ||||||||||||||||||||||||||||||||||||||
{- "products": [
- {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": 77123129891,
- "name": "Elite EPA",
- "description": "A comprehensive wellness supplement",
- "packDescription": "Whole Body Wellness",
- "pricing": {
- "retailPrice": "0.53",
- "partnerPrice": "0.32",
- "customerPrice": "0.38",
- "customerDiscount": 20,
- "partnerDiscount": 40,
- "commission": "0.06",
- "commissionRate": 16
}, - "external": false,
- "recommendedUsage": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "price": "0.53",
- "form": "Softgel",
- "certifications": [
- "kosher",
- "halal",
- "non_gmo",
- "vegan"
], - "allergens": [
- "gluten_free",
- "dairy_free",
- "soy_free"
], - "categories": [
- "Heart health",
- "Stress & sleep"
], - "supplementFactsPanel": {
- "servingSize": 2,
- "pillType": "Softgel",
- "otherIngredients": [
- "Fish oil",
- "Gelatin",
- "Glycerin"
], - "ingredientInformation": [
- {
- "name": "Fish oil",
- "value": "100.1",
- "unit": "mg",
- "dailyValue": "23",
- "ingredientFamily": "magnesium",
- "amountMcg": "string",
- "amountMcgPerUnit": "string",
- "children": [
- { }
]
}
], - "nutritionInformation": [
- {
- "name": "Carbohydrates",
- "value": "10",
- "unit": "g",
- "dailyValue": "10"
}
], - "servingsPerContainer": 30,
- "caution": "Consult your healthcare provider before use if pregnant or nursing."
}, - "status": "ACTIVE",
- "enabled": true,
- "createdAt": "2023-10-01T17:02:56.866619Z",
- "updatedAt": "2023-10-01T17:02:56.866619Z"
}
], - "cursor": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
- "totalHits": 0
}Retrieve detailed information for a specific product.
A product this partner has disabled is still returned with HTTP 200 and
enabled: false — disabling never turns this endpoint into a 404, so
historical order lines keep rendering. The 404 below means the product
id does not exist.
| productId required | integer Example: 77123129891 |
| customerId | string Example: customerId=2QKRPxjjaxbGjOPF8TfCQb7YOAZ Optional customer ID for customer-specific pricing. When provided, uses the customer's discount override (if set) instead of the partner's default discount. |
object or null Server-produced pricing; use this instead of reconstructing money from unit prices or rates. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| id | integer Unique product identifier | ||||||||||||||||||||||||||||||||||||||||||||||
| name | string Product name | ||||||||||||||||||||||||||||||||||||||||||||||
| description | string Detailed product description | ||||||||||||||||||||||||||||||||||||||||||||||
| packDescription | string Description of the product packaging | ||||||||||||||||||||||||||||||||||||||||||||||
object (PricingInfo) Price breakdown showing partner and customer pricing. Pricing Formula:
The customer discount comes from the partner's default discount setting, or can be overridden per-customer via customer.discountOverride. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| external | boolean If the product is external to the pill packs | ||||||||||||||||||||||||||||||||||||||||||||||
| image | string <uri> URL to the product image | ||||||||||||||||||||||||||||||||||||||||||||||
object (Administration) Dosage schedule information specifying how many pills to take at each time of day. Where dosing is validated (order and checkout line items), the sum of morning + afternoon + evening + sleep must equal the line item quantity. Omitted keys default to 0 in requests; responses always include all four keys. | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| price | string or null Retail (MSRP) unit price — the same value as | ||||||||||||||||||||||||||||||||||||||||||||||
| form | string Enum: "Capsule" "Veg Capsule" "Tablet" "Quick-Dissolve Tablet" "Softgel" "Powder" "Liquid" "Serum" Physical form of the supplement (display-cased) | ||||||||||||||||||||||||||||||||||||||||||||||
| certifications | Array of strings Product certifications | ||||||||||||||||||||||||||||||||||||||||||||||
| allergens | Array of strings Allergy related information | ||||||||||||||||||||||||||||||||||||||||||||||
| categories | Array of strings The shop categories the product belongs to, staff-assigned from a fixed list of nineteen (initially backfilled from its Shopify collections). Empty when none are set. | ||||||||||||||||||||||||||||||||||||||||||||||
object (SupplementFactsPanel) Detailed nutritional and ingredient information | |||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||
| status | string Enum: "ACTIVE" "INACTIVE" "DRAFT" "OUT_OF_STOCK" "DISCONTINUED" Current product availability status | ||||||||||||||||||||||||||||||||||||||||||||||
| enabled | boolean Default: true False when the calling partner has disabled this product. Disabled products are omitted from GET /v2/products unless | ||||||||||||||||||||||||||||||||||||||||||||||
| createdAt | string <date-time> Product creation timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
| updatedAt | string <date-time> Product last update timestamp | ||||||||||||||||||||||||||||||||||||||||||||||
{- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": 77123129891,
- "name": "Elite EPA",
- "description": "A comprehensive wellness supplement",
- "packDescription": "Whole Body Wellness",
- "pricing": {
- "retailPrice": "0.53",
- "partnerPrice": "0.32",
- "customerPrice": "0.38",
- "customerDiscount": 20,
- "partnerDiscount": 40,
- "commission": "0.06",
- "commissionRate": 16
}, - "external": false,
- "recommendedUsage": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "price": "0.53",
- "form": "Softgel",
- "certifications": [
- "kosher",
- "halal",
- "non_gmo",
- "vegan"
], - "allergens": [
- "gluten_free",
- "dairy_free",
- "soy_free"
], - "categories": [
- "Heart health",
- "Stress & sleep"
], - "supplementFactsPanel": {
- "servingSize": 2,
- "pillType": "Softgel",
- "otherIngredients": [
- "Fish oil",
- "Gelatin",
- "Glycerin"
], - "ingredientInformation": [
- {
- "name": "Fish oil",
- "value": "100.1",
- "unit": "mg",
- "dailyValue": "23",
- "ingredientFamily": "magnesium",
- "amountMcg": "string",
- "amountMcgPerUnit": "string",
- "children": [
- { }
]
}
], - "nutritionInformation": [
- {
- "name": "Carbohydrates",
- "value": "10",
- "unit": "g",
- "dailyValue": "10"
}
], - "servingsPerContainer": 30,
- "caution": "Consult your healthcare provider before use if pregnant or nursing."
}, - "status": "ACTIVE",
- "enabled": true,
- "createdAt": "2023-10-01T17:02:56.866619Z",
- "updatedAt": "2023-10-01T17:02:56.866619Z"
}Fetch the product's rendered label image (PNG). Responds with a 302
redirect to a presigned URL valid for 15 minutes — follow the
redirect (or read the Location header) to download the PNG. Never
cache or embed the presigned URL; re-request when needed.
The image has a transparent background and an even margin on every
side, so it sits on your own page. Pick the text color that contrasts
with that page: black (the default) for light backgrounds, white
for dark ones.
A 404 means the label has not been rendered for this product in the requested text color.
| productId required | integer |
| textColor | string Default: "black" Enum: "black" "white" Text color of the label image. Case-insensitive. |
{- "message": "One or more requested products were not found",
- "code": "INVALID_TEXT_COLOR",
- "details": {
- "missingProductIds": [
- 1001
]
}
}Returns AI-powered product recommendations based on a natural language query. The query can describe conditions, symptoms, ingredients, or similar products.
Examples of queries:
Matches come back ranked with confidence scores and generated
reasoning. Results are non-deterministic — the same query can score
differently between calls. At most 5 candidate products are considered
per query regardless of limit. An empty matches array is returned
both when nothing matches and when the recommendation backend is
degraded.
Products this partner has disabled are excluded from the candidate set
unless includeDisabled is true — a query whose only matches are
disabled returns the same empty matches as a query with no matches.
Although this is a POST, it is a read-only operation: it requires
only order/v2.product.read, with no write scope.
| query required | string [ 1 .. 1000 ] characters Natural language query describing desired products, conditions, ingredients, or similar products |
| limit | integer [ 1 .. 20 ] Default: 5 Maximum number of recommendations to return. At most 5 candidates are considered per query, so values above 5 cannot return more than 5. |
| minConfidence | number <float> [ 0 .. 1 ] Default: 0.6 Minimum confidence score threshold (0.0 to 1.0). Not applied when the top candidate is high-confidence enough to skip ranking. |
| includeDisabled | boolean Default: false Include products this partner has disabled. Disabled products are never recommended by default. camelCase here because it is a request-body field; the equivalent listing query parameter is |
| query required | string The original search query | ||||||||
required | Array of objects (ProductMatch) List of recommended products ranked by relevance | ||||||||
Array
| |||||||||
| predictivePrompts required | Array of strings <= 2 items Complementary product suggestions based on health correlations | ||||||||
| message | string or null Human-readable summary of the result | ||||||||
| clarifications | Array of strings or null Follow-up questions; present only when | ||||||||
{- "query": "supplements for joint health",
- "limit": 5,
- "minConfidence": 0.7
}{- "query": "supplements for joint health",
- "matches": [
- {
- "productId": 77123129891,
- "productName": "Joint Support Formula",
- "score": 0.92,
- "reasoning": "Glucosamine and chondroitin support joint health."
}
], - "message": "Found 1 products matching your query.",
- "clarifications": null,
- "predictivePrompts": [
- "Turmeric for inflammation support",
- "Collagen for connective tissue health"
]
}Static classification registry for the ingredientFamily slugs that
appear on product supplement-facts rows. Each family carries a display
name and a summable flag saying whether member amounts
(amountMcg) can be meaningfully summed across products for regimen
nutrient roll-ups — treat any family absent from this list, or with
summable: false, as not summable.
The content is constant per deploy: responses carry
Cache-Control: public, max-age=3600, and version changes only when
the registry changes — use it as a cache key.
| version required | string Content hash of the registry; changes only when the registry changes | ||||||||||
required | Array of objects All known ingredient families, sorted by slug | ||||||||||
Array
| |||||||||||
{- "version": "9c47e1b02a3d",
- "families": [
- {
- "family": "magnesium",
- "displayName": "Magnesium",
- "summable": true,
- "hazard": "extract-vs-whole-herb",
- "note": "string"
}
]
}Retrieve list of available shipping methods with details
required | Array of objects (ShippingMethod) Your active shipping methods | ||||||||||||||
Array
| |||||||||||||||
{- "shippingMethods": [
- {
- "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "fulfillmentMethod": "STANDARD",
- "name": "STANDARD",
- "description": "5-8 business days",
- "price": "9.99",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
]
}Retrieve the authenticated partner's current settings including branding, contact info, and billing address
| id | string Unique partner identifier (read-only) |
| name | string Partner name (read-only) |
| description | string or null Deprecated Always null in v2 |
| discount | string Human readable discount representation |
| discountValue | number Numeric discount multiplier |
| primaryColor | string Primary brand color (hex code) |
| secondaryColor | string Secondary brand color (hex code) |
| logo | string or null Deprecated Write-only legacy field; always null in responses — use logoPrimary/logoIcon |
| logoPrimary | string or null Primary logo (storage key) |
| logoIcon | string or null Icon logo (storage key) |
| supportPhone | string or null Support phone number |
| supportEmail | string <email> Support email address |
| website | string <uri> Partner website URL |
| webhookUrl | string or null <uri> Legacy single webhook URL for order status notifications. Prefer managed webhook endpoints (portal) with per-endpoint secrets. |
| elementsCheckoutEnabled | boolean Whether partner-hosted (Elements) checkout is enabled for this partner. Set by DailyStak; read-only here. |
| billingAddressLine1 | string or null Billing address line 1 |
| billingAddressLine2 | string or null Billing address line 2 |
| billingCity | string or null Billing city |
| billingState | string or null Billing state/province |
| billingPostalCode | string or null Billing postal/zip code |
| billingCountry | string or null Billing country |
| createdAt | string <date-time> Partner creation timestamp |
| updatedAt | string <date-time> Partner last update timestamp |
{- "id": "2NxK7vPqYtR5mJnLkH3wZcB",
- "name": "Acme Health",
- "description": "string",
- "discount": "10%",
- "discountValue": 0.1,
- "primaryColor": "#1a73e8",
- "secondaryColor": "#ffffff",
- "logo": "string",
- "logoPrimary": "string",
- "logoIcon": "string",
- "supportPhone": "555-123-4567",
- "supportEmail": "support@partner.com",
- "elementsCheckoutEnabled": false,
- "billingAddressLine1": "123 Main St",
- "billingAddressLine2": "Suite 100",
- "billingCity": "San Francisco",
- "billingState": "CA",
- "billingPostalCode": "94102",
- "billingCountry": "US",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Update the authenticated partner's settings. All fields are optional — only provided fields are updated, and null values are ignored (fields cannot be cleared to null; send an empty string to overwrite). Partners cannot change their name or ID.
| primaryColor | string Primary brand color (hex code) |
| secondaryColor | string Secondary brand color (hex code) |
| logoPrimary | string Primary logo (storage key) |
| logoIcon | string Icon logo (storage key) |
| supportPhone | string Support phone number |
| supportEmail | string <email> Support email address |
| website | string <uri> Partner website URL |
| webhookUrl | string <uri> Webhook URL for order status notifications. Validated when set: must be |
| billingAddressLine1 | string Billing address line 1 |
| billingAddressLine2 | string Billing address line 2 |
| billingCity | string Billing city |
| billingState | string Billing state/province |
| billingPostalCode | string Billing postal/zip code |
| billingCountry | string Billing country |
| id | string Unique partner identifier (read-only) |
| name | string Partner name (read-only) |
| description | string or null Deprecated Always null in v2 |
| discount | string Human readable discount representation |
| discountValue | number Numeric discount multiplier |
| primaryColor | string Primary brand color (hex code) |
| secondaryColor | string Secondary brand color (hex code) |
| logo | string or null Deprecated Write-only legacy field; always null in responses — use logoPrimary/logoIcon |
| logoPrimary | string or null Primary logo (storage key) |
| logoIcon | string or null Icon logo (storage key) |
| supportPhone | string or null Support phone number |
| supportEmail | string <email> Support email address |
| website | string <uri> Partner website URL |
| webhookUrl | string or null <uri> Legacy single webhook URL for order status notifications. Prefer managed webhook endpoints (portal) with per-endpoint secrets. |
| elementsCheckoutEnabled | boolean Whether partner-hosted (Elements) checkout is enabled for this partner. Set by DailyStak; read-only here. |
| billingAddressLine1 | string or null Billing address line 1 |
| billingAddressLine2 | string or null Billing address line 2 |
| billingCity | string or null Billing city |
| billingState | string or null Billing state/province |
| billingPostalCode | string or null Billing postal/zip code |
| billingCountry | string or null Billing country |
| createdAt | string <date-time> Partner creation timestamp |
| updatedAt | string <date-time> Partner last update timestamp |
{- "primaryColor": "#1a73e8",
- "secondaryColor": "#ffffff",
- "logoPrimary": "string",
- "logoIcon": "string",
- "supportPhone": "555-123-4567",
- "supportEmail": "support@partner.com",
- "billingAddressLine1": "123 Main St",
- "billingAddressLine2": "Suite 100",
- "billingCity": "San Francisco",
- "billingState": "CA",
- "billingPostalCode": "94102",
- "billingCountry": "US"
}{- "id": "2NxK7vPqYtR5mJnLkH3wZcB",
- "name": "Acme Health",
- "description": "string",
- "discount": "10%",
- "discountValue": 0.1,
- "primaryColor": "#1a73e8",
- "secondaryColor": "#ffffff",
- "logo": "string",
- "logoPrimary": "string",
- "logoIcon": "string",
- "supportPhone": "555-123-4567",
- "supportEmail": "support@partner.com",
- "elementsCheckoutEnabled": false,
- "billingAddressLine1": "123 Main St",
- "billingAddressLine2": "Suite 100",
- "billingCity": "San Francisco",
- "billingState": "CA",
- "billingPostalCode": "94102",
- "billingCountry": "US",
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}Retrieve the webhook secret used to sign deliveries to your legacy
webhookUrl (the X-Juniper-Signature header). The secret is not
included in the general settings response for security. You must have a
webhookUrl configured to have a secret.
Managed webhook endpoints (created in the portal) each have their own per-endpoint secret shown at creation — this secret does not apply to them.
| webhookSecret required | string The webhook secret |
{- "webhookSecret": "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e"
}Generate a new webhook secret. The old secret becomes invalid immediately. Use this endpoint to rotate your secret if it may have been compromised.
| webhookSecret required | string The webhook secret |
{- "webhookSecret": "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e"
}Register a custom domain for branded checkout pages (e.g., checkout.yourdomain.com).
Returns the DNS records that must be added to your domain registrar in
dnsRecords.records. How many there are depends on the domain's shape:
a subdomain needs one, a root domain needs three (see
CustomDomainDnsRecords).
| domainName required | string [ 4 .. 253 ] characters Custom domain name (e.g., checkout.yourdomain.com) |
| id | string Unique domain identifier | ||||||||||||||||||||||||||||||||||
| domainName | string The custom domain name | ||||||||||||||||||||||||||||||||||
| status | string Enum: "PENDING" "ACTIVE" "FAILED" Current domain status | ||||||||||||||||||||||||||||||||||
object or null DNS records to add. Returned on registration and GET responses at any status; absent from the verify response. Read | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
| validatedAt | string or null <date-time> When the domain was validated; null until then | ||||||||||||||||||||||||||||||||||
| createdAt | string <date-time> When the domain was registered | ||||||||||||||||||||||||||||||||||
{- "domainName": "checkout.acme.com"
}{- "id": "2vGxyz...",
- "domainName": "checkout.acme.com",
- "status": "ACTIVE",
- "dnsRecords": {
- "records": [
- {
- "type": "A",
- "name": "acme.com",
- "zone": "acme.com",
- "values": [
- "75.2.1.1",
- "99.83.1.1"
], - "purpose": "apex-redirect"
}
], - "validation": {
- "type": "CNAME",
- "name": "_abc123.checkout.acme.com",
- "value": "_xyz.acm-validations.aws."
}, - "routing": {
- "type": "CNAME",
- "name": "_abc123.checkout.acme.com",
- "value": "_xyz.acm-validations.aws."
}
}, - "validatedAt": "2026-01-25T06:26:06Z",
- "createdAt": "2026-01-25T06:17:47Z"
}Retrieve the current custom domain configuration including status and DNS records.
| id | string Unique domain identifier | ||||||||||||||||||||||||||||||||||
| domainName | string The custom domain name | ||||||||||||||||||||||||||||||||||
| status | string Enum: "PENDING" "ACTIVE" "FAILED" Current domain status | ||||||||||||||||||||||||||||||||||
object or null DNS records to add. Returned on registration and GET responses at any status; absent from the verify response. Read | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
| validatedAt | string or null <date-time> When the domain was validated; null until then | ||||||||||||||||||||||||||||||||||
| createdAt | string <date-time> When the domain was registered | ||||||||||||||||||||||||||||||||||
{- "id": "2vGxyz...",
- "domainName": "checkout.acme.com",
- "status": "ACTIVE",
- "dnsRecords": {
- "records": [
- {
- "type": "A",
- "name": "acme.com",
- "zone": "acme.com",
- "values": [
- "75.2.1.1",
- "99.83.1.1"
], - "purpose": "apex-redirect"
}
], - "validation": {
- "type": "CNAME",
- "name": "_abc123.checkout.acme.com",
- "value": "_xyz.acm-validations.aws."
}, - "routing": {
- "type": "CNAME",
- "name": "_abc123.checkout.acme.com",
- "value": "_xyz.acm-validations.aws."
}
}, - "validatedAt": "2026-01-25T06:26:06Z",
- "createdAt": "2026-01-25T06:17:47Z"
}Remove the custom domain configuration. This will:
{- "message": "UNAUTHORIZED",
- "code": "UNAUTHORIZED"
}Verify the custom domain after creating every record in
dnsRecords.records — one for a subdomain, three for a root domain.
Verification checks the certificate only — a domain can reach
ACTIVE before the storefront or apex-redirect records are in
place, so confirm your storefront loads on the domain after activation
(for a root domain, check both the root and www). If still
PENDING, call again after a few minutes (DNS propagation can take up
to an hour). A FAILED status is terminal: delete the domain and
register it again.
| id | string Unique domain identifier | ||||||||||||||||||||||||||||||||||
| domainName | string The custom domain name | ||||||||||||||||||||||||||||||||||
| status | string Enum: "PENDING" "ACTIVE" "FAILED" Current domain status | ||||||||||||||||||||||||||||||||||
object or null DNS records to add. Returned on registration and GET responses at any status; absent from the verify response. Read | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
| validatedAt | string or null <date-time> When the domain was validated; null until then | ||||||||||||||||||||||||||||||||||
| createdAt | string <date-time> When the domain was registered | ||||||||||||||||||||||||||||||||||
{- "id": "2vGxyz...",
- "domainName": "checkout.acme.com",
- "status": "ACTIVE",
- "dnsRecords": {
- "records": [
- {
- "type": "A",
- "name": "acme.com",
- "zone": "acme.com",
- "values": [
- "75.2.1.1",
- "99.83.1.1"
], - "purpose": "apex-redirect"
}
], - "validation": {
- "type": "CNAME",
- "name": "_abc123.checkout.acme.com",
- "value": "_xyz.acm-validations.aws."
}, - "routing": {
- "type": "CNAME",
- "name": "_abc123.checkout.acme.com",
- "value": "_xyz.acm-validations.aws."
}
}, - "validatedAt": "2026-01-25T06:26:06Z",
- "createdAt": "2026-01-25T06:17:47Z"
}Propose, review, approve, and cancel batched protocol regimen changes (amendments), with a customer-consent gate for protocols that have a live subscription
List all amendments on a protocol, optionally filtered by status.
The list is unpaginated and eventually consistent — an amendment created moments earlier may briefly be absent. Fetching a single amendment by ID is the read-your-write path.
| protocolId required | string Example: 2vTgd30JFgnwLrgb383PcV9Rken |
| status | string Enum: "draft" "pending" "approved" "rejected" "cancelled" "expired" Filter by amendment status (lowercase). Invalid values return 400. |
required | Array of objects (Amendment) | ||||||||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||||||||
{- "amendments": [
- {
- "id": "2xAmdK30JFgnwLrgb383PcV9Rk",
- "protocolId": "string",
- "status": "pending",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
], - "authoredBy": "string",
- "authoredAgainstRevision": 0,
- "offlineApprovalAttested": true,
- "attestedBy": "string",
- "attestedAt": "2019-08-24T14:15:22Z",
- "approvedBy": "customer",
- "approvedAt": "2019-08-24T14:15:22Z",
- "rejectedBy": "customer",
- "rejectedAt": "2019-08-24T14:15:22Z",
- "cancellationReason": "superseded",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
]
}Author a draft amendment: a batched set of regimen edits (add / remove / update) against the protocol. Nothing is applied to the protocol at this stage, and change payloads are not yet validated in depth — full validation happens when the amendment is sent for approval, so payload mistakes surface at send time (400), not at authoring.
Drafts do not lock regimen lines and never expire. A draft becomes actionable by calling the send endpoint, which applies the customer-consent gate.
There is no idempotency key: posting the same body twice creates two drafts.
| protocolId required | string |
required | Array of objects (AmendmentChange) [ 1 .. 99 ] items The regimen edits to propose (validated in depth at send time) | ||||||
Array ([ 1 .. 99 ] items)
| |||||||
| id required | string Unique amendment identifier | ||||||
| protocolId required | string Protocol this amendment applies to | ||||||
| status required | string Enum: "draft" "pending" "approved" "rejected" "cancelled" "expired" Current lifecycle status (lowercase) | ||||||
required | Array of objects (AmendmentChange) The proposed regimen edits | ||||||
Array
| |||||||
| authoredBy required | string The authoring API client ID, or | ||||||
| offlineApprovalAttested required | boolean Whether the practitioner attested customer approval was obtained offline | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| authoredAgainstRevision | integer or null Protocol revision captured when the amendment was sent; null for drafts. Approval refuses with 409 if the protocol has moved past this revision. | ||||||
| attestedBy | string or null Who attested offline approval | ||||||
| attestedAt | string or null <date-time> | ||||||
| approvedBy | string or null Enum: "customer" "partner" Approving role (not an actor ID) | ||||||
| approvedAt | string or null <date-time> | ||||||
| rejectedBy | string or null Enum: "customer" "partner" | ||||||
| rejectedAt | string or null <date-time> | ||||||
| cancellationReason | string or null Value: "superseded" Set when the platform cancelled the amendment. | ||||||
{- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
]
}{- "id": "2xAmdK30JFgnwLrgb383PcV9Rk",
- "protocolId": "string",
- "status": "pending",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
], - "authoredBy": "string",
- "authoredAgainstRevision": 0,
- "offlineApprovalAttested": true,
- "attestedBy": "string",
- "attestedAt": "2019-08-24T14:15:22Z",
- "approvedBy": "customer",
- "approvedAt": "2019-08-24T14:15:22Z",
- "rejectedBy": "customer",
- "rejectedAt": "2019-08-24T14:15:22Z",
- "cancellationReason": "superseded",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Fetch a single amendment. An amendment that exists but belongs to a different protocol (or a different partner) returns the same 404 as a missing ID.
| protocolId required | string |
| amendmentId required | string |
| id required | string Unique amendment identifier | ||||||
| protocolId required | string Protocol this amendment applies to | ||||||
| status required | string Enum: "draft" "pending" "approved" "rejected" "cancelled" "expired" Current lifecycle status (lowercase) | ||||||
required | Array of objects (AmendmentChange) The proposed regimen edits | ||||||
Array
| |||||||
| authoredBy required | string The authoring API client ID, or | ||||||
| offlineApprovalAttested required | boolean Whether the practitioner attested customer approval was obtained offline | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| authoredAgainstRevision | integer or null Protocol revision captured when the amendment was sent; null for drafts. Approval refuses with 409 if the protocol has moved past this revision. | ||||||
| attestedBy | string or null Who attested offline approval | ||||||
| attestedAt | string or null <date-time> | ||||||
| approvedBy | string or null Enum: "customer" "partner" Approving role (not an actor ID) | ||||||
| approvedAt | string or null <date-time> | ||||||
| rejectedBy | string or null Enum: "customer" "partner" | ||||||
| rejectedAt | string or null <date-time> | ||||||
| cancellationReason | string or null Value: "superseded" Set when the platform cancelled the amendment. | ||||||
{- "id": "2xAmdK30JFgnwLrgb383PcV9Rk",
- "protocolId": "string",
- "status": "pending",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
], - "authoredBy": "string",
- "authoredAgainstRevision": 0,
- "offlineApprovalAttested": true,
- "attestedBy": "string",
- "attestedAt": "2019-08-24T14:15:22Z",
- "approvedBy": "customer",
- "approvedAt": "2019-08-24T14:15:22Z",
- "rejectedBy": "customer",
- "rejectedAt": "2019-08-24T14:15:22Z",
- "cancellationReason": "superseded",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Cancel a draft or pending amendment. The protocol is never mutated. Resolved amendments (approved / rejected / cancelled) cannot be cancelled and return 400.
Emits the protocol_change_request.cancelled webhook event.
| protocolId required | string |
| amendmentId required | string |
| id required | string Unique amendment identifier | ||||||
| protocolId required | string Protocol this amendment applies to | ||||||
| status required | string Enum: "draft" "pending" "approved" "rejected" "cancelled" "expired" Current lifecycle status (lowercase) | ||||||
required | Array of objects (AmendmentChange) The proposed regimen edits | ||||||
Array
| |||||||
| authoredBy required | string The authoring API client ID, or | ||||||
| offlineApprovalAttested required | boolean Whether the practitioner attested customer approval was obtained offline | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| authoredAgainstRevision | integer or null Protocol revision captured when the amendment was sent; null for drafts. Approval refuses with 409 if the protocol has moved past this revision. | ||||||
| attestedBy | string or null Who attested offline approval | ||||||
| attestedAt | string or null <date-time> | ||||||
| approvedBy | string or null Enum: "customer" "partner" Approving role (not an actor ID) | ||||||
| approvedAt | string or null <date-time> | ||||||
| rejectedBy | string or null Enum: "customer" "partner" | ||||||
| rejectedAt | string or null <date-time> | ||||||
| cancellationReason | string or null Value: "superseded" Set when the platform cancelled the amendment. | ||||||
{- "id": "2xAmdK30JFgnwLrgb383PcV9Rk",
- "protocolId": "string",
- "status": "pending",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
], - "authoredBy": "string",
- "authoredAgainstRevision": 0,
- "offlineApprovalAttested": true,
- "attestedBy": "string",
- "attestedAt": "2019-08-24T14:15:22Z",
- "approvedBy": "customer",
- "approvedAt": "2019-08-24T14:15:22Z",
- "rejectedBy": "customer",
- "rejectedAt": "2019-08-24T14:15:22Z",
- "cancellationReason": "superseded",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Send a draft amendment for approval. Every change is first validation-folded against the live protocol as a dry run — an invalid payload fails here with 400 before any state changes, so a pending amendment can never be stranded un-approvable.
The customer-consent gate then decides the outcome. Integrators must
branch on the returned status:
pending — the protocol has a live subscription and offline
approval was not attested. The amendment is held for customer approval:
a single-use magic-link review page is minted and a branded approval
email is sent to the customer (best effort). Use the approval-link
endpoint to retrieve the URL and deliver it through your own channel.approved — there is no live subscription, or attestedOffline
was set. The changes are folded onto the protocol immediately in this
same request.The protocol revision at send time is captured as
authoredAgainstRevision and re-checked at approval.
Pending amendments held for customer approval are automatically cancelled after 45 days of inaction (a reminder email is sent to the customer after 7 days).
Emits protocol_change_request.created (and additionally
protocol_change_request.approved when auto-approved).
| protocolId required | string |
| amendmentId required | string |
Send {} when not attesting offline approval.
| attestedOffline | boolean Default: false Practitioner attests the customer's approval was already obtained offline (e.g. during an appointment). Skips the customer-consent hold; the attestation is recorded on the amendment for audit. |
| attestedBy | string or null Who attested the offline approval. Defaults to the calling API client ID when omitted. |
| id required | string Unique amendment identifier | ||||||
| protocolId required | string Protocol this amendment applies to | ||||||
| status required | string Enum: "draft" "pending" "approved" "rejected" "cancelled" "expired" Current lifecycle status (lowercase) | ||||||
required | Array of objects (AmendmentChange) The proposed regimen edits | ||||||
Array
| |||||||
| authoredBy required | string The authoring API client ID, or | ||||||
| offlineApprovalAttested required | boolean Whether the practitioner attested customer approval was obtained offline | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| authoredAgainstRevision | integer or null Protocol revision captured when the amendment was sent; null for drafts. Approval refuses with 409 if the protocol has moved past this revision. | ||||||
| attestedBy | string or null Who attested offline approval | ||||||
| attestedAt | string or null <date-time> | ||||||
| approvedBy | string or null Enum: "customer" "partner" Approving role (not an actor ID) | ||||||
| approvedAt | string or null <date-time> | ||||||
| rejectedBy | string or null Enum: "customer" "partner" | ||||||
| rejectedAt | string or null <date-time> | ||||||
| cancellationReason | string or null Value: "superseded" Set when the platform cancelled the amendment. | ||||||
{- "attestedOffline": false,
- "attestedBy": "string"
}{- "id": "2xAmdK30JFgnwLrgb383PcV9Rk",
- "protocolId": "string",
- "status": "pending",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
], - "authoredBy": "string",
- "authoredAgainstRevision": 0,
- "offlineApprovalAttested": true,
- "attestedBy": "string",
- "attestedAt": "2019-08-24T14:15:22Z",
- "approvedBy": "customer",
- "approvedAt": "2019-08-24T14:15:22Z",
- "rejectedBy": "customer",
- "rejectedAt": "2019-08-24T14:15:22Z",
- "cancellationReason": "superseded",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Issue a new magic-link review URL for a pending amendment. The link token is deliberately absent from every amendment read and is stored only as a hash, so this endpoint is the single disclosure point, on explicit partner action. Use it to deliver the review link through your own channel (SMS, portal message) instead of the platform email.
Each call issues an ADDITIONAL link. Links already delivered — the one in the platform email, or one you fetched earlier — keep working, so fetching a link never strands a customer mid-review. Every link for the amendment stops working at once when it is approved, rejected or cancelled.
| protocolId required | string |
| amendmentId required | string |
| approvalUrl required | string Review URL hosted on the partner's storefront domain (custom domain when active and linked, platform domain otherwise). Stops working once the amendment is approved, rejected or cancelled. |
{
}Approve a pending amendment on behalf of the practice, folding every
change onto the protocol atomically. Each applied change bumps the
protocol revision. approvedBy records the approving role
(partner); customer approvals via the magic-link review page record
customer.
If the protocol changed since the amendment was sent (its revision no
longer matches authoredAgainstRevision), approval refuses with 409 and
the protocol is untouched. Such an amendment can never be approved, so
it is cancelled (cancellationReason: superseded, with the usual
protocol_change_request.cancelled webhook) and its customer link stops
working — re-author it against the current regimen if still needed.
Nothing is billed at approval time: future subscription renewal orders are built and priced from the updated regimen.
Emits protocol_change_request.approved.
| protocolId required | string |
| amendmentId required | string |
| id required | string Unique amendment identifier | ||||||
| protocolId required | string Protocol this amendment applies to | ||||||
| status required | string Enum: "draft" "pending" "approved" "rejected" "cancelled" "expired" Current lifecycle status (lowercase) | ||||||
required | Array of objects (AmendmentChange) The proposed regimen edits | ||||||
Array
| |||||||
| authoredBy required | string The authoring API client ID, or | ||||||
| offlineApprovalAttested required | boolean Whether the practitioner attested customer approval was obtained offline | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| authoredAgainstRevision | integer or null Protocol revision captured when the amendment was sent; null for drafts. Approval refuses with 409 if the protocol has moved past this revision. | ||||||
| attestedBy | string or null Who attested offline approval | ||||||
| attestedAt | string or null <date-time> | ||||||
| approvedBy | string or null Enum: "customer" "partner" Approving role (not an actor ID) | ||||||
| approvedAt | string or null <date-time> | ||||||
| rejectedBy | string or null Enum: "customer" "partner" | ||||||
| rejectedAt | string or null <date-time> | ||||||
| cancellationReason | string or null Value: "superseded" Set when the platform cancelled the amendment. | ||||||
{- "id": "2xAmdK30JFgnwLrgb383PcV9Rk",
- "protocolId": "string",
- "status": "pending",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
], - "authoredBy": "string",
- "authoredAgainstRevision": 0,
- "offlineApprovalAttested": true,
- "attestedBy": "string",
- "attestedAt": "2019-08-24T14:15:22Z",
- "approvedBy": "customer",
- "approvedAt": "2019-08-24T14:15:22Z",
- "rejectedBy": "customer",
- "rejectedAt": "2019-08-24T14:15:22Z",
- "cancellationReason": "superseded",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Reject a pending amendment on behalf of the practice. The protocol
is never mutated. Emits protocol_change_request.rejected.
| protocolId required | string |
| amendmentId required | string |
| id required | string Unique amendment identifier | ||||||
| protocolId required | string Protocol this amendment applies to | ||||||
| status required | string Enum: "draft" "pending" "approved" "rejected" "cancelled" "expired" Current lifecycle status (lowercase) | ||||||
required | Array of objects (AmendmentChange) The proposed regimen edits | ||||||
Array
| |||||||
| authoredBy required | string The authoring API client ID, or | ||||||
| offlineApprovalAttested required | boolean Whether the practitioner attested customer approval was obtained offline | ||||||
| createdAt required | string <date-time> | ||||||
| updatedAt required | string <date-time> | ||||||
| authoredAgainstRevision | integer or null Protocol revision captured when the amendment was sent; null for drafts. Approval refuses with 409 if the protocol has moved past this revision. | ||||||
| attestedBy | string or null Who attested offline approval | ||||||
| attestedAt | string or null <date-time> | ||||||
| approvedBy | string or null Enum: "customer" "partner" Approving role (not an actor ID) | ||||||
| approvedAt | string or null <date-time> | ||||||
| rejectedBy | string or null Enum: "customer" "partner" | ||||||
| rejectedAt | string or null <date-time> | ||||||
| cancellationReason | string or null Value: "superseded" Set when the platform cancelled the amendment. | ||||||
{- "id": "2xAmdK30JFgnwLrgb383PcV9Rk",
- "protocolId": "string",
- "status": "pending",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}
}
], - "authoredBy": "string",
- "authoredAgainstRevision": 0,
- "offlineApprovalAttested": true,
- "attestedBy": "string",
- "attestedAt": "2019-08-24T14:15:22Z",
- "approvedBy": "customer",
- "approvedAt": "2019-08-24T14:15:22Z",
- "rejectedBy": "customer",
- "rejectedAt": "2019-08-24T14:15:22Z",
- "cancellationReason": "superseded",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Author and manage pre-consented regimen edits that fire automatically (smart protocols)
List the scheduled changes on a protocol, optionally filtered by status.
| protocolId required | string |
| status | string Enum: "awaiting_consent" "scheduled" "applied" "skipped" "cancelled" Filter by scheduled-change status (lowercase). Invalid values return 400. |
Array of objects (ScheduledChange) | |||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||
{- "scheduledChanges": [
- {
- "id": "string",
- "trigger": {
- "type": "order_count",
- "value": 1
}, - "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}, - "origin": "original_program",
- "status": "awaiting_consent",
- "endCondition": {
- "endDate": "2019-08-24T14:15:22Z",
- "maxOccurrences": 1
}, - "appliedAt": "2019-08-24T14:15:22Z",
- "appliedOnOrderId": "string",
- "skippedReason": "string",
- "createdAt": "2019-08-24T14:15:22Z"
}
]
}Get details of a single scheduled change.
| protocolId required | string |
| scheduledChangeId required | string |
| id required | string | ||||||||||||||||||||||
required | object (ScheduledChangeTrigger) When a scheduled change fires. | ||||||||||||||||||||||
| |||||||||||||||||||||||
| action required | string Enum: "add" "remove" "update" | ||||||||||||||||||||||
| origin required | string Enum: "original_program" "amendment" Server-derived provenance. | ||||||||||||||||||||||
| status required | string Enum: "awaiting_consent" "scheduled" "applied" "skipped" "cancelled"
| ||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||
| targetItemId | string or null | ||||||||||||||||||||||
object or null (RegimenChangePayload) Regimen edit payload. Keys are camelCase like the rest of the API;
Allowed keys by action:
| |||||||||||||||||||||||
| |||||||||||||||||||||||
object or null Bounds on when a scheduled change may fire. At least one of | |||||||||||||||||||||||
| |||||||||||||||||||||||
| appliedAt | string or null <date-time> | ||||||||||||||||||||||
| appliedOnOrderId | string or null | ||||||||||||||||||||||
| skippedReason | string or null | ||||||||||||||||||||||
{- "id": "string",
- "trigger": {
- "type": "order_count",
- "value": 1
}, - "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}, - "origin": "original_program",
- "status": "awaiting_consent",
- "endCondition": {
- "endDate": "2019-08-24T14:15:22Z",
- "maxOccurrences": 1
}, - "appliedAt": "2019-08-24T14:15:22Z",
- "appliedOnOrderId": "string",
- "skippedReason": "string",
- "createdAt": "2019-08-24T14:15:22Z"
}Cancel a still-scheduled change. The cancelled change is returned.
Cancelling a change that has already fired or is otherwise terminal
(applied/skipped/cancelled) returns a conflict, as does an
awaiting_consent step — resolve its pending consent amendment
instead.
| protocolId required | string |
| scheduledChangeId required | string |
| id required | string | ||||||||||||||||||||||
required | object (ScheduledChangeTrigger) When a scheduled change fires. | ||||||||||||||||||||||
| |||||||||||||||||||||||
| action required | string Enum: "add" "remove" "update" | ||||||||||||||||||||||
| origin required | string Enum: "original_program" "amendment" Server-derived provenance. | ||||||||||||||||||||||
| status required | string Enum: "awaiting_consent" "scheduled" "applied" "skipped" "cancelled"
| ||||||||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||||||||
| targetItemId | string or null | ||||||||||||||||||||||
object or null (RegimenChangePayload) Regimen edit payload. Keys are camelCase like the rest of the API;
Allowed keys by action:
| |||||||||||||||||||||||
| |||||||||||||||||||||||
object or null Bounds on when a scheduled change may fire. At least one of | |||||||||||||||||||||||
| |||||||||||||||||||||||
| appliedAt | string or null <date-time> | ||||||||||||||||||||||
| appliedOnOrderId | string or null | ||||||||||||||||||||||
| skippedReason | string or null | ||||||||||||||||||||||
{- "id": "string",
- "trigger": {
- "type": "order_count",
- "value": 1
}, - "action": "add",
- "targetItemId": "string",
- "payload": {
- "productId": 0,
- "quantity": 1,
- "schedule": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "takeWith": "food",
- "note": "string",
- "reason": "string"
}, - "origin": "original_program",
- "status": "awaiting_consent",
- "endCondition": {
- "endDate": "2019-08-24T14:15:22Z",
- "maxOccurrences": 1
}, - "appliedAt": "2019-08-24T14:15:22Z",
- "appliedOnOrderId": "string",
- "skippedReason": "string",
- "createdAt": "2019-08-24T14:15:22Z"
}Register a new document and receive presigned upload targets. Upload
with the returned uploadPost (an S3 form POST with size limits
enforced); the plain uploadUrl PUT is deprecated. Upload targets
expire after 15 minutes.
The document stays PENDING until the upload is confirmed via the
confirm endpoint. Uploaded files are malware-scanned before
confirmation can succeed — see the confirm endpoint.
Allowed file types: PDF, DOCX, PNG, JPEG, and WebP. Each partner may
hold at most 50 documents, counting unconfirmed PENDING ones.
| name required | string [ 1 .. 255 ] characters |
| fileType required | string Enum: "application/pdf" "application/vnd.openxmlformats-officedocument.wordprocessingml.document" "image/png" "image/jpeg" "image/webp" MIME type of the file |
| originalFilename required | string [ 1 .. 255 ] characters |
| description | string or null <= 1000 characters |
| productId | integer or null |
| productName | string or null |
required | object (DocumentView) | ||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
| uploadUrl required | string Deprecated Presigned URL to PUT the file to (deprecated — prefer | ||||||||||||||||||||||||||
| expiresIn required | integer Seconds until the upload targets expire (900) | ||||||||||||||||||||||||||
object Preferred upload mechanism: a presigned S3 form POST with the file size limit enforced server-side. Submit a multipart/form-data POST to | |||||||||||||||||||||||||||
| |||||||||||||||||||||||||||
{- "name": "string",
- "description": "string",
- "fileType": "application/pdf",
- "originalFilename": "string",
- "productId": 0,
- "productName": "string"
}{- "document": {
- "id": "string",
- "partnerId": "string",
- "name": "string",
- "description": "string",
- "fileType": "string",
- "fileSizeBytes": 0,
- "publicUrl": "string",
- "productId": 0,
- "productName": "string",
- "originalFilename": "string",
- "status": "PENDING",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "uploadPost": {
- "url": "string",
- "fields": {
- "property1": "string",
- "property2": "string"
}
}, - "uploadUrl": "string",
- "expiresIn": 0
}Retrieve a paginated list of the partner's uploaded documents.
| limit | integer [ 1 .. 100 ] Default: 50 |
| cursor | string |
required | Array of objects (DocumentView) | ||||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||||
| cursor | string or null | ||||||||||||||||||||||||||
| hasMore | boolean Default: false | ||||||||||||||||||||||||||
{- "documents": [
- {
- "id": "string",
- "partnerId": "string",
- "name": "string",
- "description": "string",
- "fileType": "string",
- "fileSizeBytes": 0,
- "publicUrl": "string",
- "productId": 0,
- "productName": "string",
- "originalFilename": "string",
- "status": "PENDING",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
], - "cursor": "string",
- "hasMore": false
}Retrieve a single document's metadata.
| documentId required | string |
| id required | string |
| partnerId required | string |
| name required | string |
| fileType required | string |
| originalFilename required | string |
| status required | string Enum: "PENDING" "PROCESSING" "ACTIVE" "ARCHIVED"
|
| createdAt required | string <date-time> |
| updatedAt required | string <date-time> |
| description | string or null |
| fileSizeBytes | integer or null |
| publicUrl | string or null |
| productId | integer or null |
| productName | string or null |
{- "id": "string",
- "partnerId": "string",
- "name": "string",
- "description": "string",
- "fileType": "string",
- "fileSizeBytes": 0,
- "publicUrl": "string",
- "productId": 0,
- "productName": "string",
- "originalFilename": "string",
- "status": "PENDING",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Update a document's name and/or description.
| documentId required | string |
| name | string [ 1 .. 255 ] characters |
| description | string or null <= 1000 characters |
| id required | string |
| partnerId required | string |
| name required | string |
| fileType required | string |
| originalFilename required | string |
| status required | string Enum: "PENDING" "PROCESSING" "ACTIVE" "ARCHIVED"
|
| createdAt required | string <date-time> |
| updatedAt required | string <date-time> |
| description | string or null |
| fileSizeBytes | integer or null |
| publicUrl | string or null |
| productId | integer or null |
| productName | string or null |
{- "name": "string",
- "description": "string"
}{- "id": "string",
- "partnerId": "string",
- "name": "string",
- "description": "string",
- "fileType": "string",
- "fileSizeBytes": 0,
- "publicUrl": "string",
- "productId": 0,
- "productName": "string",
- "originalFilename": "string",
- "status": "PENDING",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Delete a document. A document that still has active assignments cannot be deleted (409) — remove its assignments first.
| documentId required | string |
{- "message": "One or more requested products were not found",
- "code": "PRODUCTS_NOT_FOUND",
- "details": {
- "missingProductIds": [
- 1001
]
}
}Confirm that the file has been uploaded. Non-PDF files transition
PENDING → ACTIVE immediately. PDFs transition to PROCESSING
while they are stamped asynchronously (typically seconds), then become
ACTIVE.
A 404 here can also mean the uploaded file has not landed yet — files are malware-scanned after upload before they become confirmable. Retry after a short delay; a file that never becomes confirmable did not pass scanning.
The call is idempotent: confirming a document that is already
PROCESSING or ACTIVE returns 200 with its current state and does
no further work, so a lost response or a retry is safe.
| documentId required | string |
| id required | string |
| partnerId required | string |
| name required | string |
| fileType required | string |
| originalFilename required | string |
| status required | string Enum: "PENDING" "PROCESSING" "ACTIVE" "ARCHIVED"
|
| createdAt required | string <date-time> |
| updatedAt required | string <date-time> |
| description | string or null |
| fileSizeBytes | integer or null |
| publicUrl | string or null |
| productId | integer or null |
| productName | string or null |
{- "id": "string",
- "partnerId": "string",
- "name": "string",
- "description": "string",
- "fileType": "string",
- "fileSizeBytes": 0,
- "publicUrl": "string",
- "productId": 0,
- "productName": "string",
- "originalFilename": "string",
- "status": "PENDING",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Get a download URL for the document file, valid for one hour. For
stamped PDFs the stamped copy is served. There is no status gate — a
still-PENDING document yields a URL whose file may not exist yet.
| documentId required | string |
| downloadUrl required | string Download URL — a permanent CDN URL when available, otherwise a presigned URL valid for one hour |
{- "downloadUrl": "string"
}Assign an active document to a scope (account-wide, a protocol, or a protocol template) with a delivery mode (digital and/or print).
scopeId is required in practice for PROTOCOL and
PROTOCOL_TEMPLATE scopes — omitting it silently records the
assignment against the account instead of erroring. The target's
existence is not validated, and duplicate assignments are allowed.
| documentId required | string |
| scope required | string Enum: "ACCOUNT" "PROTOCOL" "PROTOCOL_TEMPLATE" |
| deliveryMode required | string Enum: "DIGITAL_ONLY" "DIGITAL_AND_PRINT" "DIGITAL_AND_ONE_TIME_PRINT" |
| scopeId | string or null Protocol or template ID — required in practice for PROTOCOL and PROTOCOL_TEMPLATE scopes (omitting it silently records the assignment against the account). Omit for ACCOUNT scope. |
| id required | string |
| partnerId required | string |
| documentId required | string |
| scope required | string Enum: "ACCOUNT" "PROTOCOL" "PROTOCOL_TEMPLATE" |
| scopeId required | string |
| deliveryMode required | string Enum: "DIGITAL_ONLY" "DIGITAL_AND_PRINT" "DIGITAL_AND_ONE_TIME_PRINT" |
| printFulfilled required | boolean |
| createdAt required | string <date-time> |
{- "scope": "ACCOUNT",
- "scopeId": "string",
- "deliveryMode": "DIGITAL_ONLY"
}{- "id": "string",
- "partnerId": "string",
- "documentId": "string",
- "scope": "ACCOUNT",
- "scopeId": "string",
- "deliveryMode": "DIGITAL_ONLY",
- "printFulfilled": true,
- "createdAt": "2019-08-24T14:15:22Z"
}List all assignments for a document.
| documentId required | string |
required | Array of objects (AssignmentView) | ||||||||||||||||
Array
| |||||||||||||||||
{- "assignments": [
- {
- "id": "string",
- "partnerId": "string",
- "documentId": "string",
- "scope": "ACCOUNT",
- "scopeId": "string",
- "deliveryMode": "DIGITAL_ONLY",
- "printFulfilled": true,
- "createdAt": "2019-08-24T14:15:22Z"
}
]
}Update the delivery mode of an existing assignment. printFulfilled is preserved and cannot be reset.
| assignmentId required | string |
| deliveryMode required | string Enum: "DIGITAL_ONLY" "DIGITAL_AND_PRINT" "DIGITAL_AND_ONE_TIME_PRINT" |
| id required | string |
| partnerId required | string |
| documentId required | string |
| scope required | string Enum: "ACCOUNT" "PROTOCOL" "PROTOCOL_TEMPLATE" |
| scopeId required | string |
| deliveryMode required | string Enum: "DIGITAL_ONLY" "DIGITAL_AND_PRINT" "DIGITAL_AND_ONE_TIME_PRINT" |
| printFulfilled required | boolean |
| createdAt required | string <date-time> |
{- "deliveryMode": "DIGITAL_ONLY"
}{- "id": "string",
- "partnerId": "string",
- "documentId": "string",
- "scope": "ACCOUNT",
- "scopeId": "string",
- "deliveryMode": "DIGITAL_ONLY",
- "printFulfilled": true,
- "createdAt": "2019-08-24T14:15:22Z"
}Remove a document assignment.
| assignmentId required | string |
{- "message": "One or more requested products were not found",
- "code": "PRODUCTS_NOT_FOUND",
- "details": {
- "missingProductIds": [
- 1001
]
}
}List the document assignments visible for a protocol: the union of account-wide assignments and assignments targeting this specific protocol. An unknown protocol ID is not an error — it returns just the account-wide assignments.
| protocolId required | string |
required | Array of objects (AssignmentView) | ||||||||||||||||
Array
| |||||||||||||||||
{- "assignments": [
- {
- "id": "string",
- "partnerId": "string",
- "documentId": "string",
- "scope": "ACCOUNT",
- "scopeId": "string",
- "deliveryMode": "DIGITAL_ONLY",
- "printFulfilled": true,
- "createdAt": "2019-08-24T14:15:22Z"
}
]
}List the documents attached to one of your protocol templates: its
PROTOCOL_TEMPLATE assignments, one per document attached in the
portal. Account-wide assignments are not included. The template's own
documentIds field does not reflect these, so use this route to find
a template's documents. An unknown template ID is not an error: it
returns an empty list.
| templateId required | string |
required | Array of objects (AssignmentView) | ||||||||||||||||
Array
| |||||||||||||||||
{- "assignments": [
- {
- "id": "string",
- "partnerId": "string",
- "documentId": "string",
- "scope": "ACCOUNT",
- "scopeId": "string",
- "deliveryMode": "DIGITAL_ONLY",
- "printFulfilled": true,
- "createdAt": "2019-08-24T14:15:22Z"
}
]
}List DailyStak-provided library documents available to all partners.
Supports an optional productId filter to find documents attached to a
specific product; the filtered mode returns all matches in one response
(limit/cursor are ignored and cursor is always null).
| productId | integer Filter library documents by associated product ID (unpaginated mode) |
| limit | integer [ 1 .. 100 ] Default: 50 |
| cursor | string |
required | Array of objects (LibraryDocumentView) | ||||||||||||||||||||||||
Array
| |||||||||||||||||||||||||
| cursor | string or null | ||||||||||||||||||||||||
| hasMore | boolean Default: false | ||||||||||||||||||||||||
{- "documents": [
- {
- "id": "string",
- "name": "string",
- "description": "string",
- "category": "string",
- "fileType": "string",
- "fileSizeBytes": 0,
- "publicUrl": "string",
- "productId": 0,
- "productName": "string",
- "status": "string",
- "createdAt": "string",
- "updatedAt": "string"
}
], - "cursor": "string",
- "hasMore": false
}Retrieve a single DailyStak library document's metadata.
| documentId required | string |
| id required | string |
| name required | string |
| fileType required | string |
| status required | string |
| createdAt required | string |
| updatedAt required | string |
| description | string or null |
| category | string or null |
| fileSizeBytes | integer or null |
| publicUrl | string or null |
| productId | integer or null |
| productName | string or null |
{- "id": "string",
- "name": "string",
- "description": "string",
- "category": "string",
- "fileType": "string",
- "fileSizeBytes": 0,
- "publicUrl": "string",
- "productId": 0,
- "productName": "string",
- "status": "string",
- "createdAt": "string",
- "updatedAt": "string"
}Get a download URL for a library document. This is usually a permanent CDN URL; when none exists a presigned URL valid for one hour is returned instead — don't cache the URL long-term.
| documentId required | string |
| downloadUrl required | string Download URL — a permanent CDN URL when available, otherwise a presigned URL valid for one hour |
{- "downloadUrl": "string"
}Create and manage your own reusable protocol templates (the partner-owned counterpart to the read-only DailyStak library)
List all of your active protocol templates, optionally filtered by a case-insensitive substring search over template names, line-item product names and tags.
Templates created here and templates copied from the DailyStak library
(POST /v2/library-templates/{templateId}/copy) both appear. Deleted
templates are excluded. The list is unpaginated.
| search | string Example: search=probiotic Case-insensitive substring filter over template name, line-item product names and tags |
required | Array of objects (ProtocolTemplate) | ||||||||||||||||||||
Array
| |||||||||||||||||||||
{- "templates": [
- {
- "id": "2tHkQxL0p9dY6fT3vNqW8rZaBc4",
- "name": "string",
- "description": "string",
- "documentIds": [
- "string"
], - "sourceTemplateId": "string",
- "tags": [
- "gut health",
- "sleep"
], - "lineItems": [
- {
- "productId": 1001,
- "quantity": 0,
- "name": "string",
- "image": "string",
- "administration": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "packDescription": "string",
- "external": true,
- "note": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
]
}Create a reusable, named group of product line items to apply when building protocols for customers.
Line items are enriched server-side from the product catalog: name,
image, and external are always taken from the product record, never
from the request, and packDescription falls back to the product's own
pack description when not supplied. The enrichment is a snapshot at
write time — later catalog changes do not propagate into stored
templates.
Every referenced product must exist and be orderable; unknown or discontinued/draft products reject the whole request with 400.
There is no idempotency key: retrying a successful create makes a duplicate template.
| name required | string <= 100 characters | ||||||||||
required | Array of objects (ProtocolTemplateLineItemInput) [ 1 .. 99 ] items Products in the template (duplicate product IDs are allowed) | ||||||||||
Array ([ 1 .. 99 ] items)
| |||||||||||
| description | string or null <= 500 characters | ||||||||||
| image | string or null <= 2048 characters Use the URL of a file from your DailyStak media library. Only the scheme and host are checked, not whether the file exists or who uploaded it: it must be | ||||||||||
| tags | Array of strings or null <= 50 items Tags. Each is trimmed, has inner whitespace collapsed and is lower-cased; blanks and duplicates are dropped, keeping the first occurrence. After that, each tag may be at most 30 characters and at most 10 may remain, or the request is refused with 422 | ||||||||||
| id required | string Server-generated template identifier | ||||||||||||||||
| name required | string Template display name | ||||||||||||||||
| description required | string or null | ||||||||||||||||
| documentIds required | Array of strings Attached document IDs. Populated only by the library-copy flow; reset to | ||||||||||||||||
| sourceTemplateId required | string or null DailyStak library template this was copied from; null for templates created directly. Reset to null by any update. | ||||||||||||||||
| image required | string or null <uri> The | ||||||||||||||||
| tags required | Array of strings Tags, stored normalized (trimmed, inner whitespace collapsed, lower-cased, de-duplicated). The list | ||||||||||||||||
required | Array of objects (ProtocolTemplateLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
{- "name": "string",
- "description": "string",
- "lineItems": [
- {
- "productId": 0,
- "quantity": 0,
- "administration": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string"
}
], - "image": "string",
- "tags": [
- "string"
]
}{- "id": "2tHkQxL0p9dY6fT3vNqW8rZaBc4",
- "name": "string",
- "description": "string",
- "documentIds": [
- "string"
], - "sourceTemplateId": "string",
- "tags": [
- "gut health",
- "sleep"
], - "lineItems": [
- {
- "productId": 1001,
- "quantity": 0,
- "name": "string",
- "image": "string",
- "administration": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "packDescription": "string",
- "external": true,
- "note": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Fetch one of your templates by ID. Another partner's template is indistinguishable from a missing one (404).
Note: a deleted template no longer appears in the list but remains readable by ID.
| templateId required | string |
| id required | string Server-generated template identifier | ||||||||||||||||
| name required | string Template display name | ||||||||||||||||
| description required | string or null | ||||||||||||||||
| documentIds required | Array of strings Attached document IDs. Populated only by the library-copy flow; reset to | ||||||||||||||||
| sourceTemplateId required | string or null DailyStak library template this was copied from; null for templates created directly. Reset to null by any update. | ||||||||||||||||
| image required | string or null <uri> The | ||||||||||||||||
| tags required | Array of strings Tags, stored normalized (trimmed, inner whitespace collapsed, lower-cased, de-duplicated). The list | ||||||||||||||||
required | Array of objects (ProtocolTemplateLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
{- "id": "2tHkQxL0p9dY6fT3vNqW8rZaBc4",
- "name": "string",
- "description": "string",
- "documentIds": [
- "string"
], - "sourceTemplateId": "string",
- "tags": [
- "gut health",
- "sleep"
], - "lineItems": [
- {
- "productId": 1001,
- "quantity": 0,
- "name": "string",
- "image": "string",
- "administration": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "packDescription": "string",
- "external": true,
- "note": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Partially update a template. Omitted (or explicitly null) fields are
preserved — description cannot be cleared back to null. image: "" and
tags: [] clear those fields. lineItems, when supplied, replace the
entire set and go through the same catalog validation and enrichment as
create.
Warning: every update resets documentIds to [] and
sourceTemplateId to null — updating a template copied from the
DailyStak library severs its library lineage and document attachments.
There is no optimistic concurrency control; the last write wins.
| templateId required | string |
| name | string or null <= 100 characters | ||||||||||
| description | string or null <= 500 characters New description; cannot be cleared back to null | ||||||||||
Array of objects or null (ProtocolTemplateLineItemInput) [ 1 .. 99 ] items | |||||||||||
Array ([ 1 .. 99 ] items)
| |||||||||||
| image | string or null <= 2048 characters New template image, under the same rules as create. Omitted or null keeps the current image; | ||||||||||
| tags | Array of strings or null <= 50 items New tags, normalized and capped as on create (each at most 30 characters once normalized, at most 10 after de-duplication). They replace the current set wholesale. Omitted or null keeps the current tags; | ||||||||||
| id required | string Server-generated template identifier | ||||||||||||||||
| name required | string Template display name | ||||||||||||||||
| description required | string or null | ||||||||||||||||
| documentIds required | Array of strings Attached document IDs. Populated only by the library-copy flow; reset to | ||||||||||||||||
| sourceTemplateId required | string or null DailyStak library template this was copied from; null for templates created directly. Reset to null by any update. | ||||||||||||||||
| image required | string or null <uri> The | ||||||||||||||||
| tags required | Array of strings Tags, stored normalized (trimmed, inner whitespace collapsed, lower-cased, de-duplicated). The list | ||||||||||||||||
required | Array of objects (ProtocolTemplateLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
{- "name": "string",
- "description": "string",
- "lineItems": [
- {
- "productId": 0,
- "quantity": 0,
- "administration": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string"
}
], - "image": "string",
- "tags": [
- "string"
]
}{- "id": "2tHkQxL0p9dY6fT3vNqW8rZaBc4",
- "name": "string",
- "description": "string",
- "documentIds": [
- "string"
], - "sourceTemplateId": "string",
- "tags": [
- "gut health",
- "sleep"
], - "lineItems": [
- {
- "productId": 1001,
- "quantity": 0,
- "name": "string",
- "image": "string",
- "administration": {
- "morning": 0,
- "afternoon": 0,
- "evening": 0,
- "sleep": 0
}, - "packDescription": "string",
- "external": true,
- "note": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Soft-delete a template: it drops out of the template list. There is no undelete. Repeating the delete returns 200 again.
| templateId required | string |
| templateId | string ID of the deleted template |
| message | string |
{- "templateId": "string",
- "message": "Protocol template deleted successfully"
}List DailyStak-curated protocol library templates available to copy into the partner's own protocol templates. The full active catalog is returned in one response (no pagination).
| category | string Filter templates by category (exact, case-sensitive match) |
| search | string Case-insensitive substring match over template names and line-item product names |
required | Array of objects (LibraryTemplateView) | ||||||||||||||||
Array
| |||||||||||||||||
{- "templates": [
- {
- "id": "string",
- "name": "string",
- "description": "string",
- "category": "string",
- "documentIds": [
- "string"
], - "lineItems": [
- {
- "productId": 0,
- "quantity": 0,
- "name": "string",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "external": false,
- "note": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}
]
}Retrieve a single library template with its line items.
| templateId required | string |
| id required | string | ||||||||||||||||
| name required | string | ||||||||||||||||
| category required | string | ||||||||||||||||
required | Array of objects (LibraryTemplateLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
| description | string or null | ||||||||||||||||
| documentIds | Array of strings | ||||||||||||||||
{- "id": "string",
- "name": "string",
- "description": "string",
- "category": "string",
- "documentIds": [
- "string"
], - "lineItems": [
- {
- "productId": 0,
- "quantity": 0,
- "name": "string",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "external": false,
- "note": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Copy a DailyStak library template into the partner's own protocol
templates. The newly-created template is returned (the copy response
itself does not include documentIds); it is thereafter readable and
manageable at /v2/protocol-templates, where it carries
sourceTemplateId (the library template it came from) and the library
template's documentIds.
| templateId required | string |
| id required | string | ||||||||||||||||
| name required | string | ||||||||||||||||
| image required | string or null <uri> The | ||||||||||||||||
| tags required | Array of strings Tags, stored normalized (trimmed, inner whitespace collapsed, lower-cased, de-duplicated). A fresh copy always returns | ||||||||||||||||
required | Array of objects (LibraryTemplateLineItem) | ||||||||||||||||
Array
| |||||||||||||||||
| createdAt required | string <date-time> | ||||||||||||||||
| updatedAt required | string <date-time> | ||||||||||||||||
| description | string or null | ||||||||||||||||
| sourceTemplateId | string or null | ||||||||||||||||
{- "id": "string",
- "name": "string",
- "description": "string",
- "sourceTemplateId": "string",
- "tags": [ ],
- "lineItems": [
- {
- "productId": 0,
- "quantity": 0,
- "name": "string",
- "image": "string",
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "external": false,
- "note": "string"
}
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}Signed event deliveries sent from DailyStak to your HTTPS endpoint. Endpoints are managed in the partner portal, not through this API
An order was created — by POST /v2/orders, by a completed checkout
session, or by a subscription renewal. Fired off the order record's
creation, so it is the single signal for all three paths.
fulfillmentInfo is null here; checkoutSessionId and metadata
correlate the order back to the checkout that produced it.
All order.* events carry the same payload; only type differs.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "order.created" "order.updated" "order.cancelled" "order.fulfilled" "order.shipped" "order.delivered" "order.exception" Event type. | ||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "order.created",
- "created": 1753452000,
- "data": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}Any change to the order that does not map to a more specific event:
progress through PROCESSING, DISPENSING, FILLED and
AWAITING_SHIPMENT, shipping-address edits, and tracking updates.
All order.* events carry the same payload; only type differs.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "order.created" "order.updated" "order.cancelled" "order.fulfilled" "order.shipped" "order.delivered" "order.exception" Event type. | ||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "order.created",
- "created": 1753452000,
- "data": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}The order was cancelled — through the API, in the portal, or by the cascade from deleting a customer.
All order.* events carry the same payload; only type differs.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "order.created" "order.updated" "order.cancelled" "order.fulfilled" "order.shipped" "order.delivered" "order.exception" Event type. | ||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "order.created",
- "created": 1753452000,
- "data": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}The carrier accepted the shipment. fulfillmentInfo now carries
carrier, tracking number and tracking URL.
All order.* events carry the same payload; only type differs.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "order.created" "order.updated" "order.cancelled" "order.fulfilled" "order.shipped" "order.delivered" "order.exception" Event type. | ||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "order.created",
- "created": 1753452000,
- "data": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}Carrier tracking reported delivery.
All order.* events carry the same payload; only type differs.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "order.created" "order.updated" "order.cancelled" "order.fulfilled" "order.shipped" "order.delivered" "order.exception" Event type. | ||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "order.created",
- "created": 1753452000,
- "data": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}Fulfillment hit a problem that needs attention. Orders can recover from this state and continue.
All order.* events carry the same payload; only type differs.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "order.created" "order.updated" "order.cancelled" "order.fulfilled" "order.shipped" "order.delivered" "order.exception" Event type. | ||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "order.created",
- "created": 1753452000,
- "data": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}The order passed through FULFILLED on its way to SHIPPED. The
packing flow writes that transition as its own step, so this fires
immediately before order.shipped.
Not every shipment emits it. An order advanced by the carrier
ship-notify goes from FILLED straight to SHIPPED and never touches
FULFILLED, so treat this as an optional signal and key fulfillment
logic off order.shipped.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "order.created" "order.updated" "order.cancelled" "order.fulfilled" "order.shipped" "order.delivered" "order.exception" Event type. | ||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "order.created",
- "created": 1753452000,
- "data": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}Legacy shape — not an event type. Deliveries to a partner-level
webhookUrl (Partner Settings) are not wrapped in the event envelope:
the body is {"order": {...}} with no id, type or created, and
fires on the same order changes as the order.* events above.
Signed with the partner-level secret from GET /v2/partners/settings/webhook-secret. If no secret is set the delivery
goes out unsigned, with no X-Juniper-Signature header at all.
Configure managed endpoints in the portal instead — they get the envelope, per-endpoint secrets, event filtering and delivery history. If you have both, every order change is delivered to both.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
required | object (OrderWebhookPayload) Shared by every | ||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||
{- "order": {
- "orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "status": "NEW",
- "fulfillmentInfo": {
- "carrier": "UPS",
- "trackingNumber": "1Z9999999999999999",
- "trackingStatus": "InTransit",
- "status": "string",
- "serviceCode": "string",
- "createdAt": "2019-08-24T14:15:22Z",
- "checkpoints": [
- {
- "checkpointTime": "2023-10-03T09:15:00Z",
- "location": "Chicago, IL, USA",
- "message": "Package arrived at facility",
- "tag": "InTransit"
}
]
}, - "createdAt": "2026-07-20T15:04:05+00:00",
- "updatedAt": "2026-07-25T13:20:00+00:00",
- "checkoutSessionId": null,
- "metadata": null,
- "subscriptionId": null,
- "protocolId": null
}
}POST /v2/customers succeeded.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||
| type required | string Enum: "customer.created" "customer.updated" "customer.deleted" Event type. | ||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||
| |||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "customer.created",
- "created": 1753452000,
- "data": {
- "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "payer": "customer",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "managedBy": "string",
- "createdAt": "string",
- "updatedAt": "string"
}
}PUT /v2/customers/{customerId} succeeded.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||
| type required | string Enum: "customer.created" "customer.updated" "customer.deleted" Event type. | ||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||
| |||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "customer.created",
- "created": 1753452000,
- "data": {
- "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "payer": "customer",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "managedBy": "string",
- "createdAt": "string",
- "updatedAt": "string"
}
}DELETE /v2/customers/{customerId} succeeded. The payload is the
customer's pre-delete state. The delete cascade also emits
order.cancelled for each NEW order it cancels, but emits no
subscription events.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||
| type required | string Enum: "customer.created" "customer.updated" "customer.deleted" Event type. | ||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||
| |||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "customer.created",
- "created": 1753452000,
- "data": {
- "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "payer": "customer",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "string",
- "phone": "string",
- "managedBy": "string",
- "createdAt": "string",
- "updatedAt": "string"
}
}A subscription was created — through POST /v2/subscriptions, by your
team in the portal, or by a completed hosted checkout. Carries
protocolId and customerId; with one protocol per cart, protocolId
is your cart correlation key.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Value: "subscription.created" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.created",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}daysSupply, shippingMethod, endDate or notes changed, or a
product was added or removed by the customer in your hosted storefront.
One payload either way; initiatedBy says which surface and
changedFields which fields, with reason cadence_changed when
daysSupply moved and fields_changed otherwise. A product change
adds action and productId instead.
Changes you make through the API emit this too.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "subscription.updated" "subscription.paused" "subscription.resumed" "subscription.cancelled" "subscription.skipped" "subscription.rescheduled" "subscription.payment_recovered" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.updated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}The subscription moved to PAUSED — through the API, the portal, or
the customer in your storefront. One payload either way, with
changedBy naming the actor and initiatedBy the surface.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "subscription.updated" "subscription.paused" "subscription.resumed" "subscription.cancelled" "subscription.skipped" "subscription.rescheduled" "subscription.payment_recovered" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.updated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}A PAUSED subscription returned to ACTIVE — through the API, the
portal, or the customer in your storefront.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "subscription.updated" "subscription.paused" "subscription.resumed" "subscription.cancelled" "subscription.skipped" "subscription.rescheduled" "subscription.payment_recovered" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.updated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}The subscription moved to CANCELLED, whoever caused it. reason
says why: requested, dunning_exhausted, customer_deleted (the
customer-delete cascade), protocol_deactivated,
checkout_compensation, or end_date_reached.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "subscription.updated" "subscription.paused" "subscription.resumed" "subscription.cancelled" "subscription.skipped" "subscription.rescheduled" "subscription.payment_recovered" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.updated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}A cycle was skipped and the billing date advanced — through the API, the portal, or the customer in your storefront.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "subscription.updated" "subscription.paused" "subscription.resumed" "subscription.cancelled" "subscription.skipped" "subscription.rescheduled" "subscription.payment_recovered" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.updated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}The next billing date moved — through the API, the portal, or the customer in your storefront.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "subscription.updated" "subscription.paused" "subscription.resumed" "subscription.cancelled" "subscription.skipped" "subscription.rescheduled" "subscription.payment_recovered" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.updated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}A partner-managed renewal came due and the subscription entered
AWAITING_PARTNER. Collect payment yourself, then call POST /v2/subscriptions/{subscriptionId}/confirm-renewal with the
confirmationToken — which expires 48 hours after issuance.
Miss three consecutive windows and the subscription emits
subscription.payment_failed with
reason: "partner_confirmation_timeout".
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||
| type required | string Value: "subscription.renewal_pending" Event type. | ||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||
| |||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.renewal_pending",
- "created": 1753452000,
- "data": {
- "subscriptionId": "string",
- "customerId": "string",
- "protocolId": "string",
- "confirmationToken": "string",
- "expiresAt": "string",
- "amountCents": 0,
- "lineItems": [
- {
- "productId": "string",
- "name": "string",
- "quantity": 0,
- "customerPrice": "string"
}
]
}
}A platform-billed renewal came due but can't be charged, and won't be
until the cause is fixed: no saved payment method, no payment profile
(or one that doesn't exist in the current payment account), no
shipping address, a zero total, or a missing customer or protocol.
Nothing was charged and the subscription's status is unchanged; it is
retried on each later billing run and charges once the cause is fixed.
The subscription carries renewalBlockedReason until then.
Sent once per cause per billing cycle, from the scheduled billing run (a portal "bill now" that can't charge does not send it). A transient payment provider error is retried automatically and never sends this event.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||
| type required | string Value: "subscription.renewal_blocked" Event type. | ||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||
required | object Event payload. Shape varies by | ||||||||||||
| |||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.renewal_blocked",
- "created": 1753452000,
- "data": {
- "subscriptionId": "string",
- "customerId": "string",
- "protocolId": "string",
- "reason": "customer_not_found",
- "message": "string",
- "nextBillingDate": "string"
}
}A renewal generated an order. This is the renewal signal — there is
no subscription.renewed event.
Platform-billed renewals, renewals you confirm, and portal "bill now"
all send the same payload: confirmedBy and billedNow say which
path produced it. The order itself also emits its own
order.created.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||
| type required | string Value: "subscription.order_created" Event type. | ||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||
| |||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.order_created",
- "created": 1753452000,
- "data": {
- "subscriptionId": "string",
- "customerId": "string",
- "protocolId": "string",
- "orderId": "string",
- "confirmedBy": "platform",
- "billedNow": true,
- "amountCharged": 0,
- "nextBillingDate": "string",
- "externalPaymentReference": "string"
}
}A dunning retry settled the open renewal invoice and the subscription
returned from PAST_DUE to ACTIVE. reason is retry_succeeded.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "subscription.updated" "subscription.paused" "subscription.resumed" "subscription.cancelled" "subscription.skipped" "subscription.rescheduled" "subscription.payment_recovered" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionLifecyclePayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.updated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "changedBy": "string",
- "initiatedBy": "PARTNER_API",
- "reason": "requested",
- "changedFields": [
- "daysSupply"
], - "action": "product_added",
- "productId": "string"
}
}A renewal payment attempt failed. EVERY Juniper-managed dunning
attempt sends this — the first decline, each 3-day retry, and the
terminal one, which subscription.cancelled with
reason: dunning_exhausted follows — as does a third consecutive
missed partner-managed confirmation window.
One payload for all causes: the subscription record plus why it
failed and when it will be retried. nextRetryAt is null once the
retry budget is spent.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Value: "subscription.payment_failed" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (SubscriptionPaymentFailedPayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "subscription.payment_failed",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vTgd30JFgnwLrgb383PcV9Rken",
- "partnerId": "string",
- "protocolId": "string",
- "protocol": {
- "id": "string",
- "customerId": "string",
- "status": "string",
- "lineItems": [
- { }
], - "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z"
}, - "customerId": "2vTgd30JFgnwLrgb383PcV9Rken",
- "daysSupply": 30,
- "shippingMethod": "STANDARD",
- "status": "PENDING",
- "nextBillingDate": "2019-08-24T14:15:22Z",
- "startDate": "2019-08-24T14:15:22Z",
- "endDate": "2019-08-24T14:15:22Z",
- "createdAt": "2019-08-24T14:15:22Z",
- "updatedAt": "2019-08-24T14:15:22Z",
- "lastOrderId": "string",
- "totalOrdersGenerated": 0,
- "notes": "string",
- "scheduleHistory": [
- {
- "changeType": "SKIP",
- "previousDate": "2019-08-24T14:15:22Z",
- "newDate": "2019-08-24T14:15:22Z",
- "changedAt": "2019-08-24T14:15:22Z",
- "reason": "string",
- "changedBy": "string"
}
], - "isActive": true,
- "isDueForBilling": true,
- "paymentFailureCount": 0,
- "lastPaymentError": "string",
- "renewalBlockedReason": "customer_not_found",
- "renewalBlockedAt": "2019-08-24T14:15:22Z",
- "renewalBlockedMessage": "string",
- "cadenceTermsAcceptedAt": "2019-08-24T14:15:22Z",
- "payer": "customer",
- "nextRenewalEstimate": {
- "subtotal": "120.00",
- "shipping": "9.99",
- "tax": "9.90",
- "total": "129.90",
- "currency": "usd",
- "daysSupply": 30,
- "quotedAt": "2019-08-24T14:15:22Z",
- "basis": "quote"
}, - "reason": "card_declined",
- "attempt": 2,
- "maxAttempts": 3,
- "nextRetryAt": "string",
- "failureMessage": "string",
- "changedBy": "string",
- "initiatedBy": "PARTNER_API"
}
}A protocol was created.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol.created" "protocol.updated" "protocol.archived" "protocol.reactivated" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol.created",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
}A protocol was updated through PUT /v2/protocols/{protocolId} or in
the portal. Regimen changes applied through the amendment flow signal
via protocol_change_request.approved instead.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol.created" "protocol.updated" "protocol.archived" "protocol.reactivated" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol.created",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
}A protocol was archived through DELETE /v2/protocols/{protocolId}, in
the portal, or by the daily archiver after 60 days of inactivity.
Archiver-fired events carry a stub customer block — customer.id
is populated and every other customer field is null — because the
archiver runs outside a request and does not look the customer up.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol.created" "protocol.updated" "protocol.archived" "protocol.reactivated" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol.created",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
}A protocol was deactivated; its subscriptions, checkout sessions, amendments and scheduled changes were cancelled.
API-initiated deactivations add a sweep object counting what was
cancelled. Portal-initiated ones send the protocol alone — treat
sweep as optional.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Value: "protocol.deactivated" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object (ProtocolDeactivatedWebhookPayload) Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol.deactivated",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z",
- "sweep": {
- "subscriptionsCancelled": 0,
- "checkoutSessionsCancelled": 0,
- "amendmentsCancelled": 0,
- "scheduledChangesCancelled": 0
}
}
}A deactivated protocol was reactivated.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol.created" "protocol.updated" "protocol.archived" "protocol.reactivated" Event type. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol.created",
- "created": 1753452000,
- "data": {
- "pricingBreakdown": {
- "currency": "USD",
- "status": "estimate",
- "source": "exact",
- "basis": "delivery",
- "daysSupply": null,
- "shippingIncluded": false,
- "taxIncluded": false,
- "complete": false,
- "retailTotal": null,
- "patientDiscountAmount": null,
- "supplySavingsAmount": null,
- "promoSavingsAmount": null,
- "totalPatientSavings": null,
- "patientProductTotal": null,
- "shipping": "0.00",
- "tax": "0.00",
- "patientTotal": null,
- "billingMode": null,
- "discountSource": "practice_default",
- "productCost": null,
- "fees": null,
- "practiceTotal": null,
- "earnings": null
}, - "id": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
- "customer": {
- "id": "string",
- "sourceId": "string",
- "firstName": "string",
- "lastName": "string",
- "email": "user@example.com",
- "phone": "string"
}, - "status": "ACTIVE",
- "name": "string",
- "inactivatedAt": "2019-08-24T14:15:22Z",
- "archivesAt": "2019-08-24T14:15:22Z",
- "orderType": "SUBSCRIPTION",
- "lineItems": [
- {
- "productId": 77123129891,
- "name": "string",
- "quantity": 0,
- "administration": {
- "morning": 1,
- "afternoon": 0,
- "evening": 1,
- "sleep": 0
}, - "packDescription": "string",
- "note": "string",
- "takeWith": "food",
- "templateId": "string",
- "templateSource": "partner",
- "external": true,
- "image": "string"
}
], - "hasExternal": true,
- "createdAt": "2023-10-01T12:00:00Z",
- "updatedAt": "2023-10-01T12:00:00Z"
}
}An amendment was sent for approval (draft to pending) — including sends that auto-approve, and consent amendments raised automatically by scheduled changes. Authoring a draft emits nothing; the lifecycle starts at send.
The protocol_change_request.* event NAMES predate the amendment
model and are kept for wire continuity; the payload itself is
amendment-native and camelCase like the rest of the API.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol_change_request.created" "protocol_change_request.approved" "protocol_change_request.rejected" "protocol_change_request.cancelled" Event type. | ||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol_change_request.created",
- "created": 1753452000,
- "data": {
- "changeRequestId": "string",
- "amendmentId": "string",
- "protocolId": "string",
- "subscriptionId": "string",
- "status": "draft",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": { }
}
], - "authoredBy": "string",
- "createdAt": "string",
- "approvedBy": "string",
- "approvedAt": "string",
- "rejectedBy": "string",
- "rejectedAt": "string",
- "cancellationReason": "string"
}
}An amendment was approved — by the customer on the review page, by you through the approve endpoint, or automatically during an attested or no-subscription send.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol_change_request.created" "protocol_change_request.approved" "protocol_change_request.rejected" "protocol_change_request.cancelled" Event type. | ||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol_change_request.created",
- "created": 1753452000,
- "data": {
- "changeRequestId": "string",
- "amendmentId": "string",
- "protocolId": "string",
- "subscriptionId": "string",
- "status": "draft",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": { }
}
], - "authoredBy": "string",
- "createdAt": "string",
- "approvedBy": "string",
- "approvedAt": "string",
- "rejectedBy": "string",
- "rejectedAt": "string",
- "cancellationReason": "string"
}
}An amendment was rejected, by the customer or by you.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol_change_request.created" "protocol_change_request.approved" "protocol_change_request.rejected" "protocol_change_request.cancelled" Event type. | ||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol_change_request.created",
- "created": 1753452000,
- "data": {
- "changeRequestId": "string",
- "amendmentId": "string",
- "protocolId": "string",
- "subscriptionId": "string",
- "status": "draft",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": { }
}
], - "authoredBy": "string",
- "createdAt": "string",
- "approvedBy": "string",
- "approvedAt": "string",
- "rejectedBy": "string",
- "rejectedAt": "string",
- "cancellationReason": "string"
}
}You cancelled a draft or pending amendment. The 45-day automatic lapse of an unanswered amendment emits no event — poll the amendment if you need to observe it.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||||||||||||||||||||||
| type required | string Enum: "protocol_change_request.created" "protocol_change_request.approved" "protocol_change_request.rejected" "protocol_change_request.cancelled" Event type. | ||||||||||||||||||||||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||||||||||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "protocol_change_request.created",
- "created": 1753452000,
- "data": {
- "changeRequestId": "string",
- "amendmentId": "string",
- "protocolId": "string",
- "subscriptionId": "string",
- "status": "draft",
- "changes": [
- {
- "action": "add",
- "targetItemId": "string",
- "payload": { }
}
], - "authoredBy": "string",
- "createdAt": "string",
- "approvedBy": "string",
- "approvedAt": "string",
- "rejectedBy": "string",
- "rejectedAt": "string",
- "cancellationReason": "string"
}
}A refund was created against one of your orders; the refund is
PROCESSING.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||
| type required | string Enum: "refund.created" "refund.completed" "refund.failed" Event type. | ||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||
| |||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "refund.created",
- "created": 1753452000,
- "data": {
- "refundId": "string",
- "orderId": "string",
- "amount": 4999,
- "refundType": "FULL",
- "status": "PENDING",
- "reason": "string",
- "failureReason": "expired_or_canceled_card"
}
}The refund settled with the payment processor.
A card refund can still fail asynchronously, even days later, flipping
the refund to FAILED; that sends refund.failed.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||
| type required | string Enum: "refund.created" "refund.completed" "refund.failed" Event type. | ||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||
| |||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "refund.created",
- "created": 1753452000,
- "data": {
- "refundId": "string",
- "orderId": "string",
- "amount": 4999,
- "refundType": "FULL",
- "status": "PENDING",
- "reason": "string",
- "failureReason": "expired_or_canceled_card"
}
}The refund did not go through and is now FAILED: the payment
processor rejected it when it was created, or reported it failed later
(even days after refund.completed, in which case the order's refund
accounting is reversed). failureReason carries the processor's
reason when it gives one. Sent once per failure.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||
| type required | string Enum: "refund.created" "refund.completed" "refund.failed" Event type. | ||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||
| |||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "refund.created",
- "created": 1753452000,
- "data": {
- "refundId": "string",
- "orderId": "string",
- "amount": 4999,
- "refundType": "FULL",
- "status": "PENDING",
- "reason": "string",
- "failureReason": "expired_or_canceled_card"
}
}A visitor submitted the contact form on your hosted storefront.
| X-Juniper-Signature required | string Example: t=1753452005,v1=43cebf3abec6a3b49b4745b03fb5279786cdeab4b37377e5afa7990c0b1bb4ae
|
| X-Juniper-Webhook-Id required | string Example: whk_2zKQx4tR8vWn3kFh9YbT0cGdE5L Unique id for this delivery attempt. A retry of the same event carries a new one, so deduplicate on the envelope's |
| id required | string Unique event id. Stable across retries and across endpoints — this is your deduplication key. | ||||||||||||||
| type required | string Value: "contact.received" Event type. | ||||||||||||||
| created required | integer <int64> Unix timestamp (seconds) at which the event was generated. | ||||||||||||||
required | object Event payload. Shape varies by | ||||||||||||||
| |||||||||||||||
{- "id": "evt_2zKQx4tR8vWn3kFh9YbT0cGdE5L",
- "type": "contact.received",
- "created": 1753452000,
- "data": {
- "id": "string",
- "partnerId": "string",
- "fields": { },
- "page": "string",
- "receivedAt": "string"
}
}