Pular para o conteúdo

Idempotência no Lote/Carnê

Plano: Personalizado

Novo No lote/carnê, o Token de Controle existe em dois níveis: o batch.tokenControleUsuario protege e recupera o lote inteiro, e o tokenControleUsuario de cada item de boletos[] protege cada boleto — o mesmo padrão de campo da Idempotência do boleto individual, um nível acima no JSON. Como em toda a API V1, reenviar o mesmo token retorna 409 (não é replay de 201/200); a recuperação do estado é sempre pela consulta.

O mesmo campo existe em dois níveis — não os confunda:

Por boleto Por lote/carnê
Campo boleto.tokenControleUsuario (individual) ou tokenControleUsuario em cada item de boletos[] batch.tokenControleUsuario (um por requisição, fora de boletos[])
Protege contra Boleto duplicado Lote inteiro duplicado
Recuperação Consulta individual por controle GET .../controle/{tokenControleUsuario}/situacao (esta página)
Documentação Idempotência Esta página

Os dois níveis podem (e devem) ser usados juntos: o token do lote fecha o cenário de timeout da requisição; os tokens por boleto continuam correlacionando cada parcela com o seu sistema.

Regras que valem para os dois níveis — por conta e case-insensitive, sempre ecoando a grafia persistida:

  • O namespace por boleto é um só: um token usado num boleto individual colide com o mesmo token num boleto de batch ou carnê (e vice-versa);
  • O namespace do lote é separado do de título, mas compartilhado entre batch e carnê;
  • Precedência: o conflito do token do lote é verificado antes da geração dos boletos — no reenvio de um lote inteiro, você recebe o 409 de lote, não o do primeiro título;
  • Criação atômica: conflito em qualquer título impede o lote inteiro — nunca há lote parcialmente criado. Por isso o 404 da consulta por token de controle significa “pode reenviar”.

Um POST /batch/boletos ou POST /carnes pode criar até 300 boletos de uma vez. Se a conexão cai ou o timeout estoura depois que a requisição chegou, seu sistema fica sem saber se o lote existe — e as duas reações ingênuas são ruins:

  • Reenviar às cegas: risco de criar o lote em duplicidade (cobranças duplicadas em massa);
  • Não reenviar: risco de não cobrar ninguém.
POST /batch/boletos (com batch.tokenControleUsuario) ──▶ timeout / falha de rede
│
▼
GET /batch/boletos/controle/{tokenControleUsuario}/situacao
│
├── 200 ──▶ o lote FOI criado: estado completo reconstruído
│ (token do lote, tokens e situação de cada boleto)
│
└── 404 ──▶ o lote NÃO foi criado: reenvie o POST com segurança

A criação do lote é atômica: ou o lote existe inteiro, ou não existe. Por isso o 404 da consulta por token de controle é um sinal confiável de “pode reenviar”.

Atributo Valor
Campo batch.tokenControleUsuario (nível do lote, fora de boletos[])
Endpoints POST /api/v1/batch/boletos e POST /api/v1/carnes
Tipo string
Tamanho máximo 44 caracteres
Caracteres permitidos Alfanuméricos e - _ =
Unicidade Por conta, sem diferenciar maiúsculas de minúsculas
Obrigatoriedade Opcional — sem o campo, o comportamento atual não muda

Formato inválido → 400 com o código estável A7C4B21E.

Quando informado, o token de controle é ecoado na resposta de criação — sempre presente no envelope (null quando não usado):

{
"batch": {
"token": "vSD9nA6iTFXhTComVKF-cQoY9Wq5RJkuJoTLPje8zVE=",
"tokenControleUsuario": "pedido-2026-0117",
"total": 2,
"boletos": [
"token-do-boleto-1",
"token-do-boleto-2"
],
"titulos": [
{ "token": "token-do-boleto-1", "numero": "12345678901", "documento": "PED-1", "codigoDeBarras": "23791...", "linhaDigitavel": "23790.50004 ...", "pixCodigoEMV": null, "tokenControleUsuario": "pedido-2026-0117-1" },
{ "token": "token-do-boleto-2", "numero": "12345678902", "documento": "PED-2", "codigoDeBarras": "23792...", "linhaDigitavel": "23790.50004 ...", "pixCodigoEMV": null, "tokenControleUsuario": "pedido-2026-0117-2" }
]
}
}

total é a quantidade de boletos criados (sempre igual ao tamanho de boletos[] e de titulos[]); boletos é a lista com o token (string) de cada boleto criado, alinhada por índice com titulos[], que resume cada boleto (inclusive o tokenControleUsuario por boleto, que é o outro nível). O restante do 201 segue o contrato das páginas de criação.

O reenvio de um token de controle já usado retorna 409 identificando o lote existente (nunca um boleto):

