Skip to main content
O que entra aqui. Mudança de contrato: tipo de campo, nome de campo, valor de enum, resposta de erro, endpoint novo ou removido. Melhoria de texto e correção de exemplo na documentação não entram.Como ler. Incompatível altera o que a API já devolvia e exige ação de quem integra. Corrigido, Adicionado e Documentado não exigem nada.
Produtos, ofertas, bumps, clientes e checkouts pelo negócio da chave

Incompatível

Escopo pelo negócio da chave. Nas contas com a leitura por negócio liberada, products, offers, bumps, customers e checkouts passam a listar e alterar só o que é do negócio da chave de API, como já acontece com pedidos, saldo e saques. Chave sem negócio vale para o negócio pessoal. Item de outro negócio responde 404. Nas demais contas nada muda.
Assinatura informa quem a cancelou e o motivo do assinante

Adicionado

canceledBy e cancellationFeedback na assinatura. Os dois campos saem em todo objeto de assinatura: na API pública e no subscription dos webhooks.
  • canceledBy diz quem fez o último cancelamento: subscriber (o assinante, pelo portal), producer (o painel), api (a API pública) ou system (régua de cobrança, disputa, reembolso, cobrança recusada).
  • cancellationFeedback traz o motivo que o assinante escolheu ao cancelar pelo portal: too_expensive, not_using, not_as_expected, access_problem ou other.
Os dois vêm null em assinatura nunca cancelada, em cancelamento anterior a esta mudança e, no caso do motivo, quando o assinante não informou. Até aqui, todo cancelamento pedido chegava igual, e não dava para separar o que o produtor fez do que a integração ou o assinante fizeram.
Duplicação de produto copia a área de membros

Adicionado

Duplicação copia a área de membros. POST /public_api/products/{id}/duplicate/ passa a copiar a área de membros do produto, sem alunos nem progresso, e a resposta ganha o campo member_area_copy: status (done ou copying) e member_area_id, ou null quando o produto não tem área de membros. Com copying, o conteúdo da área chega em segundo plano.Resposta 409 na duplicação. Enquanto a área de membros do produto de origem ainda está sendo copiada, POST /public_api/products/{id}/duplicate/ responde 409 com code: member_area_copying e não cria nada.
Chargeback e MED: aviso por webhook e defesa pela API

Adicionado

Evento dispute_defense_requested. Sai quando um chargeback ou MED abre prazo de defesa, com a forma de pedido e o bloco dispute: tipo (chargeback ou med), registro da contestação, fim do prazo do produtor (deadline, 23:59 de Brasília) e o link da contestação no painel. O evento chargeback também dispara no pré-chargeback, que não comporta defesa, e o MED não tinha evento: quem precisa saber que há uma defesa a enviar assina este. Ele pode ser testado em Testar Webhook.Endpoints de chargeback e MED, com o escopo orders e o id do pedido no caminho, como em reembolsos:
Webhook de pedido informa a moeda da venda

Adicionado

currency no data dos eventos de pedido. A moeda em que vêm baseAmount, amount, fees, discount e as comissões, que é a moeda do produto (BRL, USD, …). Antes ela só aparecia em offer.currency, e quem lia amount sem olhar a oferta tratava uma venda de US19,18comoR 19,18 como R 19,18. Em produto em real nada muda: o campo vem "BRL".
Chave de API de negócio adicional: escrita financeira só depois da aprovação

Adicionado

409 com code: "business_not_approved". Uma chave de API pertence a um negócio. Quando é a chave de um negócio adicional que ainda não foi aprovado, as operações que alteram dados ou movimentam dinheiro respondem 409:
  • Onde vale: POST /public_api/withdrawals/, PUT /public_api/installment-interest/, POST /public_api/refunds/{id}/approve/ e POST /public_api/orders/{id}/refund/.
  • O que continua liberado: todas as consultas (GET), inclusive saldo, taxas e saques.
  • O que fazer: trate pelo code, não pelo texto. Repetir a chamada não resolve; a escrita passa a funcionar quando o negócio for aprovado, sem trocar a chave.
