Merchant product families
Manage a merchant's product families — the top-level grouping that organizes related products.
A product family is the top-level bucket for related products. It carries no price of its own; it exists to organize the catalog and to scope reporting. These endpoints back the Dashboard's product-families screen.
Deleting a family cascades to the products under it, so removals are soft and can be restored. Reordering families changes how the catalog is presented, not how anything is charged.
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 product family object
Returned by the list and retrieve endpoints on this page.
| Field | Type | Description |
|---|---|---|
id | string | — |
merchant_id | string | — |
name | string | — |
slug | string | — |
description | string | null | — |
custom_plan_id | string | null | External plan reference ID (display only). |
change_charge_behavior | string | Default behavior when a subscription transitions between offers in this family. One of next_renew, prorated, override. |
created_at | string | (ISO 8601 UTC). |
updated_at | string | (ISO 8601 UTC). |
Endpoints
GET/api/v1/merchant/product-families
Auth: Dashboard session (JWT bearer). Merchant membership required.
List product families.
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. |
name | string | No | Partial-match filter on family name |
slug | string | No | Filter by exact slug |
change_charge_behavior | string | No | One of next_renew, prorated, override. |
Example request
curl -G https://api.tokeflow.com/api/v1/merchant/product-families \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
--data-urlencode "page=1" \
--data-urlencode "limit=20" \
--data-urlencode "name=Streaming" \
--data-urlencode "slug=streaming-plans"Example response — 200
{
"data": [
{
"id": "pfa_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"name": "Streaming Plans",
"slug": "streaming-plans",
"description": "Family of streaming subscription plans",
"custom_plan_id": "plan_external_42",
"change_charge_behavior": "next_renew",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 42,
"total_pages": 3,
"has_next": true,
"has_prev": false
}
}POST/api/v1/merchant/product-families
Auth: Dashboard session (JWT bearer). Merchant membership required.
Create a product family.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | — |
slug | string | Yes | Lowercase identifier, unique per merchant. |
description | string | No | — |
custom_plan_id | string | No | External plan reference ID (display only). |
change_charge_behavior | string | No | Default offer-transition behavior. Omit to use next_renew. One of next_renew, prorated, override. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/product-families \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"name": "Streaming Plans",
"slug": "streaming-plans",
"description": "Family of streaming subscription plans",
"custom_plan_id": "plan_external_42",
"change_charge_behavior": "next_renew"
}'Example response — 201
{
"id": "pfa_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"name": "Streaming Plans",
"slug": "streaming-plans",
"description": "Family of streaming subscription plans",
"custom_plan_id": "plan_external_42",
"change_charge_behavior": "next_renew",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}DELETE/api/v1/merchant/product-families/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Refuses with 409 while any product (active) or subscription (any status) references the family. Delete or move the products first; families referenced by subscriptions are kept for billing history.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X DELETE https://api.tokeflow.com/api/v1/merchant/product-families/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 204 No Content
An empty body is returned on success.
GET/api/v1/merchant/product-families/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Get a product family by ID.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl https://api.tokeflow.com/api/v1/merchant/product-families/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{
"id": "pfa_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"name": "Streaming Plans",
"slug": "streaming-plans",
"description": "Family of streaming subscription plans",
"custom_plan_id": "plan_external_42",
"change_charge_behavior": "next_renew",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}PATCH/api/v1/merchant/product-families/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Update a product family.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X PATCH https://api.tokeflow.com/api/v1/merchant/product-families/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{}'Example response — 200
{
"id": "pfa_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"name": "Streaming Plans",
"slug": "streaming-plans",
"description": "Family of streaming subscription plans",
"custom_plan_id": "plan_external_42",
"change_charge_behavior": "next_renew",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}POST/api/v1/merchant/product-families/:id/restore
Auth: Dashboard session (JWT bearer). Merchant membership required.
Restore a soft-deleted product family.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/product-families/obj_1a2b3c4d/restore \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 201
{
"id": "pfa_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"name": "Streaming Plans",
"slug": "streaming-plans",
"description": "Family of streaming subscription plans",
"custom_plan_id": "plan_external_42",
"change_charge_behavior": "next_renew",
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}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. |