Merchant products
Manage a merchant's products from the Dashboard — the sellable items customers pay for, one-time or recurring.
A product is something a merchant sells. It belongs to a product family and is sold through one or more offers that set the billing terms. These endpoints back the Dashboard's products screen: create, list, update, soft-delete, and restore.
A product is either one_time or recurring; the type shapes which offers and prices make sense for it. Products are soft-deleted so historical orders keep resolving to a real record.
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 object
Returned by the list and retrieve endpoints on this page.
| Field | Type | Description |
|---|---|---|
id | string | — |
merchant_id | string | — |
product_family_id | string | null | — |
name | string | — |
description | string | null | — |
type | string | One of one_time, recurring. |
tier_order | number | null | — |
status | string | One of active, archived. |
metadata | object | null | — |
created_at | string | (ISO 8601 UTC). |
updated_at | string | (ISO 8601 UTC). |
Endpoints
GET/api/v1/merchant/products
Auth: Dashboard session (JWT bearer). Merchant membership required.
List products.
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. |
type | string | No | One of one_time, recurring. |
status | string | No | One of active, archived. |
product_family_id | string | No | Filter products to a single family |
name | string | No | Partial-match filter on product name |
date_from | string | No | Filter products created after this date (ISO 8601) |
date_to | string | No | Filter products created before this date (ISO 8601) |
Example request
curl -G https://api.tokeflow.com/api/v1/merchant/products \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
--data-urlencode "page=1" \
--data-urlencode "limit=20" \
--data-urlencode "type=recurring" \
--data-urlencode "status=active"Example response — 200
{
"data": [
{
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
},
"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/products
Auth: Dashboard session (JWT bearer). Merchant membership required.
Create a product.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
product_family_id | string | No | Optional product family ID for grouping related products. Required for recurring with upgrade/downgrade. |
name | string | Yes | — |
description | string | No | — |
type | string | Yes | One of one_time, recurring. |
tier_order | number | No | Position in family. Higher = superior plan. Required when product_family_id is set. |
status | string | Yes | One of active, archived. |
metadata | object | No | Arbitrary metadata (jsonb). |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/products \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{
"product_family_id": "pfa_12345",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
}
}'Example response — 201
{
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
},
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}DELETE/api/v1/merchant/products/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Refuses with 409 while any offer (active) or subscription (any status, via the offer) references the product. Delete the offers first; products 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/products/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/products/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Get a product by ID.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl https://api.tokeflow.com/api/v1/merchant/products/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
},
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}PATCH/api/v1/merchant/products/:id
Auth: Dashboard session (JWT bearer). Merchant membership required.
Update a product.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X PATCH https://api.tokeflow.com/api/v1/merchant/products/obj_1a2b3c4d \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9" \
-H "Content-Type: application/json" \
-d '{}'Example response — 200
{
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
},
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}POST/api/v1/merchant/products/:id/archive
Auth: Dashboard session (JWT bearer). Merchant membership required.
Archive a product (status = archived).
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/products/obj_1a2b3c4d/archive \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 201
{
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
},
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}GET/api/v1/merchant/products/:id/default-offer
Auth: Dashboard session (JWT bearer). Merchant membership required.
Get the default offer for a product (null if none).
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl https://api.tokeflow.com/api/v1/merchant/products/obj_1a2b3c4d/default-offer \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 200
{}POST/api/v1/merchant/products/:id/restore
Auth: Dashboard session (JWT bearer). Merchant membership required.
Restore a soft-deleted product.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/products/obj_1a2b3c4d/restore \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 201
{
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
},
"created_at": "2026-05-19T12:00:00Z",
"updated_at": "2026-05-19T12:00:00Z"
}POST/api/v1/merchant/products/:id/unarchive
Auth: Dashboard session (JWT bearer). Merchant membership required.
Unarchive a product (status = active).
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | — |
Example request
curl -X POST https://api.tokeflow.com/api/v1/merchant/products/obj_1a2b3c4d/unarchive \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "x-merchant-id: mrc_8a3f12d9"Example response — 201
{
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 1,
"status": "active",
"metadata": {
"source": "dashboard"
},
"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. |