403 quando o dono da chave não é proprietário do negócio. Nas mesmas operações de escrita, a chave de quem deixou de ser proprietário ativo do negócio responde 403 com {"detail": "Apenas o proprietário do negócio pode executar esta ação."}.

Documentado

Saldo, saques, taxas, juro adicional e reembolsos passam a valer para o negócio da chave. Uma chave sem negócio continua valendo para o seu negócio pessoal. O header X-Cakto-Business não tem efeito na API pública: quem usa a chave não escolhe outro negócio.X-Idempotency-Key é isolado por negócio nas contas com o financeiro por negócio liberado. Nessas contas, chaves de API de negócios diferentes do mesmo usuário podem reutilizar o mesmo valor sem que um pedido confunda o outro. Nas demais, todas as chaves do usuário movimentam a mesma conta e o valor continua valendo para o usuário inteiro, como antes.
Cupom em valor, percentual com centavos e cupom só na 1ª cobrança da assinatura

Incompatível

coupon.discount do pedido deixa de ser sempre um percentual. O objeto coupon que vem no pedido ganha discountType. Com "percentage", discount continua sendo o percentual, agora com até duas casas decimais. Com "fixed", discount é um valor na moeda de coupon.currency:
  • Nada muda nos cupons que já existiam: todos vêm "percentage", com o mesmo número.
  • Quem sente: quem calcula o desconto multiplicando o preço por coupon.discount / 100. Leia discountType antes, ou use o discount do pedido, que sempre foi o valor descontado em dinheiro e não mudou.

Adicionado

coupon.discountType e coupon.currency. O tipo do desconto (percentage ou fixed) e a moeda do cupom. No fixed, currency é a moeda em que discount está.coupon.couponType. default: o desconto vale em toda cobrança, como sempre. first_charge: o desconto vale só na 1ª cobrança da assinatura, e as renovações saem pelo preço cheio da oferta. Os pedidos de renovação de uma assinatura vendida com cupom first_charge vêm sem cupom e com discount zero.Regras do coupon na criação de pagamento (POST /public_api/payments/).
  • Cupom fixed vale só na oferta principal (nunca em order bump), só em oferta da mesma moeda e até 80% do preço da oferta. Fora disso, o cupom é ignorado e o pagamento segue sem desconto, como já acontece com cupom inválido ou expirado.
  • Cupom first_charge vale só em oferta de assinatura (em oferta única é ignorado) e só com cartão, Pix ou Pix Automático. Com outro método, a criação devolve 400 com {"detail": "Este cupom não vale para esse meio de pagamento."}.
  • No Pix Automático com cupom first_charge, o comprador paga a 1ª cobrança com desconto e o mandato fica autorizado pelo valor cheio das renovações.
Saldo separa o total bloqueado por reembolso das demais parcelas

Incompatível

pending e reserved de GET /public_api/balance/ mudam de valor. Eles passam a vir descontados do que está retido por pedido de reembolso em processamento. Antes, a parte retida ainda aparecia dentro deles — e, no caso do dinheiro já liberado, aparecia ao mesmo tempo em heldByRefund e em pending, contando o mesmo valor duas vezes.
  • Nada mudou de nome nem de tipo. Continuam string decimal, na mesma posição. O que mudou é o número, e ele fica menor quando há retenção — por isso este aviso: uma mudança assim não quebra o parsing e passa despercebida numa conferência rápida.
  • Quem sente: quem concilia pending ou reserved contra um histórico próprio, ou soma os campos para exibir um total. Conta sem pedido de reembolso em processamento não muda de número.
  • Para reconstituir os valores anteriores, use as duas fatias publicadas junto:
    No exemplo acima: 3890.15 + 120.00 + 89.90 = 4100.05 e 412.00 + 50.00 = 462.00. As fórmulas fecham exatamente sobre os valores da mesma resposta, sem tolerância de arredondamento: se na sua conciliação não fecharem, é inconsistência a investigar — registre a chamada e fale com o suporte.
  • O + heldByRefund da primeira linha não é erro de digitação. Até esta mudança, a parte já liberada do bloqueio era contada ao mesmo tempo em pending e em heldByRefund — o mesmo dinheiro em dois campos. Reconstituir o valor antigo exige devolver essa duplicata, que é justamente o que a mudança apagou.
  • Não use refundBlocked - heldByRefund no lugar das fatias. Essa diferença mistura o que saiu de pending, o que saiu de reserved e o retido que nunca esteve em nenhum dos dois — ela não é a parcela de campo nenhum, e erra em quase todos os casos.
  • Com refundBlocked: null as duas fatias vêm null junto com ele, e pending e reserved não sofreram desconto nenhum: pending_antigo = pending + heldByRefund e reserved_antigo = reserved. Com a retenção desligada heldByRefund é "0.00"; quando a apuração falhou, ele traz a fatia já descontada de available, que a resposta antiga também somava em pending.

