Ofertas
Crie e gerencie ofertas — as configurações compráveis e precificadas de um produto, com ciclos de cobrança, períodos de teste, cobranças de setup e preços por moeda.
Uma oferta é uma configuração comprável de um produto. O produto modela o que você vende; a oferta modela como ele é comprado e cobrado: o ciclo de cobrança, o período de teste, o limite de ciclos e um ou mais preços — um por moeda.
Um único produto pode ter muitas ofertas. Um plano mensal e um plano anual do mesmo produto são duas ofertas. Uma promoção e o preço padrão são duas ofertas. Essa separação permite lançar novos preços, executar experimentos e localizar valores sem tocar no produto em si.
A Tokeflow é uma plataforma de orquestração de pagamentos, não um processador de pagamentos. As ofertas descrevem sua precificação; a autorização, captura e liquidação efetivas de qualquer cobrança acontecem no provedor de pagamento conectado. Objetos de catálogo nunca movimentam dinheiro.
Como as ofertas são cobradas
O billing_cycle de uma oferta define sua cadência:
| Ciclo | Significado |
|---|---|
daily | Renova todos os dias |
biweekly | Renova a cada duas semanas |
monthly | Renova todo mês |
quarterly | Renova a cada três meses |
half_yearly | Renova a cada seis meses |
yearly | Renova todo ano |
custom | Renova a cada custom_billing_days dias (obrigatório quando custom é usado) |
none | Sob demanda, sem agendador — uma cobrança única sem renovação recorrente |
O cycle_limit limita o número de cobranças. Omita-o (ou defina como null) para cobrar até que a assinatura seja cancelada. Quando o limite é atingido, a assinatura expira — a menos que renew_after_cycle_limit seja true, caso em que uma nova assinatura é criada. Se renewal_offer_id estiver definido, essa oferta é usada para a renovação pós-limite; caso contrário, a mesma oferta é renovada.
Valor da primeira cobrança
Cada preço carrega um amount base e um first_charge_amount opcional. O first_charge_amount é aplicado quando o setup_charge da oferta é true, e define o preço do primeiro ciclo de cobrança — útil para um preço introdutório, uma taxa de setup única ou um primeiro mês com desconto. Um first_charge_amount de 0 significa uma cobrança de validação de cartão (verificar o instrumento sem cobrar).
Um período de teste gratuito sobrepõe tudo isso: veja a seção Período de teste gratuito abaixo.
Período de teste gratuito
Quando free_trial é true, trial_days é obrigatório e define a duração do período de teste em dias. Durante o período de teste, nenhuma cobrança recorrente é feita; a cobrança começa após o término do período de teste.
Um período de teste cobra 0 no início, e setup_charge não é obrigatório. free_trial é lido diretamente pela regra de preço, então a primeira cobrança é 0 independentemente de a oferta ter uma cobrança de setup. O cartão continua sendo cadastrado — o checkout de valor zero executa uma validação de cartão — para que a primeira cobrança real possa ser feita ao fim do período de teste.
A única coisa que setup_charge ainda acrescenta a um período de teste é um preço de aquisição: com setup_charge: true e um first_charge_amount acima de 0, esse valor é cobrado no início como taxa de adesão, e o período de teste cobre o primeiro ciclo normalmente. Defini-lo igual ou acima de amount não é um período de teste — o cliente já pagou o primeiro ciclo, então a assinatura começa ativa em vez de em teste.
amount e first_charge_amount são inteiros em unidades menores (centavos). R$99,00 = 9900; US$150,00 = 15000. Sempre associe um valor à sua currency ISO 4217.
Um produto pode ter no máximo uma oferta padrão (is_default = true), e uma oferta pode ter no máximo um preço padrão (is_default = true). Dentro de uma oferta, cada moeda pode aparecer no máximo uma vez.
O objeto de oferta
{
"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": true,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"prices": [
{
"id": "opr_b1c2d3e4f5",
"offer_id": "ofr_a1b2c3d4e5",
"currency": "BRL",
"amount": 9900,
"first_charge_amount": 0,
"is_default": true,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
},
{
"id": "opr_c2d3e4f5g6",
"offer_id": "ofr_a1b2c3d4e5",
"currency": "USD",
"amount": 1900,
"first_charge_amount": null,
"is_default": false,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
}
]
}Atributos
| Field | Type | Descrição |
|---|---|---|
id | string | Identificador único, prefixado com ofr_. |
product_id | string | O produto que esta oferta precifica (prd_). |
name | string | Nome legível, por exemplo, "Mensal". |
slug | string | Identificador em minúsculas, único por produto. |
description | string | null | Descrição opcional. |
billing_cycle | enum | daily biweekly monthly quarterly half_yearly yearly custom none. |
custom_billing_days | integer | null | Período em dias. Obrigatório quando billing_cycle = custom. |
cycle_limit | integer | null | Número fixo de cobranças. null = cobrar até o cancelamento. |
free_trial | boolean | Se a oferta inclui um período de teste gratuito. |
trial_days | integer | null | Duração do período de teste em dias. Obrigatório quando free_trial = true. |
setup_charge | boolean | Quando true, o first_charge_amount do preço é usado para o primeiro ciclo. |
renew_after_cycle_limit | boolean | Quando true e o cycle_limit é atingido, uma nova assinatura é criada em vez de expirar. |
renewal_offer_id | string | null | Oferta usada para a renovação pós-limite (ofr_). null = renovar com a mesma oferta. Deve pertencer à mesma família de produtos. |
is_default | boolean | Se esta é a oferta padrão para o produto. |
status | enum | active ou archived. |
created_at | string | Timestamp UTC ISO 8601. |
updated_at | string | Timestamp UTC ISO 8601. |
prices | array | Preços de oferta aninhados (incluídos na busca por ID). |
O objeto de preço de oferta
| Field | Type | Descrição |
|---|---|---|
id | string | Identificador único, prefixado com opr_. |
offer_id | string | A oferta proprietária (ofr_). |
currency | string | Código de moeda ISO 4217, por exemplo, BRL. |
amount | integer | Preço recorrente em unidades menores (centavos). |
first_charge_amount | integer | null | Preço do primeiro ciclo em unidades menores. Aplicado quando setup_charge = true. 0 = validação de cartão. Uma oferta com free_trial cobra 0 no primeiro ciclo de qualquer forma. |
is_default | boolean | Se este é o preço padrão para a oferta. |
created_at | string | Timestamp UTC ISO 8601. |
updated_at | string | Timestamp UTC ISO 8601. |
Endpoints
Todos os endpoints de ofertas estão sob https://api.tokeflow.com/api/v1. Consulte Autenticação para chaves e escopos. As ofertas são um recurso com escopo de merchant: com uma chave de Organization você deve identificar o merchant de destino via merchant_id; com uma chave de Merchant o merchant é inferido.
POST/api/v1/offers
Crie uma oferta junto com pelo menos um preço.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:write.
| Field | Type | Required | Descrição |
|---|---|---|---|
product_id | string | Sim | Produto ao qual anexar a oferta (prd_). |
name | string | Sim | Nome legível. |
slug | string | Sim | Identificador em minúsculas, único por produto. |
billing_cycle | enum | Sim | Um dos valores de ciclo de cobrança. |
status | enum | Sim | active ou archived. |
prices | array | Sim | Pelo menos um objeto de preço (veja abaixo). |
description | string | Não | Descrição opcional. |
custom_billing_days | integer | Condicional | Obrigatório quando billing_cycle = custom. |
cycle_limit | integer | Não | Número fixo de cobranças. Omitir = cobrar até o cancelamento. |
free_trial | boolean | Não | Padrão false. |
trial_days | integer | Condicional | Obrigatório quando free_trial = true. |
setup_charge | boolean | Não | Padrão false. |
renew_after_cycle_limit | boolean | Não | Padrão false. |
renewal_offer_id | string | Não | Oferta para renovação pós-limite (ofr_). |
is_default | boolean | Não | Padrão false. |
Cada entrada em prices:
| Field | Type | Required | Descrição |
|---|---|---|---|
currency | string | Sim | Código ISO 4217. |
amount | integer | Sim | Preço recorrente em unidades menores (≥ 0). |
first_charge_amount | integer | Não | Preço do primeiro ciclo em unidades menores (≥ 0). |
is_default | boolean | Não | Marca o preço padrão para a oferta. |
curl -X POST https://api.tokeflow.com/api/v1/offers \
-H "Authorization: Bearer sk_live_mer_xxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8f3c1d2a-7b6e-4c0a-9f12-3d4e5f6a7b8c" \
-d '{
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"billing_cycle": "monthly",
"status": "active",
"setup_charge": true,
"is_default": true,
"prices": [
{ "currency": "BRL", "amount": 9900, "first_charge_amount": 0, "is_default": true },
{ "currency": "USD", "amount": 1900, "is_default": false }
]
}'Resposta 201 Created:
{
"success": true,
"data": {
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": null,
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": null,
"free_trial": false,
"trial_days": null,
"setup_charge": true,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z",
"prices": [
{
"id": "opr_b1c2d3e4f5",
"offer_id": "ofr_a1b2c3d4e5",
"currency": "BRL",
"amount": 9900,
"first_charge_amount": 0,
"is_default": true,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
},
{
"id": "opr_c2d3e4f5g6",
"offer_id": "ofr_a1b2c3d4e5",
"currency": "USD",
"amount": 1900,
"first_charge_amount": null,
"is_default": false,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
}
]
},
"request_id": "req_8f3c2a1b",
"timestamp": "2026-01-15T12:30:00.000Z"
}Passe um header Idempotency-Key (ou idempotency_key no corpo) nas chamadas de criação. Repetir a mesma chave retorna o resultado original (200 OK) em vez de criar uma duplicata.
GET/api/v1/offers
Liste ofertas com paginação e filtros.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:read.
| Query param | Type | Descrição |
|---|---|---|
page | integer | Número da página, padrão 1, mínimo 1. |
limit | integer | Itens por página, padrão 20, máximo 100. |
product_id | string | Filtrar para um único produto. |
status | enum | active ou archived. |
billing_cycle | enum | Um dos valores de ciclo de cobrança. |
is_default | boolean | Filtrar apenas para ofertas padrão. |
name | string | Filtro de correspondência parcial no nome da oferta. |
merchant_id | string | Obrigatório ao usar uma chave de Organization. |
curl "https://api.tokeflow.com/api/v1/offers?product_id=prd_a1b2c3d4e5&status=active&limit=20" \
-H "Authorization: Bearer sk_live_mer_xxx"Resposta 200 OK:
{
"success": true,
"data": [
{
"id": "ofr_a1b2c3d4e5",
"product_id": "prd_a1b2c3d4e5",
"name": "Mensal",
"slug": "mensal",
"description": null,
"billing_cycle": "monthly",
"custom_billing_days": null,
"cycle_limit": null,
"free_trial": false,
"trial_days": null,
"setup_charge": true,
"renew_after_cycle_limit": false,
"renewal_offer_id": null,
"is_default": true,
"status": "active",
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 1,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
},
"request_id": "req_8f3c2a1c",
"timestamp": "2026-01-15T12:30:00.000Z"
}Os itens da lista retornam o resumo da oferta. Use Obter uma oferta por ID para recuperar os prices aninhados.
GET/api/v1/offers/:id
Recupere uma única oferta, incluindo seus preços.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:read.
curl https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_xxx"A resposta 200 OK retorna o objeto de oferta (com prices) encapsulado no envelope de sucesso padrão. Um 404 (not_found_error) é retornado se a oferta não existir ou pertencer a outro merchant.
PATCH/api/v1/offers/:id
Atualize os campos mutáveis de uma oferta. Envie apenas os campos que você deseja alterar.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:write.
curl -X PATCH https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_xxx" \
-H "Content-Type: application/json" \
-d '{ "name": "Mensal Promo", "cycle_limit": 12 }'A resposta 200 OK retorna o objeto de oferta atualizado.
Para gerenciar os preços anexados a uma oferta, use os endpoints dedicados de Preços de Oferta. O PATCH da oferta atualiza campos de nível de oferta, não os preços aninhados.
DELETE/api/v1/offers/:id
Exclua uma oferta de forma reversível (soft delete). A exclusão se propaga em cascata para os preços da oferta. Ofertas excluídas de forma reversível podem ser recuperadas com restaurar.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:write.
curl -X DELETE https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_xxx"Resposta: 204 No Content (corpo vazio).
GET/api/v1/offers/:id/default-price
Resolva o preço padrão de uma oferta. Passe currency para obter o preço padrão nessa moeda; a resposta é null se nenhum preço correspondente existir.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:read.
| Query param | Type | Required | Descrição |
|---|---|---|---|
currency | string | Não | Código ISO 4217 para o qual resolver o preço. |
curl "https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5/default-price?currency=BRL" \
-H "Authorization: Bearer sk_live_mer_xxx"Resposta 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-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
},
"request_id": "req_8f3c2a1d",
"timestamp": "2026-01-15T12:30:00.000Z"
}Quando nenhum preço padrão corresponde, data é null.
GET/api/v1/offers/:fromId/transitions/:toId/effective-behavior
Resolva o change_charge_behavior efetivo para mover uma assinatura de uma oferta para outra — por exemplo, um upgrade ou downgrade. Ambas as ofertas devem pertencer à mesma família de produtos.
Ordem de resolução:
- Se uma transição ativa existir para o par
fromId → toIde definir umchange_charge_behavior, esse valor prevalece. - Caso contrário, aplica-se o padrão da família de produtos da oferta de origem.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:read.
change_charge_behavior | Significado |
|---|---|
next_renew | Aplica a alteração na próxima renovação. |
prorated | Rateia a diferença imediatamente. |
override | Substitui o cronograma de cobrança atual pelo da nova oferta. |
curl "https://api.tokeflow.com/api/v1/offers/ofr_from123/transitions/ofr_to456/effective-behavior" \
-H "Authorization: Bearer sk_live_mer_xxx"Resposta 200 OK:
{
"success": true,
"data": {
"change_charge_behavior": "next_renew"
},
"request_id": "req_8f3c2a1e",
"timestamp": "2026-01-15T12:30:00.000Z"
}Um 400 (validation_error) é retornado quando a oferta de origem não está em uma família de produtos, ou quando as duas ofertas pertencem a famílias diferentes. Consulte Transições de Oferta para criar e gerenciar sobrescritas de transição.
POST/api/v1/offers/:id/restore
Restaure uma oferta excluída de forma reversível e seus preços.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:write.
curl -X POST https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5/restore \
-H "Authorization: Bearer sk_live_mer_xxx"A resposta retorna o objeto de oferta restaurado.
POST/api/v1/offers/:id/archive
Arquive uma oferta (define status como archived). Ofertas arquivadas permanecem no catálogo e continuam recuperáveis, mas não devem ser oferecidas para novas compras.
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:write.
curl -X POST https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5/archive \
-H "Authorization: Bearer sk_live_mer_xxx"A resposta retorna a oferta com status: "archived".
POST/api/v1/offers/:id/unarchive
Desarquive uma oferta (define status de volta como active).
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Escopo: offers:write.
curl -X POST https://api.tokeflow.com/api/v1/offers/ofr_a1b2c3d4e5/unarchive \
-H "Authorization: Bearer sk_live_mer_xxx"A resposta retorna a oferta com status: "active".
Erros
Os endpoints de ofertas usam o envelope de erro padrão. Casos comuns:
| HTTP | type | Quando |
|---|---|---|
| 400 | validation_error | Campos ausentes/inválidos, ou uma transição resolvida entre famílias diferentes. |
| 401 | authentication_error | Chave de API ausente ou inválida. |
| 403 | authorization_error | A chave não possui o escopo necessário. |
| 404 | not_found_error | Oferta (ou produto referenciado) não encontrada para este merchant. |
| 409 | conflict_error | Slug duplicado por produto, uma segunda oferta/preço padrão, ou uma moeda duplicada. |
| 429 | rate_limit_error | Limite de taxa excedido — recue e tente novamente. |
Relacionados
- Produtos — o que uma oferta precifica.
- Preços de Oferta — gerencie preços por moeda em uma oferta.
- Transições de Oferta — sobrescreva o comportamento de alteração entre pares de ofertas.
- Famílias de Produtos — agrupe produtos e defina o comportamento de alteração padrão.