Merchant subscriptions
Manage a merchant's subscriptions from the Dashboard — inspect lifecycle and dunning, pause or resume, cancel, and change the billed offer.
A subscription is a recurring agreement between a customer and a merchant, billed against an offer. These endpoints back the Dashboard's subscriptions screen: list and open subscriptions, walk their transition history, pause and resume, cancel, and move one to a different offer.
Every state change is recorded as a transition — the append-only history that answers what changed, when, and why. Dunning is part of that lifecycle: a failed renewal moves the subscription into a retry state rather than ending it outright.
These endpoints power the Tokeflow Dashboard UI. They are session-authenticated (JWT bearer) — the token comes from signing in to the Dashboard, not from an sk_/pk_ API key. Requests also carry the x-merchant-id header identifying the merchant in context, and the signed-in user must be a member with access to it.
The subscription object
Returned by the list and retrieve endpoints on this page.
| Field | Type | Description |
|---|---|---|
id | string | Subscription id. |
merchant_id | string | Merchant id. |
customer_id | string | Customer id. |
customer_name | string | null | Customer name (resolved). |
customer_email | string | null | Customer email (resolved). |
current_offer_id | string | Current offer id. |
offer_name | string | Offer name (resolved). |
product_id | string | Product id of the current offer. |
product_name | string | Product name (resolved). |
product_family_id | string | Product family id. |
billing_cycle | string | One of daily, biweekly, monthly, quarterly, half_yearly, yearly, custom, none. |
currency | string | ISO 4217 currency. |
current_amount | number | Current charge amount in minor units. |
current_period_start | string | (ISO 8601 UTC). |
current_period_end | string | (ISO 8601 UTC). |
next_billing_at | string | null | (ISO 8601 UTC). |
billing_anchor_day | number | null | — |
trial_start | string | null | (ISO 8601 UTC). |
trial_end | string | null | (ISO 8601 UTC). |
dunning_started_at | string | null | (ISO 8601 UTC). |
dunning_attempt_count | number | — |
dunning_next_retry_at | string | null | (ISO 8601 UTC). |
cycles_completed | number | — |
cycle_limit | number | null | — |
status | string | One of trialing, active, dunning, paused, cancelled, expired. |
cancel_at_period_end | boolean | — |
cancelled_at | string | null | (ISO 8601 UTC). |
cancellation_reason | string | null | — |
payment_instrument_id | string | — |
preferred_connector_id | string | null | — |
preferred_connector_name | string | Preferred connector name (resolved). |
initial_network_transaction_id | string | null | — |
preferred_installments | number | — |
created_at | string | (ISO 8601 UTC). |
updated_at | string | (ISO 8601 UTC). |
Endpoints
GET/api/v1/merchant/subscriptions
Auth: Dashboard session (JWT bearer). Merchant membership required.
List subscriptions for merchant.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number (1-indexed). Default 1, min 1. |
limit | number | No | Number of items per page. Default 20, min 1, max 100. |
customer_id | string | No | Filter by customer ID |
status | string | No | Filter by subscription status |
current_offer_id | string | No | Filter by current offer ID |
product_family_id | string | No | Filter by product family ID |
Example request
curl -G https://api.tokeflow.com/api/v1/merchant/subscriptions \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
--data-urlencode "page=1" \
--data-urlencode "limit=20" \
--data-urlencode "customer_id=cust_123" \
--data-urlencode "status=active"Example response — 200
{
"data": [
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 42,
"total_pages": 3,
"has_next": true,
"has_prev": false
}
}GET/api/v1/merchant/subscriptions/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Get subscription by ID.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}POST/api/v1/merchant/subscriptions/:id/cancel
Auth: Dashboard session (JWT bearer). Merchant membership required.
Cancel subscription (admin attribution).
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
at_period_end | boolean | Yes | When true defers cancellation to the next renewal; false cancels immediately. |
reason | string | No | Optional human-readable reason persisted on the subscription. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d/cancel \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"at_period_end": true,
"reason": "customer no longer needs the service"
}'Example response — 200
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}POST/api/v1/merchant/subscriptions/:id/change-offer
Auth: Dashboard session (JWT bearer). Merchant membership required.
Change subscription offer.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
to_offer_id | string | Yes | Target offer id. Must belong to the same product family as the current offer. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d/change-offer \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"to_offer_id": "off_premium_monthly"
}'Example response — 200
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}POST/api/v1/merchant/subscriptions/:id/change-payment-instrument
Auth: Dashboard session (JWT bearer). Merchant membership required.
Change subscription payment instrument.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
payment_instrument_id | string | Yes | Id of the payment instrument to bill going forward. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d/change-payment-instrument \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"payment_instrument_id": "pi_123"
}'Example response — 200
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}POST/api/v1/merchant/subscriptions/:id/pause
Auth: Dashboard session (JWT bearer). Merchant membership required.
Pause subscription.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | No | Optional human-readable reason persisted on the transition. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d/pause \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"reason": "customer travelling for 6 weeks"
}'Example response — 200
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}POST/api/v1/merchant/subscriptions/:id/resume
Auth: Dashboard session (JWT bearer). Merchant membership required.
Resume subscription.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d/resume \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}POST/api/v1/merchant/subscriptions/:id/reactivate
Auth: Dashboard session (JWT bearer). Merchant membership required.
Call off a scheduled cancellation and keep the subscription on its current plan. Reactivation sets cancel_at_period_end back to false and records a reactivation transition; it does not change the offer, the amount, or the billing dates. Available for a live subscription (active, trialing, or dunning); a paused subscription must be resumed instead, and a terminal one is rejected (409).
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d/reactivate \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{
"id": "sub_123",
"merchant_id": "mrc_123",
"customer_id": "cust_123",
"customer_name": "Acme Store",
"customer_email": "jane@example.com",
"current_offer_id": "off_123",
"offer_name": "Acme Store",
"product_id": "prd_9b1e6c05",
"product_name": "Acme Store",
"product_family_id": "pfam_123",
"billing_cycle": "monthly",
"currency": "BRL",
"current_amount": 4990,
"current_period_start": "2026-05-25T00:00:00Z",
"current_period_end": "2026-06-25T00:00:00Z",
"next_billing_at": "2026-06-25T00:00:00Z",
"billing_anchor_day": 25,
"trial_start": "2026-05-25T00:00:00Z",
"trial_end": "2026-06-08T00:00:00Z",
"dunning_started_at": "2026-01-15T12:30:00.000Z",
"dunning_attempt_count": 0,
"dunning_next_retry_at": "2026-01-15T12:30:00.000Z",
"cycles_completed": 3,
"cycle_limit": 12,
"status": "active",
"cancel_at_period_end": false
}GET/api/v1/merchant/subscriptions/:id/transitions
Auth: Dashboard session (JWT bearer). Merchant membership required.
List subscription transition history.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl https://api.tokeflow.com/api/v1/merchant/subscriptions/obj_1a2b3c4d/transitions \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{
"data": [
{
"id": "sbt_123",
"subscription_id": "sub_123",
"transition_type": "creation",
"from_offer_id": "off_basic_monthly",
"to_offer_id": "off_premium_monthly",
"from_status": "trialing",
"to_status": "trialing",
"triggered_by": "customer",
"order_id": "ord_123",
"reason": "requested_by_customer",
"metadata": {},
"created_at": "2026-01-15T12:30:00.000Z"
}
]
}Errors
Errors use the standard envelope. The most common cases here:
| HTTP | Meaning |
|---|---|
400 | Invalid parameters or a state that does not allow this operation. |
401 | Missing, expired, or invalid Dashboard session token. |
403 | Authenticated, but the signed-in user lacks access to this entity. |
404 | The record does not exist or is not visible to this entity. |