Adicionado

refundBlocked no saldo, com as duas fatias de onde ele saiu. O total bloqueado por pedidos de reembolso em processamento: a soma de todas as partes da sua conta nesses pedidos, tenha o dinheiro liberado ou não. Junto com ele vêm refundBlockedFromPending e refundBlockedFromReserved, que dizem quanto desse total saiu de cada campo.heldByRefund continua existindo e não foi renomeado: ele conta só a fatia já liberada, que é a única que o saque de fato recusa. refundBlocked conta essa fatia e a que ainda não liberou — e é por isso que é sempre maior ou igual a heldByRefund. Parte do bloqueio não aparecia em campo nenhum até aqui.As fatias não somam o total. heldByRefund + refundBlockedFromPending + refundBlockedFromReserved pode ser menor que refundBlocked — no exemplo acima, 89.90 + 120.00 + 50.00 = 259.90 contra 289.90. Os 30.00 de diferença são exatamente o bloqueio que não aparecia em campo nenhum antes: pendente já dentro da janela de antecipação, e venda parcialmente antecipada.Com ele, available, pending, reserved, anticipatable e refundBlocked são parcelas disjuntas: cada real aparece em um campo só, então somá-las não conta nada duas vezes. A soma não é “o patrimônio da conta” — uma venda parcialmente antecipada fora de reembolso deixa o resto a receber fora dos cinco campos. anticipatable entra nessa lista porque ele não é um recorte de pending: os dois são baldes separados, e a descrição anterior dizia o contrário.
  • refundBlocked pode vir null, e null não é zero. Significa que não há número a publicar — a retenção não está ativa para a sua conta, ou a apuração falhou naquela chamada. Zero de verdade vem "0.00". Tratar null como zero anuncia ao produtor que nada está bloqueado, o que pode ser falso.
  • As duas fatias acompanham esse null. Sem apuração não há fatia a publicar, e "0.00" ali afirmaria que nada saiu daquele campo.
  • heldByRefund não acompanha o null: ele mantém o próprio valor, que é "0.00" com a retenção desligada e a fatia já descontada de available quando a apuração falhou.
  • refundBlocked não concilia 1:1 com o valor das vendas em análise. Numa venda parcialmente antecipada que ainda não liberou, o bloqueio é só o resto — R30,00numavendadeR 30,00 numa venda de R 50,00 com R$ 20,00 já antecipados. Depois que a venda libera, e na venda antecipada por inteiro, o bloqueio é o valor cheio da venda, e fica também em heldByRefund.
  • Detalhe e exemplos em Saldo.
Reembolsos: evento de webhook refund_requested, consulta e aprovação pela API

Adicionado

Evento de webhook refund_requested. Dispara quando o comprador pede reembolso, com a janela aberta. Além da forma de pedido de sempre, data traz o bloco refundRequest, com requestedAt, autoApproveAt (quando o reembolso é efetivado sozinho, se ninguém agir antes) e reason. O contato do comprador é o customer de sempre. Ver Guia de Webhooks.
  • Nenhuma ação é necessária para quem não quer o evento. Os demais eventos de pedido não mudaram: o bloco só existe no refund_requested.
  • Para receber, assine o evento no webhook (painel ou Atualizar Webhook).
