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"
}
FieldTypeDescription
idstringUnique product ID, prefixed prd_. Opaque — never parse it.
merchant_idstringThe merchant that owns the product (mrc_).
product_family_idstring | nullFamily this product belongs to (pfa_), or null if standalone.
namestringHuman-readable name, e.g. "Plano Light".
descriptionstring | nullOptional longer description.
typeenumone_time or recurring.
tier_orderinteger | nullPosition within the family (higher = superior plan). null when the product has no family.
statusenumactive or archived.
metadataobject | nullArbitrary key/value data you attach. Returned as-is.
created_atstringISO 8601 UTC creation timestamp.
updated_atstringISO 8601 UTC last-update timestamp.

Enums

EnumValues
typeone_time, recurring
statusactive, 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

FieldTypeRequiredDescription
namestringYesProduct name.
typeenumYesone_time or recurring.
statusenumYesactive or archived.
product_family_idstringNoFamily to group the product under (pfa_). Required for recurring products that participate in upgrade/downgrade flows.
tier_orderintegerConditionalPosition within the family (min 0, higher = superior). Required when product_family_id is set.
descriptionstringNoOptional description.
metadataobjectNoArbitrary 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

FieldTypeRequiredDescription
pageintegerNoPage number, 1-indexed. Default 1.
limitintegerNoItems per page. Default 20, max 100.
typeenumNoFilter by one_time or recurring.
statusenumNoFilter by active or archived.
product_family_idstringNoRestrict to a single family (pfa_).
namestringNoPartial, case-insensitive match on product name.
date_fromstringNoOnly products created on/after this ISO 8601 timestamp.
date_tostringNoOnly products created on/before this ISO 8601 timestamp.
merchant_idstringConditionalRequired 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

FieldTypeRequiredDescription
namestringNoNew name.
typeenumNoone_time or recurring.
statusenumNoactive or archived.
product_family_idstringNoMove the product to another family.
tier_orderintegerNoNew position within the family.
descriptionstringNoNew description.
metadataobjectNoReplaces 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:

StatustypeWhen
400validation_errorMissing/invalid field, e.g. unknown type or status.
401authentication_errorMissing or invalid API key.
403authorization_errorKey lacks the required products:read / products:write scope.
404not_found_errorNo product with that ID for the merchant.
409conflict_errortier_order already used by another product in the family.

See the full error catalog for codes and handling.

On this page