> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cakto.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Mudanças no contrato da API pública, da mais recente para a mais antiga.

<Info>
  **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.
</Info>

<Update label="4 de setembro de 2026" description="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](/conceitos/webhooks#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.
</Update>

<Update label="4 de setembro de 2026" description="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.

  * **[`GET /public_api/installment-interest/`](/api-reference/installment-interest/retrieve)**
    — a sua tabela de juro adicional.
  * **[`PUT /public_api/installment-interest/`](/api-reference/installment-interest/update)**
    — configura essa tabela.
  * **[`GET /public_api/installment-interest/earnings/`](/api-reference/installment-interest/earnings)**
    — quanto de juro foi cobrado e quanto ficou com você num período.

  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](/api-reference/installment-interest/update).
  * **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](/api-reference/fees/retrieve).

  * 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.
</Update>

<Update label="3 de setembro de 2026" description="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](/api-reference/fees/retrieve).

  * 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

  <Info>
    **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.
  </Info>

  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ó**:

  | valor           | contrato           | exemplo          |
  | --------------- | ------------------ | ---------------- |
  | dinheiro        | **string decimal** | `"2.49"`         |
  | percentual      | **number**         | `4.99` (= 4,99%) |
  | prazo, contagem | **integer**        | `15`             |

  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](/api-reference/payments/create-pix#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](/conceitos/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.
</Update>