Três operações de reembolso, com o escopo orders que as chaves já têm:
  • GET /public_api/refunds/: Listar Reembolsos, com filtro por status, data do pedido de reembolso e produto.
  • GET /public_api/refunds/{id}/: Consultar Reembolso.
  • POST /public_api/refunds/{id}/approve/: Aprovar Reembolso, que antecipa um reembolso pedido pelo comprador. Só alcança solicitação aberta: se o comprador desistiu, responde 409 e não estorna nada. Repetir a chamada é seguro.
O id do reembolso é o id do pedido, o mesmo data.id do webhook.
Produto em moeda estrangeira exige a venda internacional habilitada na conta

Incompatível

Criar produto com currency diferente de BRL passou a exigir a venda em moeda estrangeira habilitada na conta. A habilitação é feita pela Cakto, por conta, e o painel já só oferecia a escolha de moeda a quem a tinha; a API aceitava de qualquer conta. Agora POST /public_api/products/ com moeda estrangeira, e POST /public_api/products/{id}/duplicate/ de um produto nessa moeda, voltam 400 para conta sem a habilitação:
  • Quem sente: conta sem a venda internacional habilitada que criava ou duplicava produto em dólar (ou em outra moeda que não o real) pela API ou pelo MCP.
  • Produto que já existe continua editável. A moeda de um produto nunca pôde ser trocada depois da criação, então editar não passa por esta regra.
  • Produto em real não muda, inclusive o criado sem currency no corpo, que nasce em BRL.
Evento de pedido ganha o campo callback

Adicionado

callback no payload de pedido. Todo evento de pedido passa a trazer mais um campo: o token que o produtor pôs em ?callback= no link do checkout. É null quando a compra não veio com um.
  • Nenhuma ação é necessária. É adição de campo: o payload não mudou de forma, nada foi renomeado e nada saiu. Um handler que ignora chaves desconhecidas continua funcionando.
  • A lista de campos fixos do evento de pedido foi de 43 para 44 — ver Guia de Webhooks.
Por que o campo existe. Ele é a perna server-to-server do redirect pós-pagamento, um recurso novo em que o comprador é devolvido ao sistema do produtor depois da aprovação, com um identificador daquela compra na URL. O redirect acontece no navegador e por isso pode não acontecer — aba fechada, Pix pago em outro aparelho. O webhook acontece sempre, e agora carrega o mesmo token, o que permite reconciliar a compra sem depender do retorno do navegador.O recurso em si é liberado caso a caso, após análise do Compliance da Cakto; o campo callback, esse, chega para todo mundo — apenas vem null para quem não usa o recurso.
Listagem de saques passa a usar o envelope padrão de paginação

Corrigido

GET /public_api/withdrawals/ estava paginando fora do padrão. A operação nasceu hoje devolvendo { count, total_pages, page, page_size, results } e aceitando page_size — um formato que não é o do contrato. Toda listagem da API pública usa limit e devolve { count, next, previous, results }, e a página de Paginação afirma que o envelope é o mesmo em todas. Agora é.
  • Se você integrou a listagem de saques nas primeiras horas de hoje, troque page_size por limit e leia a próxima página por next em vez de calcular a partir de total_pages. É a única ação necessária, e só afeta a listagem — solicitar e consultar saque não mudaram.
  • O teto de limit nesta operação é 1000, como nas demais listagens ampliadas.
Saque pela API pública, com escopos próprios

Adicionado

Saque. Três operações novas: solicitar, consultar e listar. É a primeira operação do contrato que tira dinheiro da conta, e por isso ela chega com regras que as outras não têm.Dois escopos novos, e nenhuma chave existente os tem.
  • withdrawals_read — consulta e listagem
  • withdrawals_write — solicitar saque
