Search Documentation

Search for a documentation page...

Subscription Cycles API

REST API endpoints for the curated boxes of a rolling subscription.

A rolling subscription ships a different box every cycle — "October: Ethiopia Guji + Colombia Huila". Each box is a subscription cycle: a title, a start date, and the list of items that ship in it. A cycle is the current one for exactly one step of its subscription plan's cadence (startsAt <= now < endsAt), and endsAt is derived from that cadence — it is returned but never accepted as input.

Cycles belong to a product whose subscriptionMode is rolling. Create the programme in the dashboard under Subscriptions → Rolling, then curate its calendar here. See Subscription Plans for the billing side, and Products for how the current and upcoming box appear on a product read.

Draft visibility

draft cycles are the merchant's unannounced boxes. They are returned only to a key that also holds the catalog:write scope (or to an unscoped key). A storefront:read key sees published cycles only — a draft is a 404 on the single read and is filtered out of the list, even when ?status=draft is requested.


List Subscription Cycles

GET /api/v1/subscription-cycles

Returns the boxes of one rolling subscription, or of every rolling subscription in the store, newest start date first.

Query Parameters

ParameterTypeDefaultDescription
productIdstringOnly the boxes of this rolling subscription (its product UUID). Omit for every rolling subscription in the store.
statusstringdraft or published. Omit for both.
fromstringOnly boxes starting on or after this ISO 8601 date
tostringOnly boxes starting on or before this ISO 8601 date
limitnumber50Boxes per page (1-100)
offsetnumber0Boxes to skip
curl -H "Authorization: Bearer your_api_key" \
"https://your-store.yns.store/api/v1/subscription-cycles?productId=0191abc0-0000-7000-8000-000000000200"

Response (200)

meta.count is how many boxes matched the filters, not the store total.

{
"data": [
{
"id": "0191abc0-1234-7def-8000-000000000001",
"productId": "0191abc0-0000-7000-8000-000000000200",
"title": "October 2026",
"description": "Two washed lots picked for filter brewing.",
"image": "https://cdn.example.com/october-box.jpg",
"startsAt": "2026-10-01T00:00:00.000Z",
"endsAt": "2026-11-01T00:00:00.000Z",
"status": "published",
"items": [
{
"productVariantId": "0191abc0-0000-7000-8000-000000000100",
"productId": "0191abc0-0000-7000-8000-000000000300",
"name": "Ethiopia Guji",
"variantLabel": "250g / Whole bean",
"sku": "ETH-GUJI-250",
"image": "https://cdn.example.com/eth-guji.jpg",
"quantity": 1
}
],
"createdAt": "2026-09-01T10:30:00.000Z",
"updatedAt": "2026-09-01T10:30:00.000Z"
}
],
"meta": {
"count": 1,
"offset": 0,
"limit": 50
}
}

Items are returned as a denormalized snapshot — the same shape the packing list and order emails use. Prices, stock and other catalog fields are deliberately not exposed: a box may contain subscription-only products (status: "hidden") that are never listed on their own.


Get Subscription Cycle

GET /api/v1/subscription-cycles/{id}

Returns a single box by UUID. Same shape as one entry of the list data.

curl -H "Authorization: Bearer your_api_key" \
https://your-store.yns.store/api/v1/subscription-cycles/0191abc0-1234-7def-8000-000000000001

Errors

StatusMeaning
404No such box in this store — or the box is a draft and the key cannot see drafts

Create Subscription Cycle

POST /api/v1/subscription-cycles

Adds a box to a rolling subscription's calendar.

Request Body

FieldTypeRequiredDescription
productIdstringYesUUID of the rolling subscription's product (subscriptionMode must be rolling)
titlestringYesWhat subscribers see this box called (1-120 chars), e.g. "October 2026"
startsAtstringYesISO 8601 date or date-time the box starts shipping
descriptionstring | nullNoA few lines about the box, shown under the title
imagestring | nullNoAbsolute URL of the box photo. Storefronts fall back to the item photos when unset.
statusstringNodraft (default) or published
itemsobject[]NoWhat ships in the box (max 20 entries)

Each entry of items:

FieldTypeRequiredDescription
productVariantIdstringYesVariant UUID of an item that ships in this box
quantitynumberNoHow many of this item ship in the box (1-99, default 1)

A variant may appear only once — raise its quantity instead. Subscription-only items are ordinary products set to status: "hidden" (sellable but unlisted), so a box never needs a separate catalog.

endsAt is not accepted: it is derived from the subscription plan's cadence and moves with startsAt.

curl -X POST \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
"productId": "0191abc0-0000-7000-8000-000000000200",
"title": "October 2026",
"startsAt": "2026-10-01",
"status": "published",
"items": [
{ "productVariantId": "0191abc0-0000-7000-8000-000000000100", "quantity": 1 }
]
}' \
https://your-store.yns.store/api/v1/subscription-cycles

Response (201)

Returns the created cycle (same shape as Get Subscription Cycle).

Errors

StatusMeaning
400The product is not a rolling subscription, an item variant is not in this store, an item appears twice, or the box is published with no items — or with an item whose own product is still a draft
404No such product in this store
409Another box of this subscription already starts on that date, or the derived window overlaps another published box

Error bodies carry error plus, where one applies, a hint describing the next step:

{
"error": "[POST /api/v1/subscription-cycles] ApiError: A published cycle must contain at least one item",
"hint": "Add the items this box ships, or keep the cycle as a draft."
}

Update Subscription Cycle

PATCH /api/v1/subscription-cycles/{id}

Partially updates a box. Every field from Create is accepted and optional, except productId — a box cannot move between subscriptions.

Sending items replaces the whole list, so send the full box. Omitting it leaves the items untouched. Moving startsAt moves the derived endsAt with it.

The publish rules are evaluated against the resulting box: flipping an empty box to published without also sending its items is rejected with 400.

curl -X PATCH \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{"status": "published", "title": "October 2026 — Harvest box"}' \
https://your-store.yns.store/api/v1/subscription-cycles/0191abc0-1234-7def-8000-000000000001

Response (200)

Returns the updated cycle.

Errors

Same as Create, plus 404 when the box does not exist in this store.


Delete Subscription Cycle

DELETE /api/v1/subscription-cycles/{id}

Removes a box from the calendar. Orders keep their own snapshot of what shipped, so deleting a box never erases a packing list.

curl -X DELETE \
-H "Authorization: Bearer your_api_key" \
https://your-store.yns.store/api/v1/subscription-cycles/0191abc0-1234-7def-8000-000000000001

Response (200)

{
"ok": true,
"deleted": 1
}

Errors

StatusMeaning
404No such box in this store