Transições de Oferta
Defina como a movimentação de uma assinatura de uma oferta para outra é cobrada, com substituições por par que têm precedência sobre o padrão da família de produtos.
Uma transição de oferta descreve como uma assinatura é cobrada quando ela se move de uma oferta para outra — um upgrade, um downgrade ou uma troca de plano dentro da mesma família de produtos. Ela vincula um par específico from_offer_id → to_offer_id a uma regra de cobrança (change_charge_behavior) e a um interruptor (is_active).
As famílias de produtos já carregam um comportamento de cobrança de mudança padrão para toda movimentação dentro da família. Uma transição de oferta é a substituição de granularidade fina: quando você precisa que um par específico seja cobrado de forma diferente do padrão da família, você cria uma transição para esse par.
O Tokeflow orquestra pagamentos; ele não processa nem liquida fundos. As transições de oferta são pura modelagem de catálogo — elas descrevem como uma mudança de plano deve ser cobrada, nunca quem movimenta o dinheiro.
As transições de oferta fazem parte do catálogo, que é escopado por Merchant. Com uma chave de Merchant, o merchant é inferido. Com uma chave de Organization, direcione um merchant com o parâmetro de query merchant_id (em list/get) ou campo do corpo (em create). Consulte Authentication.
O problema que elas resolvem
A maioria das mudanças de plano dentro de uma família é cobrada da mesma forma — defina essa regra uma vez na família e pronto. Mas catálogos reais têm exceções:
- Um upgrade de uma oferta mensal para uma anual deve alternar imediatamente (
override), enquanto toda outra movimentação aguarda a próxima renovação. - Um downgrade específico nunca deve ratear proporcionalmente, mesmo que o padrão da família rateie.
- Você quer desabilitar temporariamente a cobrança para um par de transição sem tocar no restante da família.
As transições de oferta permitem que você codifique essas exceções por par, deixando o padrão da família cuidar de todo o resto.
Precedência: transição explícita vence o padrão da família
Quando uma assinatura se move entre duas ofertas, o Tokeflow resolve a regra de cobrança efetiva nesta ordem:
Em palavras:
- Se houver uma transição ativa para o par exato
from_offer_id→to_offer_ide ela definir umchange_charge_behaviornão nulo, esse valor vence. - Caso contrário — sem transição, uma transição inativa ou uma transição com
change_charge_behaviornulo — o Tokeflow recorre aochange_charge_behaviorpadrão da família de produtos.
Para resolver o comportamento efetivo de qualquer par sem reproduzir essa lógica você mesmo, chame o endpoint de effective-behavior das ofertas. Ele retorna o único valor que se aplicaria, após a precedência.
O motor de transição executa os três comportamentos — next_renew, prorated e override. O change_charge_behavior definido para um par é o que será aplicado quando uma assinatura se mover entre essas ofertas.
O comportamento de cobrança de mudança
change_charge_behavior é opcional em uma transição. Deixe-o como null para herdar o padrão da família para esse par enquanto ainda usa a linha da transição para o seu interruptor is_active.
| Valor | Significado |
|---|---|
next_renew | Aplica a mudança na próxima renovação — o novo preço entra em vigor no ciclo de cobrança seguinte. |
prorated | Rateia proporcionalmente a diferença para o ciclo atual. |
override | Substitui a cobrança atual pelos termos da nova oferta imediatamente. |
O objeto de transição de oferta
| Field | Type | Description |
|---|---|---|
id | string | Identificador único, prefixado com oft_. |
from_offer_id | string | A oferta da qual a assinatura está saindo. Prefixado com ofr_. |
to_offer_id | string | A oferta para a qual a assinatura está indo. Prefixado com ofr_. Deve pertencer à mesma família de produtos que from_offer_id. |
change_charge_behavior | enum | null | Substituição por par: next_renew, prorated ou override. null significa recorrer ao padrão da família. |
is_active | boolean | Se a transição está elegível para processamento. Transições inativas são ignoradas e o padrão da família se aplica. O padrão é true. |
created_at | string | Timestamp de criação em ISO 8601 UTC. |
updated_at | string | Timestamp da última atualização em ISO 8601 UTC. |
{
"id": "oft_a1b2c3d4e5",
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_7pq4m1",
"change_charge_behavior": "override",
"is_active": true,
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
}Um par de transição é único. Cada combinação from_offer_id → to_offer_id pode ter no máximo uma transição. Ambas as ofertas devem pertencer à mesma família de produtos, e from_offer_id deve diferir de to_offer_id.
Endpoints
POST/api/v1/offer-transitions
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Requer o escopo offers:write.
Cria uma transição para um par de ofertas específico.
| Field | Type | Required | Description |
|---|---|---|---|
from_offer_id | string | Sim | A oferta da qual a assinatura está saindo. |
to_offer_id | string | Sim | A oferta para a qual a assinatura está indo. Deve compartilhar a mesma família de produtos que from_offer_id e diferir dela. |
change_charge_behavior | enum | Não | next_renew, prorated ou override. Omita ou use null para herdar o padrão da família. |
is_active | boolean | Não | Se a transição está elegível para processamento. O padrão é true. |
merchant_id | string | Condicional | Obrigatório com uma chave de Organization; omita com uma chave de Merchant. |
curl -X POST https://api.tokeflow.com/api/v1/offer-transitions \
-H "Authorization: Bearer sk_live_mer_REPLACE_WITH_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3a7f9c2e-2c1d-4a8b-9f10-1e2d3c4b5a6f" \
-d '{
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_7pq4m1",
"change_charge_behavior": "override",
"is_active": true
}'{
"success": true,
"data": {
"id": "oft_a1b2c3d4e5",
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_7pq4m1",
"change_charge_behavior": "override",
"is_active": true,
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
},
"request_id": "req_8f3c1a2b4d5e",
"timestamp": "2026-05-19T12:00:00.000Z"
}Envie um cabeçalho Idempotency-Key na criação para que uma requisição repetida nunca produza uma transição duplicada. Repetir a mesma chave retorna o resultado original. Consulte Idempotency & rate limits.
Erros comuns:
| Status | Type | When |
|---|---|---|
| 400 | validation_error | from_offer_id é igual a to_offer_id, uma oferta não existe ou as duas ofertas estão em famílias de produtos diferentes. |
| 409 | conflict_error | Já existe uma transição para este par from_offer_id → to_offer_id. |
{
"error": {
"type": "conflict_error",
"code": "OFFER_TRANSITION_ALREADY_EXISTS",
"message": "A transition for this offer pair already exists.",
"details": {
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_7pq4m1"
},
"request_id": "req_2b9e7c1f0a3d",
"timestamp": "2026-05-19T12:00:00.000Z"
}
}GET/api/v1/offer-transitions
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Requer o escopo offers:read.
Lista as transições do merchant. Suporta paginação e filtragem.
| Query param | Type | Description |
|---|---|---|
page | integer | Número da página, indexado a partir de 1. Padrão 1, mínimo 1. |
limit | integer | Itens por página. Padrão 20, máximo 100. |
from_offer_id | string | Filtra para transições originadas desta oferta. |
to_offer_id | string | Filtra para transições que têm esta oferta como destino. |
is_active | boolean | Filtra para transições ativas (true) ou inativas (false). |
change_charge_behavior | enum | Filtra por comportamento: next_renew, prorated ou override. |
merchant_id | string | Obrigatório com uma chave de Organization; omita com uma chave de Merchant. |
curl "https://api.tokeflow.com/api/v1/offer-transitions?page=1&limit=20&from_offer_id=ofr_3hk9d2&is_active=true" \
-H "Authorization: Bearer sk_live_mer_REPLACE_WITH_YOUR_KEY"{
"success": true,
"data": [
{
"id": "oft_a1b2c3d4e5",
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_7pq4m1",
"change_charge_behavior": "override",
"is_active": true,
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
},
{
"id": "oft_f6g7h8i9j0",
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_2zx5n8",
"change_charge_behavior": null,
"is_active": true,
"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_5c1d8e2f9a0b",
"timestamp": "2026-05-19T12:30:00.000Z"
}Consulte Pagination & filtering para convenções compartilhadas.
GET/api/v1/offer-transitions/:id
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Requer o escopo offers:read.
Recupera uma única transição pelo ID.
curl "https://api.tokeflow.com/api/v1/offer-transitions/oft_a1b2c3d4e5" \
-H "Authorization: Bearer sk_live_mer_REPLACE_WITH_YOUR_KEY"{
"success": true,
"data": {
"id": "oft_a1b2c3d4e5",
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_7pq4m1",
"change_charge_behavior": "override",
"is_active": true,
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T12:00:00.000Z"
},
"request_id": "req_9a0b1c2d3e4f",
"timestamp": "2026-05-19T12:30:00.000Z"
}Uma transição que não existe ou pertence a outro merchant retorna 404 not_found_error.
PATCH/api/v1/offer-transitions/:id
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Requer o escopo offers:write.
Atualiza a regra de cobrança ou o estado ativo de uma transição. Envie apenas os campos que você deseja alterar.
| Field | Type | Required | Description |
|---|---|---|---|
change_charge_behavior | enum | null | Não | Novo comportamento, ou null para recorrer ao padrão da família. |
is_active | boolean | Não | Habilita ou desabilita a transição. |
O par de ofertas (from_offer_id, to_offer_id) é imutável. Mudar o par é uma alteração estrutural, não uma atualização — exclua a transição e crie uma nova para redirecioná-la.
curl -X PATCH https://api.tokeflow.com/api/v1/offer-transitions/oft_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_REPLACE_WITH_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"change_charge_behavior": "next_renew",
"is_active": false
}'{
"success": true,
"data": {
"id": "oft_a1b2c3d4e5",
"from_offer_id": "ofr_3hk9d2",
"to_offer_id": "ofr_7pq4m1",
"change_charge_behavior": "next_renew",
"is_active": false,
"created_at": "2026-05-19T12:00:00.000Z",
"updated_at": "2026-05-19T13:15:00.000Z"
},
"request_id": "req_0b1c2d3e4f5a",
"timestamp": "2026-05-19T13:15:00.000Z"
}DELETE/api/v1/offer-transitions/:id
Auth: chave de Organization (com merchant_id) ou chave de Merchant. Requer o escopo offers:write.
Remove uma transição. Após a exclusão, as movimentações entre esse par de ofertas recorrem ao padrão da família de produtos. Retorna 204 No Content.
curl -X DELETE https://api.tokeflow.com/api/v1/offer-transitions/oft_a1b2c3d4e5 \
-H "Authorization: Bearer sk_live_mer_REPLACE_WITH_YOUR_KEY"Desabilitar uma transição com PATCH … { "is_active": false } é reversível e preserva a linha. Use DELETE apenas quando você quiser que o par desapareça de vez.
Relacionados
- Famílias de produtos — definem o
change_charge_behaviorpadrão que as transições substituem. - Ofertas — e o endpoint de effective-behavior, que resolve o valor que se aplica a qualquer par após a precedência.
- Assinaturas — o endpoint de change-offer consome o comportamento resolvido quando um assinante troca de plano.