São independentes: quem só dispara saque não ganha leitura do histórico, e quem concilia não ganha o poder de sacar. Nenhuma chave emitida antes de hoje pode sacar — para usar a operação é preciso emitir uma chave concedendo withdrawals_write explicitamente. Isso é proteção, não burocracia: uma chave antiga que vazasse não deve conseguir esvaziar a conta.O que a operação não faz, e não vai fazer:
  • Não cadastra nem altera chave Pix. O saque vai sempre para a chave já cadastrada no painel; pixKey e account no corpo são ignorados. Quem tem a chave de API decide quanto sacar, nunca para onde.
  • Não paga na hora. Todo saque pedido pela API entra na fila de análise com status: "pending". 201 significa pedido registrado, não dinheiro transferido — acompanhe pela consulta.
Duas travas operacionais:
  • X-Idempotency-Key obrigatória no POST. Sem ela a requisição é recusada com 400: num endpoint que move dinheiro, um retry de timeout sem chave de idempotência vira um segundo saque. A resposta fica guardada por 24h; repetir a mesma chave devolve o mesmo resultado.
  • 2 solicitações por hora, por chave de API. Acima disso, 429 com Retry-After.
Valores seguem o padrão do contrato: string decimal, mínimo de "10.00", e a taxa de saque descontada do pedido — você pede amount, a conta recebe amountReceived.
Saldo e agenda de recebimentos na API pública

Adicionado

Saldo e agenda de recebimentos. Duas operações novas, escopo payments. São as mesmas informações do painel: o cartão de saldo e a tela Agenda Financeira.Três pontos que mudam como o cliente é escrito:
  • available pode ser negativo. Retenção por reembolso, chargeback e MED descontam do disponível sem piso, e o resultado é dívida real. Cliente que trate saldo como “sempre ≥ 0” esconde exatamente o que o produtor precisa ver.
  • A agenda filtra por data de liberação, não pela data da venda. releaseDate__gte e releaseDate__lte recortam quando o dinheiro cai; para recortar vendas por criação, use GET /public_api/orders/. Sem os dois parâmetros a resposta traz os próximos 30 dias, e a janela não passa de 365 dias.
  • Dia sem valor não aparece na agenda. A lista é esparsa: para montar um calendário, preencha os buracos com zero.
As duas leem sempre a conta dona do token — não existe parâmetro de produtor. Valor monetário sai como string decimal ("1250.40"), como no resto do contrato. Conta sem cadastro de recebimento concluído responde 409, que não significa saldo zero: saldo zero é 200 com "0.00".

Documentado

Valores e campos que já existiam no backend e não constavam na referência.
  • multi_card e card_pix no enum de paymentMethod. Os dois métodos já chegavam nos seus webhooks; a referência é que listava só os demais. Quem validava o enum como fechado tratava pedido real como valor inválido.
  • offer do order bump pode vir null — o schema declarava o campo como sempre presente.
  • Descrição de paymentMethod em GET /public_api/fees/: a lista é a das taxas configuradas e inclui método que a cobrança não aceita.
Boleto sai do contrato e o pré-requisito de conta Cakto Banking deixa de existir

Incompatível

boleto saiu de paymentMethod em POST /public_api/payments/. O trilho de boleto liquidava numa conta Cakto Banking, que a Cakto deixou de usar. Cobranças com paymentMethod: "boleto" agora voltam 400:
  • Quem sente: quem emite boleto pela API. Migre para pix ou pix_auto, que aceitam o mesmo payload sem os campos dueDate e boleto.
  • POST /public_api/subscriptions/{id}/change-payment-method/ também recusa boleto, pelo mesmo motivo. Assinatura que hoje cobra por boleto precisa ser migrada de método.
  • Boleto já emitido não é cancelado por esta mudança: o que existe segue com o mesmo status e a mesma baixa. O que muda é a emissão de novos.

Corrigido

