Products
Create and manage products — the sellable items and plans in your Tokeflow catalog, grouped into families and priced through offers.
A product is a sellable thing: a one-time item like a course, or a recurring plan like a subscription tier. Products are the middle layer of the catalog. They belong to a product family and are priced through one or more offers.
Use products to model what you sell. Use offers to model how it is purchased and billed.
A product has a type (one_time or recurring) and a status (active or archived). When a product belongs to a family, its tier_order positions it within that family — higher means a superior plan — which powers upgrade and downgrade paths between recurring plans.
Tokeflow is a payment orchestration platform, not a payment processor. The catalog models your pricing; the actual authorization, capture, and settlement of any charge happen at the connected payment provider. Catalog objects never move money.
The product object
{
"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:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
id | string | Unique product ID, prefixed prd_. Opaque — never parse it. |
merchant_id | string | The merchant that owns the product (mrc_). |
product_family_id | string | null | Family this product belongs to (pfa_), or null if standalone. |
name | string | Human-readable name, e.g. "Plano Light". |
description | string | null | Optional longer description. |
type | enum | one_time or recurring. |
tier_order | integer | null | Position within the family (higher = superior plan). null when the product has no family. |
status | enum | active or archived. |
metadata | object | null | Arbitrary key/value data you attach. Returned as-is. |
created_at | string | ISO 8601 UTC creation timestamp. |
updated_at | string | ISO 8601 UTC last-update timestamp. |
Enums
| Enum | Values |
|---|---|
type | one_time, recurring |
status | active, archived |
tier_order is required whenever product_family_id is set, and it must be unique within the family. It is used to order plans for upgrades and downgrades. For a standalone product (no family) leave it unset.
Create a product
POST/api/v1/products
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:write.
Creates a product under a merchant. Pair this create with an idempotency key when retries are possible.
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Product name. |
type | enum | Yes | one_time or recurring. |
status | enum | Yes | active or archived. |
product_family_id | string | No | Family to group the product under (pfa_). Required for recurring products that participate in upgrade/downgrade flows. |
tier_order | integer | Conditional | Position within the family (min 0, higher = superior). Required when product_family_id is set. |
description | string | No | Optional description. |
metadata | object | No | Arbitrary metadata to attach. |
With an Organization key, include merchant_id in the request body to target a merchant. With a Merchant key, the merchant is inferred from the key — omit it.
curl -X POST https://api.tokeflow.com/api/v1/products \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9b1f7e0a-2c3d-4e5f-8a9b-0c1d2e3f4a5b" \
-d '{
"name": "Plano Light",
"type": "recurring",
"status": "active",
"product_family_id": "pfa_a1b2c3d4e5",
"description": "Plano com recursos essenciais",
"tier_order": 1,
"metadata": { "source": "dashboard" }
}'Response 201 Created
{
"success": true,
"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:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T12:00:00.000Z"
}tier_order must be unique within a family. Reusing a tier_order already taken by another product in the same family returns 409 (conflict_error).
List products
GET/api/v1/products
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:read.
Returns a paginated list of products for the merchant, newest first. All filters are optional and combine with AND.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number, 1-indexed. Default 1. |
limit | integer | No | Items per page. Default 20, max 100. |
type | enum | No | Filter by one_time or recurring. |
status | enum | No | Filter by active or archived. |
product_family_id | string | No | Restrict to a single family (pfa_). |
name | string | No | Partial, case-insensitive match on product name. |
date_from | string | No | Only products created on/after this ISO 8601 timestamp. |
date_to | string | No | Only products created on/before this ISO 8601 timestamp. |
merchant_id | string | Conditional | Required with an Organization key to target a merchant. |
curl "https://api.tokeflow.com/api/v1/products?page=1&limit=20&type=recurring&status=active&product_family_id=pfa_a1b2c3d4e5&name=Pro&date_from=2026-01-01T00:00:00Z&date_to=2026-12-31T23:59:59Z" \
-H "Authorization: Bearer sk_live_mer_…"Response 200 OK
{
"success": true,
"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:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
},
{
"id": "prd_f6e5d4c3b2",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Pro",
"description": "Plano com recursos avançados",
"type": "recurring",
"tier_order": 2,
"status": "active",
"metadata": null,
"created_at": "2026-05-19T12:05:00.000Z",
"updated_at": "2026-05-19T12:05:00.000Z"
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 2,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T12:30:00.000Z"
}See Pagination and filtering for the shared conventions.
Get a product
GET/api/v1/products/:id
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:read.
Retrieves a single product by ID.
curl https://api.tokeflow.com/api/v1/products/prd_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_…"Response 200 OK
{
"success": true,
"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:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T12:30:00.000Z"
}An unknown ID returns 404 (not_found_error).
Update a product
PATCH/api/v1/products/:id
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:write.
Partial update. Send only the fields you want to change; omitted fields are left untouched.
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | New name. |
type | enum | No | one_time or recurring. |
status | enum | No | active or archived. |
product_family_id | string | No | Move the product to another family. |
tier_order | integer | No | New position within the family. |
description | string | No | New description. |
metadata | object | No | Replaces the stored metadata object. |
curl -X PATCH https://api.tokeflow.com/api/v1/products/prd_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Plano Light Plus",
"tier_order": 2
}'Response 200 OK
{
"success": true,
"data": {
"id": "prd_a1b2c3d4e5",
"merchant_id": "mrc_a1b2c3d4e5",
"product_family_id": "pfa_a1b2c3d4e5",
"name": "Plano Light Plus",
"description": "Plano com recursos essenciais",
"type": "recurring",
"tier_order": 2,
"status": "active",
"metadata": { "source": "dashboard" },
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T13:00:00.000Z"
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T13:00:00.000Z"
}metadata is replaced, not merged. To keep existing keys, send the full object you want stored.
Delete a product
DELETE/api/v1/products/:id
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:write.
Soft-deletes a product. The record is retained for auditability and can be brought back with restore. A soft-deleted product no longer appears in list results.
curl -X DELETE https://api.tokeflow.com/api/v1/products/prd_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_…"Response 204 No Content
A successful delete returns an empty body with status 204.
Soft delete and archive are different. Archiving keeps the product in your catalog with status: archived (visible, not sellable). Deleting removes it from the catalog (recoverable via restore). Use archive to retire a plan you still want to reference; use delete to take a product out entirely.
Restore a product
POST/api/v1/products/:id/restore
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:write.
Restores a soft-deleted product, returning it to the catalog with its previous attributes.
curl -X POST https://api.tokeflow.com/api/v1/products/prd_a1b2c3d4e5/restore \
-H "Authorization: Bearer sk_live_mer_…"Response 201 Created
{
"success": true,
"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:00.000Z",
"updated_at": "2026-05-19T14:00:00.000Z"
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T14:00:00.000Z"
}Archive a product
POST/api/v1/products/:id/archive
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:write.
Sets status to archived. The product stays in your catalog and history but is no longer offered for new purchases.
curl -X POST https://api.tokeflow.com/api/v1/products/prd_a1b2c3d4e5/archive \
-H "Authorization: Bearer sk_live_mer_…"Response 201 Created
{
"success": true,
"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": "archived",
"metadata": { "source": "dashboard" },
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T15:00:00.000Z"
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T15:00:00.000Z"
}Unarchive a product
POST/api/v1/products/:id/unarchive
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:write.
Sets status back to active, returning an archived product to sellable state.
curl -X POST https://api.tokeflow.com/api/v1/products/prd_a1b2c3d4e5/unarchive \
-H "Authorization: Bearer sk_live_mer_…"Response 201 Created
{
"success": true,
"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:00.000Z",
"updated_at": "2026-05-19T16:00:00.000Z"
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T16:00:00.000Z"
}Get the default offer
GET/api/v1/products/:id/default-offer
Auth: Organization key (with merchant_id) or Merchant key. Scope: products:read.
Returns the product's default offer — the one a checkout uses when no specific offer is named. Responds with data: null when the product has no default offer.
curl https://api.tokeflow.com/api/v1/products/prd_a1b2c3d4e5/default-offer \
-H "Authorization: Bearer sk_live_mer_…"Response 200 OK — default offer exists
{
"success": true,
"data": {
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": "Plano mensal padrão",
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": 12,
"free_trial": false,
"trial_days": null,
"setup_charge": false,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z",
"prices": [
{
"id": "opr_b1c2d3e4f5",
"offer_id": "ofr_a1b2c3d4e5",
"currency": "BRL",
"amount": 9900,
"first_charge_amount": null,
"is_default": true,
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
}
]
},
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T12:30:00.000Z"
}Response 200 OK — no default offer
{
"success": true,
"data": null,
"request_id": "req_8f3c…",
"timestamp": "2026-05-19T12:30:00.000Z"
}Amounts in offer prices are integers in minor units (cents). 9900 with currency BRL is R$99.00. See Offers and Offer Prices for the full pricing model.
Errors
Products use the standard error envelope. Common cases:
| Status | type | When |
|---|---|---|
| 400 | validation_error | Missing/invalid field, e.g. unknown type or status. |
| 401 | authentication_error | Missing or invalid API key. |
| 403 | authorization_error | Key lacks the required products:read / products:write scope. |
| 404 | not_found_error | No product with that ID for the merchant. |
| 409 | conflict_error | tier_order already used by another product in the family. |
See the full error catalog for codes and handling.
Related
- Product Families — the grouping a product belongs to.
- Offers — purchasable, billable configurations of a product.
- Offer Prices — amount and currency variants of an offer.
- Subscriptions — recurring billing driven by offers.