Offer Prices

Attach amounts and currencies to an offer with offer prices — one default per currency, with soft-delete and restore for safe lifecycle management.

An offer price attaches a concrete amount, in a specific currency, to an offer. An offer can carry several prices — one per currency — so the same plan can be sold in BRL, USD, EUR, and more. Exactly one price per offer can be marked as the default for that offer.

Prices are the leaf of the catalog. The offer defines how a plan bills (cycle, trial, setup); the price defines how much it costs in each currency.

Tokeflow is a payment orchestration platform, not a payment processor. Offer prices model what a plan costs; the actual authorization, capture, and settlement of any charge happen at the connected payment provider. Catalog objects never move money.

How pricing works

  • Amounts are integer minor units. Store cents, not decimals. R$99.00 is 9900; US$150.00 is 15000. Always pair an amount with an ISO 4217 currency.
  • One price per currency, per offer. An offer cannot have two live prices in the same currency. Creating or updating a price into a currency that is already taken returns 409 Conflict.
  • One default per offer. At most one price on an offer can have is_default: true. The default is the price Tokeflow surfaces first (and lists first) for that offer.
  • first_charge_amount drives setup charges. When the parent offer has setup_charge = true, the first cycle is billed at first_charge_amount instead of amount. A value of 0 means a card-validation-only first charge (for example, a free trial that verifies the card). Leave it null to bill the standard amount on the first cycle.

List endpoints sort prices with the default first (is_default DESC), then by currency ascending — so the price you most often need is at the top of the response.

The offer price object

{
  "id": "opr_b1c2d3e4f5",
  "offer_id": "ofr_a1b2c3d4e5",
  "currency": "BRL",
  "amount": 9900,
  "first_charge_amount": 0,
  "is_default": true,
  "created_at": "2026-05-19T12:00:00.000Z",
  "updated_at": "2026-05-19T12:00:00.000Z"
}
FieldTypeDescription
idstringUnique offer price ID, prefixed opr_. Opaque — never parse it.
offer_idstringThe offer this price belongs to (ofr_).
currencystringISO 4217 currency code, e.g. "BRL". Unique per offer among live prices.
amountintegerRecurring/standard amount in minor units (cents).
first_charge_amountinteger | nullFirst-cycle amount in minor units, used when the offer has setup_charge = true. 0 = card-validation-only. null = bill the standard amount.
is_defaultbooleanWhether this is the default price for the offer. At most one per offer.
created_atstringISO 8601 UTC creation timestamp.
updated_atstringISO 8601 UTC last-update timestamp.

Endpoints

All offer price endpoints live under https://api.tokeflow.com/api/v1. Authentication uses a secret key — see Authentication.

MethodPathPurpose
POST/offers/:offerId/pricesAdd a price to an offer
GET/offers/:offerId/pricesList the prices on an offer
GET/offer-prices/:idRetrieve a single price
PATCH/offer-prices/:idUpdate a price
DELETE/offer-prices/:idSoft-delete a price
POST/offer-prices/:id/restoreRestore a soft-deleted price

Offer prices require the offers:read scope for reads and offers:write for mutations. With an Organization key, scope the request to a merchant via the merchant_id query parameter; a Merchant key infers the merchant automatically. See Authentication.


POST/api/v1/offers/:offerId/prices

Auth: Organization key (with merchant_id) or Merchant key.

Adds a price to an existing offer. The parent offer must belong to the authenticated merchant.

Path parameters

FieldTypeRequiredDescription
offerIdstringYesThe offer to price (ofr_).

Body parameters

FieldTypeRequiredDescription
currencystringYesISO 4217 code (exactly 3 characters). Must be unique among the offer's live prices.
amountintegerYesStandard amount in minor units. Minimum 0.
first_charge_amountintegerNoFirst-cycle amount in minor units (minimum 0). Used when the offer has setup_charge = true. Omit to bill the standard amount.
is_defaultbooleanNoMark this price as the offer's default. Defaults to false. The offer may have at most one default.
curl -X POST https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5/prices \
  -H "Authorization: Bearer sk_live_mer_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c3b9f1e-2a4d-4c8e-9f1a-6b2d8e0c4a11" \
  -d '{
    "currency": "BRL",
    "amount": 9900,
    "first_charge_amount": 0,
    "is_default": true
  }'