Pix e Pix Automático não exigem mais conta Cakto Banking. Até aqui, pix e pix_auto voltavam 400 no campo paymentMethod quando o produtor não tinha conta Cakto Banking aberta e ativa. A exigência não correspondia ao fluxo real — esses métodos não liquidam nessa conta — e bloqueava a maioria dos integradores. O gate foi removido: nenhuma resposta nova aparece, uma que não deveria existir deixa de aparecer.
  • Nada a fazer. Quem tratava esse 400 pode remover o tratamento; quem estava bloqueado volta a cobrar sem mudar payload.
  • O erro ContaBankingAusente deixou de existir no schema.
Dígito verificador do cartão na tokenização

Incompatível

POST /public_api/card-tokens/ passou a recusar número de cartão com dígito verificador inválido. Antes o número era tokenizado e a recusa só vinha da adquirente, na autorização. Agora a validação acontece na entrada e a resposta é 422, o mesmo status que a operação já usa para corpo inválido:
  • Só é recusado o que já não era cobrável. Todo cartão termina num dígito verificador calculado pelo algoritmo de Luhn; número que não fecha essa conta não existe, e nenhuma adquirente autoriza. Cartão real continua tokenizando normalmente.
  • Quem sente: quem envia número gerado ou digitado sem conferência — teste automatizado com PAN fictício, principalmente. Se a sua suíte usa número inventado, troque por um que feche o Luhn (4111 1111 1111 1111 e 5555 5555 5555 4444 fecham).
  • Espaço e hífen continuam aceitos: a validação normaliza antes de conferir.
  • Como o pagamento por cartão só aceita cardToken, isso fecha a porta para o número inválido em todo o fluxo — não há outro caminho de PAN na API.
  • A validação não diz que o cartão existe, está ativo ou tem saldo. Recusa por saldo, limite ou antifraude continua vindo da adquirente como sempre.
Como o algoritmo funciona, o que ele pega, implementação pronta e números de teste: Validação do número do cartão.
Assinatura nos webhooks e expiração do Pix aplicada de verdade

Adicionado

Webhooks passam a ir assinados. Toda entrega leva agora dois headers novos: X-Cakto-Timestamp (Unix time do envio) e X-Cakto-Signature, no formato v1=<hmac-sha256> — HMAC-SHA256 de {timestamp}.{corpo cru}, com o secret do webhook como chave. Serve para provar a origem sem depender do segredo que vem no corpo, e detecta payload adulterado ou reenviado.Nada muda para quem já valida pelo corpo. O campo secret continua em toda entrega e a validação por comparação segue válida — os headers são adicionais. Ver Validando a origem.

Corrigido

pixExpiresIn passa a valer. O campo era aceito e validado contra o limite do produto, mas não chegava à adquirente: a validade do QR acabava sendo sempre o padrão de quem processou a cobrança. Agora o valor enviado é repassado, e o pix.expirationDate da resposta reflete o que foi pedido.Se você já mandava o campo, a validade do QR muda a partir de agora — passa a ser a que você pediu, em vez do padrão da adquirente (que costumava ser maior). Quem não envia o campo não é afetado: segue valendo o padrão da adquirente, igual a antes.
Juro adicional de parcelamento, e a taxa que o comprador paga

Adicionado

