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:

CicloSignificado
dailyRenova todos os dias
biweeklyRenova a cada duas semanas
monthlyRenova todo mês
quarterlyRenova a cada três meses
half_yearlyRenova a cada seis meses
yearlyRenova todo ano
customRenova a cada custom_billing_days dias (obrigatório quando custom é usado)
noneSob 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

FieldTypeDescrição
idstringIdentificador único, prefixado com ofr_.
product_idstringO produto que esta oferta precifica (prd_).
namestringNome legível, por exemplo, "Mensal".
slugstringIdentificador em minúsculas, único por produto.
descriptionstring | nullDescrição opcional.
billing_cycleenumdaily biweekly monthly quarterly half_yearly yearly custom none.
custom_billing_daysinteger | nullPeríodo em dias. Obrigatório quando billing_cycle = custom.
cycle_limitinteger | nullNúmero fixo de cobranças. null = cobrar até o cancelamento.
free_trialbooleanSe a oferta inclui um período de teste gratuito.
trial_daysinteger | nullDuração do período de teste em dias. Obrigatório quando free_trial = true.
setup_chargebooleanQuando true, o first_charge_amount do preço é usado para o primeiro ciclo.
renew_after_cycle_limitbooleanQuando true e o cycle_limit é atingido, uma nova assinatura é criada em vez de expirar.
renewal_offer_idstring | nullOferta usada para a renovação pós-limite (ofr_). null = renovar com a mesma oferta. Deve pertencer à mesma família de produtos.
is_defaultbooleanSe esta é a oferta padrão para o produto.
statusenumactive ou archived.
created_atstringTimestamp UTC ISO 8601.
updated_atstringTimestamp UTC ISO 8601.
pricesarrayPreços de oferta aninhados (incluídos na busca por ID).

O objeto de preço de oferta

FieldTypeDescrição
idstringIdentificador único, prefixado com opr_.
offer_idstringA oferta proprietária (ofr_).
currencystringCódigo de moeda ISO 4217, por exemplo, BRL.
amountintegerPreço recorrente em unidades menores (centavos).
first_charge_amountinteger | nullPreç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_defaultbooleanSe este é o preço padrão para a oferta.
created_atstringTimestamp UTC ISO 8601.
updated_atstringTimestamp 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.

FieldTypeRequiredDescrição
product_idstringSimProduto ao qual anexar a oferta (prd_).
namestringSimNome legível.
slugstringSimIdentificador em minúsculas, único por produto.
billing_cycleenumSimUm dos valores de ciclo de cobrança.
statusenumSimactive ou archived.
pricesarraySimPelo menos um objeto de preço (veja abaixo).
descriptionstringNãoDescrição opcional.
custom_billing_daysintegerCondicionalObrigatório quando billing_cycle = custom.
cycle_limitintegerNãoNúmero fixo de cobranças. Omitir = cobrar até o cancelamento.
free_trialbooleanNãoPadrão false.
trial_daysintegerCondicionalObrigatório quando free_trial = true.
setup_chargebooleanNãoPadrão false.
renew_after_cycle_limitbooleanNãoPadrão false.
renewal_offer_idstringNãoOferta para renovação pós-limite (ofr_).
is_defaultbooleanNãoPadrão false.

Cada entrada em prices:

FieldTypeRequiredDescrição
currencystringSimCódigo ISO 4217.
amountintegerSimPreço recorrente em unidades menores (≥ 0).
first_charge_amountintegerNãoPreço do primeiro ciclo em unidades menores (≥ 0).
is_defaultbooleanNãoMarca 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 paramTypeDescrição
pageintegerNúmero da página, padrão 1, mínimo 1.
limitintegerItens por página, padrão 20, máximo 100.
product_idstringFiltrar para um único produto.
statusenumactive ou archived.
billing_cycleenumUm dos valores de ciclo de cobrança.
is_defaultbooleanFiltrar apenas para ofertas padrão.
namestringFiltro de correspondência parcial no nome da oferta.
merchant_idstringObrigató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 paramTypeRequiredDescrição
currencystringNãoCó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:

  1. Se uma transição ativa existir para o par fromId → toId e definir um change_charge_behavior, esse valor prevalece.
  2. 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_behaviorSignificado
next_renewAplica a alteração na próxima renovação.
proratedRateia a diferença imediatamente.
overrideSubstitui 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:

HTTPtypeQuando
400validation_errorCampos ausentes/inválidos, ou uma transição resolvida entre famílias diferentes.
401authentication_errorChave de API ausente ou inválida.
403authorization_errorA chave não possui o escopo necessário.
404not_found_errorOferta (ou produto referenciado) não encontrada para este merchant.
409conflict_errorSlug duplicado por produto, uma segunda oferta/preço padrão, ou uma moeda duplicada.
429rate_limit_errorLimite de taxa excedido — recue e tente novamente.

Relacionados

Nesta página