Response 201 Created

{
  "success": true,
  "data": {
    "id": "opr_b1c2d3e4f5",
    "offer_id": "ofr_a1b2c3d4e5",
    "currency": "BRL",
    "amount": 9900,
    "first_charge_amount": 0,
    "is_default": true,
    "created_at": "2026-05-19T12:00:00.000Z",
    "updated_at": "2026-05-19T12:00:00.000Z"
  },
  "request_id": "req_8f3c2a1b9d",
  "timestamp": "2026-05-19T12:00:00.000Z"
}

Send an Idempotency-Key header (or idempotency_key in the body) on create requests. Replaying the same key returns the original result with HTTP 200 instead of creating a duplicate. See Idempotency.

You cannot add a second price in a currency the offer already prices, and you cannot add a second default. Both cases return 409 Conflict with a conflict_error. Update or soft-delete the existing price first.


GET/api/v1/offers/:offerId/prices

Auth: Organization key (with merchant_id) or Merchant key.

Lists the prices on an offer, default first then by currency. Supports filtering and pagination.

Path parameters

FieldTypeRequiredDescription
offerIdstringYesThe offer whose prices to list (ofr_).

Query parameters

FieldTypeRequiredDescription
currencystringNoFilter to a single ISO 4217 currency (exactly 3 characters).
is_defaultbooleanNoWhen true, return only the default price; when false, exclude it.
pageintegerNoPage number, 1-indexed. Default 1, min 1.
limitintegerNoItems per page. Default 20, max 100.
curl "https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5/prices?page=1&limit=20&currency=BRL&is_default=true" \
  -H "Authorization: Bearer sk_live_mer_REPLACE_ME"

Response 200 OK

{
  "success": true,
  "data": [
    {
      "id": "opr_b1c2d3e4f5",
      "offer_id": "ofr_a1b2c3d4e5",
      "currency": "BRL",
      "amount": 9900,
      "first_charge_amount": 0,
      "is_default": true,
      "created_at": "2026-05-19T12:00:00.000Z",
      "updated_at": "2026-05-19T12:00:00.000Z"
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 1,
      "total_pages": 1,
      "has_next": false,
      "has_prev": false
    }
  },
  "request_id": "req_3a1d7f9c2b",
  "timestamp": "2026-05-19T12:00:00.000Z"
}

GET/api/v1/offer-prices/:id

Auth: Organization key (with merchant_id) or Merchant key.

Retrieves a single price by its opr_ ID. The price must belong to an offer owned by the authenticated merchant.

Path parameters

FieldTypeRequiredDescription
idstringYesThe offer price ID (opr_).
curl https://api.tokeflow.com/api/v1/offer-prices/opr_b1c2d3e4f5 \
  -H "Authorization: Bearer sk_live_mer_REPLACE_ME"

Response 200 OK

{
  "success": true,
  "data": {
    "id": "opr_b1c2d3e4f5",
    "offer_id": "ofr_a1b2c3d4e5",
    "currency": "BRL",
    "amount": 9900,
    "first_charge_amount": 0,
    "is_default": true,
    "created_at": "2026-05-19T12:00:00.000Z",
    "updated_at": "2026-05-19T12:00:00.000Z"
  },
  "request_id": "req_5b2e8c0a1f",
  "timestamp": "2026-05-19T12:00:00.000Z"
}

A missing or out-of-scope price returns 404 Not Found with a not_found_error.


PATCH/api/v1/offer-prices/:id

Auth: Organization key (with merchant_id) or Merchant key.

Partially updates a price. Send only the fields you want to change.

Path parameters

FieldTypeRequiredDescription
idstringYesThe offer price ID (opr_).

Body parameters (all optional)