Juro adicional de parcelamento. Três operações novas, escopo payments (a escrita também exige write). É o juro que você cobra do comprador por parcelar, somado por cima do juro-base da Cakto que sai em GET /public_api/fees/ — são dois números diferentes, cobrados por partes diferentes, na mesma parcela.Sobre a configuração:
  • A faixa é de 2x a 12x, sempre com as onze faixas na resposta e sempre na mesma ordem. Não existe juro adicional em 1x. Parcela sem juro configurado vem com interestPercentage: null — null não é zero.
  • interestPercentage é number em pontos percentuais (1.5 é 1,5%), como toda porcentagem no contrato.
  • active é quem decide se algo é cobrado. Com active: false a tabela continua guardada e nada é somado ao comprador — desligar preserva os percentuais em vez de apagá-los. Sempre cruze os dois campos antes de calcular o valor de uma parcela.
  • O PUT substitui a tabela inteira: parcela que não estiver no corpo fica sem juro, e uma lista vazia zera todas. Mandar o mesmo corpo duas vezes deixa a conta no mesmo estado, então a chamada é segura de repetir — e por isso ela não lê o header X-Idempotency-Key.
  • O PUT muda o que o comprador paga, a partir do próximo pagamento parcelado. Pedidos já pagos não mudam. O checkout que o comprador já tem aberto continua exibindo a tabela antiga até recarregar, mas a cobrança usa a tabela nova — os detalhes estão em Configurar Juro Adicional.
  • Não há teto de negócio no percentual — o quanto cobrar é decisão sua, e a API aceita a mesma faixa que o painel. O limite é técnico (99999999.99, o que cabe no campo) e o percentual aceita no máximo duas casas decimais.
  • Reativar é explícito. active omitido vale true, então um PUT sem active sobre uma tabela desligada volta 409 em vez de religar a cobrança em silêncio. Mande active: true para religar, ou active: false para mexer nos percentuais mantendo desligado. Com a tabela vigente, ou nunca configurada, omitir segue valendo true.
  • 409 tem três motivos, distinguíveis pelo detail: conta sem cadastro de recebimento concluído, recurso não habilitado para a conta, ou a reativação implícita acima. Nenhum dos três se resolve repetindo a chamada.
GET /public_api/fees/ passou a devolver customerFees. Campo novo, aditivo — a lista de taxas que a Cakto cobra do comprador nas vendas da sua conta, somadas ao total que ele paga e que não saem do seu repasse. Ver Taxas e Prazos.
  • Hoje traz uma linha: Taxa de serviço, amount "0.99", todos os métodos (paymentMethod: null), devolvida ao comprador no reembolso (refundable: true). O valor é por pedido, não por parcela, e não varia com o valor da venda.
  • Leia a lista, não assuma os R$ 0,99. Em parte das contas essa taxa é cobrada do produtor em vez do comprador, ou não é cobrada — nesses casos ela não aparece, e customerFees: [] é resposta válida.
  • É lista, e não um campo fixo, justamente para a composição poder mudar sem quebrar quem já integra.
  • Se você monta a tela de preço do comprador, o total dele é o produto mais amount. Se você calcula o seu líquido, customerFees não entra na conta.
Sobre os ganhos:
  • A resposta traz charged e earned lado a lado, e eles não são o mesmo número. charged é o juro que o comprador pagou nos seus pedidos; earned é a fatia que ficou com você depois do rateio por comissão. Num produto com coprodutor a 30%, earned é 70% de charged. Somar o juro dos pedidos e chamar de ganho superestima quem divide receita.
  • Os dois vêm como string decimal ("1000.00"), como todo dinheiro no contrato novo.
  • A janela é obrigatória por consequência: sem startDate/endDate a consulta responde os últimos 30 dias, e o período pedido não pode passar de 92 dias (400 acima disso). Os campos startDate e endDate da resposta ecoam a janela que foi realmente aplicada — confira, principalmente se você não mandou nenhuma das duas.
  • Só entram pedidos pagos, e uma consulta responde sobre uma moeda só (currency, padrão BRL).
  • O total de um período passado pode mudar. Não há registro de reversão: se um pedido daquele período for reembolsado ou sofrer chargeback depois, ele sai do total. Se você armazenar o valor, reconcilie em vez de tratá-lo como fechado.
Contrato de pagamentos, webhooks, produtos e taxas

Incompatível

initiate_checkout saiu do catálogo de webhooks. Ele era assinável e nunca foi entregue — é evento de pixel, disparado no carregamento do checkout, quando ainda não existe pedido nem pagamento. Assinar não dava erro e nada chegava. Não há substituto por webhook hoje.Só afeta quem tentou assinar esse evento; nenhuma entrega existente muda.

Corrigido