{
"erro": {
"status": 409,
"tipo": "conflito",
"causas": [
{
"codigo": "5C1D9F04",
"mensagem": "Campo (batch.tokenControleUsuario) - Conflito de dados, já existe um lote de boletos com a mesma chave de controle (pedido-2026-0117), localizado em (/api/v1/batch/boletos/vSD9nA6iTFXhTComVKF-cQoY9Wq5RJkuJoTLPje8zVE=/situacao).",
"suporte": "https://developers.boleto.cloud"
},
{
"codigo": "5C1DA015",
"mensagem": "Campo(batch.token):Conteúdo(vSD9nA6iTFXhTComVKF-cQoY9Wq5RJkuJoTLPje8zVE=)",
"suporte": "https://developers.boleto.cloud"
},
{
"codigo": "5C1DA015",
"mensagem": "Campo(batch.tokenControleUsuario):Conteúdo(pedido-2026-0117)",
"suporte": "https://developers.boleto.cloud"
}
]
}
}
Header Descrição
Location Rota canônica de situação do lote existente, decidida pelo tipo dele: /api/v1/carnes/{token}/situacao se for carnê, /api/v1/batch/boletos/{token}/situacao se for batch
X-BoletoCloud-Token Token do lote existente
X-BoletoCloud-Token-Controle-Usuario O token de controle, na grafia persistida

Não há header de nosso número: o conflito é de lote, que não tem um boleto único de referência.

Se o conflito for de um título (o tokenControleUsuario de um item de boletos[] já usado), o 409 identifica o boleto existente — mesmo molde do individual — e a criação do lote inteiro é abortada (atomicidade). Compare os dois conflitos:

409 de boleto 409 de lote
Identifica O boleto existente O lote existente
Headers X-BoletoCloud-Token + X-BoletoCloud-NIB-Nosso-Numero + X-BoletoCloud-Token-Controle-Usuario X-BoletoCloud-Token + X-BoletoCloud-Token-Controle-Usuario (sem nosso número)
Location O boleto individual existente A situação canônica do lote existente (batch ou carnê, pelo tipo dele)
Recuperação Situação individual GET .../controle/{tokenControleUsuario}/situacao
GET https://sandbox.boletocloud.com/api/v1/batch/boletos/controle/{tokenControleUsuario}/situacao
GET https://sandbox.boletocloud.com/api/v1/carnes/controle/{tokenControleUsuario}/situacao
  • Mesma resposta, semânticas e autenticação da consulta de situação por token — envelope batch com token, tokenControleUsuario, total, boletos[] e titulos[];
  • Use a rota da família em que o lote foi criado (batch ou carnê) — a canônica;
  • O valor na URL resolve em qualquer caixa; o envelope ecoa a grafia persistida;
  • 404 = o token de controle não existe para a sua conta → pode reenviar o POST com segurança.

Exemplo de 404 (token nunca usado):

{
"erro": {
"status": 404,
"tipo": "requisicao_para_recurso_nao_encontrado",
"causas": [
{
"codigo": "7B41C2E9",
"mensagem": "Lote de boletos com token (pedido-2026-0117) não encontrado.",
"suporte": "https://developers.boleto.cloud"
}
]
}
}

Na rota de carnê, o mesmo 404 usa o código 3F8A5D16 com a mensagem análoga (Carnê com token (...) não encontrado.) — a semântica “pode reenviar” é idêntica; se você trata códigos absolutos, trate o par.

Janela do terminal
# 1) Criar o lote com o token de controle
curl -v "https://sandbox.boletocloud.com/api/v1/batch/boletos" \
-H "Content-Type: application/json; charset=utf-8" \
-u "api-key_SUA-API-KEY:token" \
-d '{"batch": {"tokenControleUsuario": "pedido-2026-0117", "boletos": [ ... ]}}'
# 2) Em timeout/queda de rede, consultar pelo token de controle:
curl -v "https://sandbox.boletocloud.com/api/v1/batch/boletos/controle/pedido-2026-0117/situacao" \
-H "Accept: application/json" \
-u "api-key_SUA-API-KEY:token"
# 200 -> lote existe (token no corpo) | 404 -> reenviar o POST
Prática Motivo
Use um identificador do seu sistema (nº do pedido, contrato, fatura) Rastreabilidade sem armazenar tokens antes da resposta
Um token de controle por lote, nunca reaproveitado Reuso com conteúdo diferente resulta em 409 — é erro de integração
Grave o token de controle antes de enviar o POST É ele que permite recuperar depois de um timeout
Trate o 409 de lote como “o lote já existe” (siga o Location e reconcilie); o 409 de boleto significa que um boleto já existia e o lote novo não foi criado Discrimine pelo shape — não é falha
Combine com o token de controle por boleto Cada nível protege contra uma duplicidade diferente
Código Onde Descrição
201 Created POST Lote criado; token de controle ecoado no envelope
409 Conflict POST Token de controle já usado nesta conta (5C1D9F04/5C1DA015) — lote existente identificado no corpo e headers
400 Bad Request POST e GET Token de controle fora do formato (A7C4B21E no POST)
200 OK GET /controle Lote encontrado; mesma resposta da situação agregada
404 Not Found GET /controle Token de controle não existe para a sua conta → pode reenviar o POST
403 Forbidden GET /controle Conta sem o plano personalizado ativo