FieldTypeRequiredDescription
currencystringNoNew ISO 4217 code (exactly 3 characters). Must not collide with another live price on the same offer.
amountintegerNoNew standard amount in minor units (minimum 0).
first_charge_amountintegerNoNew first-cycle amount in minor units (minimum 0).
is_defaultbooleanNoPromote this price to the offer's default. Fails if another default already exists.
curl -X PATCH https://api.tokeflow.com/api/v1/offer-prices/opr_b1c2d3e4f5 \
  -H "Authorization: Bearer sk_live_mer_REPLACE_ME" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 12900 }'

Response 200 OK

{
  "success": true,
  "data": {
    "id": "opr_b1c2d3e4f5",
    "offer_id": "ofr_a1b2c3d4e5",
    "currency": "BRL",
    "amount": 12900,
    "first_charge_amount": 0,
    "is_default": true,
    "created_at": "2026-05-19T12:00:00.000Z",
    "updated_at": "2026-05-19T13:45:00.000Z"
  },
  "request_id": "req_9d4f1a7e3c",
  "timestamp": "2026-05-19T13:45:00.000Z"
}

Changing currency into one another live price already uses, or setting is_default: true when the offer already has a default, returns 409 Conflict. To move the default, demote the current default first (or soft-delete it), then promote the new one.

Editing a price changes future charges only. It does not retroactively alter charges that already happened at the connected payment provider.


DELETE/api/v1/offer-prices/:id

Auth: Organization key (with merchant_id) or Merchant key.

Soft-deletes a price. The record is retained for auditability and can be brought back with restore. Soft-deleting frees the price's currency slot — and, if it was the default, the default slot — so a new price can occupy them.

Path parameters

FieldTypeRequiredDescription
idstringYesThe offer price ID (opr_).
curl -X DELETE https://api.tokeflow.com/api/v1/offer-prices/opr_b1c2d3e4f5 \
  -H "Authorization: Bearer sk_live_mer_REPLACE_ME"

Response 204 No Content

The response body is empty on success.


POST/api/v1/offer-prices/:id/restore

Auth: Organization key (with merchant_id) or Merchant key.

Restores a soft-deleted price, returning it to active use. Restore is rejected if a live price has since taken the same currency slot, or — for a default price — if the offer already has a default again.

Path parameters

FieldTypeRequiredDescription
idstringYesThe soft-deleted offer price ID (opr_).
curl -X POST https://api.tokeflow.com/api/v1/offer-prices/opr_b1c2d3e4f5/restore \
  -H "Authorization: Bearer sk_live_mer_REPLACE_ME"

Response 201 Created

{
  "success": true,
  "data": {
    "id": "opr_b1c2d3e4f5",
    "offer_id": "ofr_a1b2c3d4e5",
    "currency": "BRL",
    "amount": 12900,
    "first_charge_amount": 0,
    "is_default": true,
    "created_at": "2026-05-19T12:00:00.000Z",
    "updated_at": "2026-05-19T14:10:00.000Z"
  },
  "request_id": "req_1c7a3e9f2d",
  "timestamp": "2026-05-19T14:10:00.000Z"
}

Restoring an already-active price returns 400 Bad Request (validation_error). A currency or default collision with a live price returns 409 Conflict — clear or rename the conflicting price first.

Errors

Offer price endpoints use Tokeflow's standard error envelope. Common cases:

HTTPtypeWhen
400validation_errorMalformed body, bad currency length, negative amount, or restoring an already-active price.
401authentication_errorMissing or invalid secret key.
403authorization_errorKey lacks the required offers:read / offers:write scope.
404not_found_errorThe price or its parent offer does not exist or is out of the key's scope.
409conflict_errorDuplicate currency on the offer, a second default, or a restore that collides with a live price.
429rate_limit_errorRate limit exceeded — back off exponentially. See Rate limits.
{
  "error": {
    "type": "conflict_error",
    "code": "OFFER_PRICE_CURRENCY_EXISTS",
    "message": "Offer ofr_a1b2c3d4e5 already has a price in currency BRL",
    "details": {},
    "request_id": "req_2f8b1d4c7a",
    "timestamp": "2026-05-19T12:05:00.000Z"
  }
}

On this page