Nestes casos a documentação descrevia algo diferente do que a API sempre fez. O comportamento não mudou — a referência é que passou a dizer a verdade.
  • Campo de antifraude com o nome errado. Era publicado em camelCase; o contrato só aceita antifraud_profiling_attempt_reference, em snake_case. Quem copiava o exemplo recebia 400.
  • Obrigatoriedade errada do antifraude. Estava marcado como obrigatório nos cinco métodos de pagamento. Só é exigido em credit_card e threeDs.
  • Objeto pix com nomes errados. A referência dizia expiresAt e qrCodeBase64; o real é expirationDate e qrCode. expirationDate também não é date-time: vem como 2026-04-29 01:30:00+00:00, com espaço.
  • Resposta de criar produto descrevia o serializer errado, mostrando 7 campos onde a API devolve 59.
  • Listagem de checkouts documentava paginação dentro de paginação, envelope que a API nunca devolveu. O correto é um envelope só.

Adicionado

GET /public_api/fees/ — taxas e prazos da sua conta. Endpoint novo. Devolve, por método de pagamento, o percentual e o valor fixo cobrados por venda aprovada e o prazo de recebimento em dias, mais a tabela de juro-base do parcelamento no cartão de 1x a 18x. É a mesma informação da tela Taxas e Prazos do painel. Escopo payments, leitura. Ver Taxas e Prazos.
  • Corpo com dois campos: paymentMethods (lista de paymentMethod, percentage, fixed, releaseDays) e creditCardInstallments (lista de installments, interestPercentage).
  • paymentMethods traz sempre os dez métodos, na mesma ordem: pix, pix_auto, boleto, credit_card, threeDs, picpay, pagaleve, googlepay, applepay, openfinance_nubank. Método sem taxa vigente configurada vem com os três valores em null — null não é zero.
  • fixed vem como string decimal ("2.49"), e percentage e interestPercentage como number em pontos percentuais (4.99 é 4,99%). Não é inconsistência: é o padrão novo, explicado abaixo.
  • 409 tem um único motivo nesta operação: conta sem cadastro de recebimento concluído. 503 é falha temporária ao consultar as taxas, e pode ser repetida com backoff.

Padrão de valores, daqui pra frente

Nada muda no que já existe. Esta seção descreve a regra que passa a valer para o que for publicado a partir de agora, para você não ser surpreendido pela diferença.
A API não tinha regra sobre o tipo de valor: o mesmo produto sai "197.00" no POST que o cria e 197.0 no GET seguinte. A partir de agora, o que for publicado segue uma regra só:Dinheiro em string porque 0.1 + 0.2 não é 0.3 em ponto flutuante, e dinheiro não tolera esse erro. Percentual não sofre disso e continua number.GET /public_api/fees/ é o primeiro endpoint no padrão: por isso fixed é string e percentage é number no mesmo objeto.Os endpoints que já existem continuam como estão. Migrá-los muda o tipo de campos que integrações em produção já consomem, então será feito com aviso prévio aqui, numa entrada marcada Incompatível, com a lista completa dos campos afetados e o que fazer. Não há data.Se você está escrevendo uma integração nova agora, o mais seguro é converter todo valor monetário para um tipo decimal na entrada, aceitando tanto 2.49 quanto "2.49". Isso já é boa prática para dinheiro e torna a migração um não-evento para você.

Documentado

Campos e comportamentos que sempre existiram e não constavam na referência.
  • Cartão no enum de paymentMethod. credit_card e threeDs sempre foram aceitos, mas a referência listava só pix, pix_auto e boleto. Os objetos card e threeDSecure e o campo installments passaram a constar.
  • Pré-requisito de conta Cakto Banking. Cobrar por pix, pix_auto ou boleto exige conta aberta e ativa; cartão não exige. Ver Pré-requisitos.
  • Corpo entregue nos webhooks, campo a campo, incluindo o fato de data ter duas formas: checkout_abandonment não traz id nem status, e quebra handler que deduplica por data.id. Ver Webhooks.
  • Respostas de erro 400, 403 e 404 em 16 operações que não as declaravam.
  • Campos que a referência omitia: currency em ofertas e produtos, id no cliente do pedido e no order bump, errors no histórico de eventos de webhook, e mais oito campos de produto. Já vinham na resposta; agora estão descritos.