Respostas, validação e estados da API
Este guia detalha o comportamento da API pública da Calebe Pay: respostas HTTP, regras de validação, permissões e acompanhamento das cobranças. Ele complementa integration.md e o contrato openapi.json.
Respostas e retentativas
Não deduza o pagamento pelo HTTP: uma repetição idempotente pode retornar HTTP 200 com uma transação failed, unknown ou pending. Verifique sempre data.status, data.livemode, data.simulated e data.environment.
| HTTP / código | O que aconteceu | Ação correta |
|---|---|---|
201 |
Nova cobrança registrada e resultado inicial recebido | Salve ID e consulte estado |
200 + Idempotency-Replayed: true |
A mesma tentativa já existe | Use a transação retornada; não houve nova submissão |
202 / processing |
Tentativa reservada e processamento ainda não concluído | Aguarde e consulte; não crie outra chave |
202 / unknown |
O provedor pode ter criado a cobrança, mas a resposta é incerta | Consulte a tentativa existente; não envie uma nova cobrança |
400 invalid_json |
JSON inválido/nulo, campos duplicados/desconhecidos, valor incompatível ou mais de um objeto | Corrija o payload |
400 idempotency_key_required |
Header ausente ou fora de ^[A-Za-z0-9._:-]{1,128}$ |
Persista e envie uma chave válida |
401 unauthorized |
Chave ausente, revogada ou inválida | Corrija a autenticação; não tente em loop |
403 insufficient_scope |
A chave não permite a operação | Solicite uma chave com os scopes necessários |
403 tenant_suspended |
A conta, ou uma conta acima dela na hierarquia, está suspensa | Fale com a Calebe Pay |
403 live_payments_disabled |
Pagamentos reais ainda não estão liberados para este ambiente | Aguarde a liberação de produção pela Calebe Pay; não contorne a trava |
403 live_payment_out_of_scope |
A cobrança está fora da autorização de produção concedida à conta | Use exatamente os dados autorizados ou fale com a Calebe Pay; não troque a chave de idempotência |
403 live_key_cannot_simulate |
A simulação foi pedida com uma chave de produção | Simule com uma chave de teste (cp_test_); cobrança real só muda pelo provedor de pagamento |
403 live_key_required |
Cadastro de subtenant solicitado com chave de teste | Use uma chave de produção com subtenants:write |
403 origin_denied |
Chamada feita de um navegador com origem não autorizada | Chame a API a partir do seu servidor |
404 not_found |
ID inexistente ou fora do escopo | Confira o ID, a conta e o modo da chave; recursos de outra hierarquia e cobranças do outro modo (teste ou produção) também respondem 404 |
409 idempotency_conflict |
Mesma chave, payload diferente | Recupere payload original. Nova chave somente para nova tentativa comercial deliberada |
409 reconciliation_required |
Não há identificador externo para consultar o provedor | Não recrie a cobrança; acione o suporte com o ID txn_... |
409 provider_status_conflict |
A atualização externa regrediria um estado final | O estado local é preservado; acione o suporte se precisar revisar |
409 simulation_not_allowed |
A cobrança de teste já não está pendente, ou (boleto com paid_with: "pix") o Pix de teste do boleto já não está pendente ou passou do prazo |
Crie outra cobrança de teste para simular de novo; no boleto, simule o pagamento pelo boleto |
400 parent_id_not_allowed |
O cadastro público tentou escolher o pai do subtenant | Remova parent_id; o pai é sempre o tenant da chave |
413 request_too_large |
Corpo JSON ultrapassou 1 MiB | Reduza o corpo antes de reenviar |
422 validation_error |
Campo inválido | Corrija os dados; veja OpenAPI e a mensagem |
422 tenant_from_api_key |
A criação pública informou outro tenant | Remova tenant_id; use a chave emitida para a conta alvo |
422 provider_not_configured |
O método solicitado não está habilitado para a conta | Fale com a Calebe Pay para habilitar Pix ou boleto |
422 provider_not_available |
O provider informado no cadastro não está ativo e roteado para a árvore | Omita provider_id para seleção automática ou corrija o roteamento |
422 provider_method_disabled |
O método foi desabilitado para a conta | Fale com a Calebe Pay; não há o que corrigir na requisição |
422 subaccount_not_configured |
O cadastro financeiro da conta ainda não está ativo | Fale com a Calebe Pay; a conta não herda o cadastro da conta acima dela |
422 subaccount_credentials_missing |
O cadastro financeiro da conta está sem credencial utilizável no ambiente | Fale com a Calebe Pay; não há o que corrigir na requisição |
422 provider_pix_sandbox_unsupported |
Pix solicitado em um sandbox remoto, que não existe para esse método | Use o simulador ou a produção autorizada; boleto possui sandbox remoto |
422 simulator_disabled |
O ambiente não aceita cobranças simuladas | Confirme com a Calebe Pay qual ambiente a sua chave usa |
422 amount_limit_exceeded |
Valor acima do limite por cobrança da conta | Solicite a revisão do limite; não divida a cobrança automaticamente |
422 tenant_fee_sync_pending |
Sincronização de tarifas ainda pendente; sem nova reserva | Aguarde e confira novamente; preserve tentativas já registradas |
422 tenant_fee_sync_blocked |
A sincronização de tarifas requer correção; sem nova reserva | Solicite revisão à Calebe Pay; não gere outra chave para tentativas já registradas |
422 split_* |
Recebedores ou taxas da divisão automática incompatíveis; sem nova reserva | Solicite revisão da configuração à Calebe Pay; preserve pedidos anteriores |
409 tenant_hierarchy_invalid |
Árvore de tarifas inconsistente | Solicite revisão da configuração à Calebe Pay |
422 fee_exceeds_amount |
Soma das tarifas vigentes do método maior que o valor da nova cobrança; emissão recusada | Fale com a Calebe Pay para revisar a tarifa |
429 rate_limit_exceeded |
Limite por credencial/IP atingido | Aguarde no mínimo 60 s, com jitter; preserve chave/payload |
502 provider_rejected |
Rejeição conhecida na criação; registro local failed preservado |
Consulte GET pelo error.transaction_id e os diagnósticos; a mesma chave/corpo recupera o registro sem reenvio. HTTP 200 no replay não significa sucesso financeiro |
502 provider_refresh_failed |
Consulta ao provedor falhou e nada foi aplicado | Estado local preservado; repita a consulta com espera progressiva. No boleto, se a consulta do boleto ou a do Pix que o acompanha aplicou uma mudança, a resposta é 200 com o estado novo e a outra é repetida em segundo plano |
503 payment_settings_unavailable |
O controle de pagamentos em produção está indisponível | Emissão e refresh em produção ficam bloqueados; repita mais tarde com a mesma chave |
503 rate_limiter_unavailable |
O controle de acesso está temporariamente indisponível | Só ocorre em login, MFA, senha, QR do app, checkout público e site: esses acessos falham de forma fechada. Chamadas autenticadas (chave de API ou sessão) seguem funcionando. Repita mais tarde com a mesma idempotência |
503 reconciliation_required |
A solicitação foi reservada, mas o resultado não pôde ser gravado | Preserve a chave e consulte a transação; não reprocesse o pagamento |
500 internal_error ou falha de rede |
Resposta não confirmada | Preserve a chave, consulte e repita somente a tentativa original |
Endpoints inexistentes e métodos HTTP incorretos podem receber uma resposta padrão do roteador, que não é o envelope JSON de erro da aplicação.
O serviço não garante Retry-After. O SDK o expõe quando presente, mas o integrador deve ter uma espera mínima própria para 429. Um backoff recomendado para consultas é 5, 10, 20 e 30 segundos com jitter, respeitando o rate limit. Não faça retries automáticos que troquem a chave idempotente.
Validação e normalização
- Corpos JSON aceitam somente os campos documentados. JSON nulo, objetos adicionais e campos duplicados (inclusive variantes de maiúsculas/minúsculas) são recusados com 400. O limite de 1 MiB é aplicado ao corpo completo; excesso retorna 413.
amountdeve ser um inteiro JSON. Frações ou strings são rejeitadas.reference,descriptione nome do pagador têm espaços externos removidos.- Documento recebe trim e conversão para maiúsculas. Aceita 11 ou 14 caracteres alfanuméricos sem pontuação. Esta validação de formato não confere dígitos verificadores nem comprova identidade.
- E-mail recebe trim e conversão para minúsculas. A validação básica exige
@e até 254 bytes; o provedor pode impor critérios adicionais. - Novas cobranças Pix exigem telefone não vazio, somente dígitos após trim e no máximo 20 bytes no valor original, 10 ou 11 dígitos com DDD e sem código do país quando a regra
customer_phone_lengthestá ativa (padrão). Boleto exige telefone não vazio de até 20 bytes após trim. Nome tem 2 a 120 bytes; referência, 1 a 100; descrição, 1 a 200. Os limites contam bytes UTF-8: acentos podem ocupar mais de um byte. - Novos Pix e boletos exigem endereço: CEP de 8 dígitos, logradouro de 2 a 120 bytes, número de 1 a 20, bairro/cidade de 2 a 80 e UF brasileira válida. Complemento é opcional, até 120 bytes. Em Pix, trim e UF em maiúsculas são aplicados em uma cópia para validar e preparar o corpo remoto; o telefone/endereço originais continuam no hash e no snapshot da API.
- Omissão ou zero em
expires_inusa 3.600; valores explícitos de 60 a 86.400 definem a duração. - A comparação idempotente usa esse objeto estruturado normalizado, incluindo tenant e expiração. A ordem das propriedades JSON não afeta o hash.
- A chave idempotente fica vinculada à transação e não expira automaticamente.
Replays Pix de registros anteriores continuam aceitos com o corpo e a chave originais, mesmo sem telefone/endereço; não reenviam ao provedor. Alterar o corpo na mesma chave retorna 409 idempotency_conflict. Os novos requisitos são aplicados a novas emissões antes da reserva/chamada externa. O SDK createPix é estrito; uma repetição legada deliberada usa request de baixo nível com o corpo original salvo, sem preencher dados nem gerar nova chave. O GET local permanece disponível.
Permissões e hierarquia
Cada usuário do painel tem um perfil, e o perfil é uma lista de permissões granulares, como ver transações, criar cobranças, revogar chaves ou cadastrar clientes. A tabela abaixo mostra os perfis do sistema como vêm de fábrica.
- O super admin cria perfis com qualquer combinação de permissões e ajusta os perfis do sistema, menos o próprio super admin, que tem sempre todas.
- Mudar as permissões de um perfil vale na requisição seguinte de quem o usa, sem novo login.
- Quem cadastra usuários só entrega perfis com permissões que também tem.
As chaves de API não seguem o perfil editado: valem pelos scopes e pelo access_profile da tabela.
| Capacidade | Administrador da conta (tenant_admin) |
Desenvolvedor (developer) |
Leitura (viewer) |
Chave de API |
|---|---|---|---|---|
| Ler transações | Própria conta e contas abaixo | Própria conta e contas abaixo | Própria conta e contas abaixo | Scope transactions:read; listagem das contas abaixo por opt-in |
| Criar Pix, boleto, cartão e checkout | Própria conta ou conta abaixo permitida | Própria conta ou conta abaixo permitida | Não | Scope de escrita de cada método; somente na conta da chave |
| Cadastrar subtenant e tentar conversão | Emite a chave padrão da própria conta | Não | Não | Chave padrão de produção com subtenants:write; o tenant da chave é o pai |
| Atualizar estado pelo provedor | Própria hierarquia | Não | Não | transactions:read + scope de escrita do método; própria hierarquia |
| Simular pagamento ou expiração de cobrança de teste | Própria hierarquia | Não | Não | Somente chave de teste: transactions:read + scope de escrita do método |
| Consultar e emitir chaves | Própria hierarquia | Própria hierarquia | Não | Não |
| Revogar chaves | Própria hierarquia | Não | Não | Não |
| Documentação | Sim | Sim | Sim | Pública |
| Monitor de integrações | Não | Metadados das cobranças da própria hierarquia; sem request/response brutos | Não | Não |
| Ler logs e notificações | Própria hierarquia | Não | Própria hierarquia | Não |
O perfil desenvolvedor sempre pertence a um tenant e não administra usuários, tenants, tarifas, provedores, webhooks ou transferências. Sua sessão também não estorna, cancela ou captura cobranças. Ele mantém o autoatendimento da própria conta, como senha, MFA e sessões conectadas.
Um tenant pode consultar o total de tarifas cobrado dele e dos tenants abaixo. Não recebe valores, componentes ou identidades de tarifas dos níveis superiores. A composição interna e a taxa configurada no provedor são restritas à administração global. As prévias e transações de perfis vinculados a tenant mostram somente o total da operação e o saldo do estabelecimento.
Chaves emitidas por quem não tem a permissão de verificar e simular cobranças, como o perfil desenvolvedor, recebem access_profile: "developer" e ficam restritas à criação e consulta de cobranças. Essa restrição vale também quando usadas diretamente pela API: scopes de escrita não liberam refresh, simulação, cancelamento, estorno ou captura. Chaves standard mantêm as permissões da tabela. O perfil da chave é definido pelo servidor e não pode ser promovido.
O cadastro público de subtenant é uma operação de produção e só aceita chave standard com subtenants:write. A conta autenticada vira o pai; parent_id é rejeitado. O provider_id pode ser omitido para seleção automática. Na conversão, as taxas padrão do provider completam somente os meios de pagamento ausentes em registration.MerchantSplit; omitir o bloco usa todos os padrões cadastrados. Interprete sempre conversion_status: apenas converted confirma a subconta, enquanto creating e unknown exigem preservar a mesma tentativa idempotente.
Uma chave de teste (cp_test_) opera só sobre cobranças de teste, e uma de produção só sobre as de produção, com as mesmas permissões acima. As contas formam uma árvore: uma software house, por exemplo, enxerga os clientes abaixo dela. Se uma conta for suspensa, as credenciais dela e das contas abaixo deixam de funcionar. Consultas a recursos de outra árvore usam 404 para não expor sua existência.
No GET público de uma transação específica, o pai pode consultar um filho sem o parâmetro include_descendants. Esse parâmetro controla a listagem. Com tenant_id na listagem, o tenant indicado passa a ser a raiz do filtro; include_descendants=true inclui a subárvore dele.
Acompanhar e atualizar uma cobrança
A tentativa é registrada como processing antes da chamada ao provedor, e a chave de idempotência é única por conta, inclusive entre chamadas concorrentes. A chamada ao provedor continua por um prazo limitado mesmo se o cliente desconectar.
Se uma tentativa ficar em processing ou unknown, não crie outra. Leia o registro por GET, use /refresh quando houver provider_reference e, se a API responder reconciliation_required, acione o suporte com o ID txn_... e o request_id: a cobrança pode existir no provedor, e uma nova emissão duplicaria o pagamento.
Diagnóstico da criação e da consulta
Transações incluem provider_error_code, provider_error_message e provider_http_status, todos nullable. O código remoto aceita somente string de 1 a 10 dígitos; a mensagem é uma orientação estática segura da Calebe Pay, de até 400 caracteres; HTTP remoto aceita 100 a 599. Textos brutos do provedor, tokens, QR Code e dados pessoais nunca são gravados nesses campos. Registros anteriores a esse recurso têm valores nulos.
Uma recusa confirmada na criação retorna 502 provider_rejected e error.transaction_id, mesmo quando o provedor responde HTTP 200 e o status da cobrança indica recusa. Resultado incerto permanece 202 com data.status: "unknown"; os diagnósticos não transformam timeout em certeza de falha. Replay de failed/unknown retorna 200 com o registro existente; apenas processing continua 202 no replay. Nunca há reenvio ao provedor nesse replay.
Os erros de pagamento podem incluir transaction_id e os três diagnósticos diretamente no objeto error, além de code, message e request_id. Validações anteriores à reserva não possuem ID local de transação. request_id continua exclusivo da requisição/auditoria, não do DTO de transação. Se uma proteção local impedir o envio depois da reserva, a mensagem identifica bloqueio local e o HTTP remoto permanece nulo.
Uma falha da consulta ao provedor no refresh retorna os diagnósticos daquela consulta em error, com o ID local, mas preserva o estado e o diagnóstico de criação já salvos. Não confunda a recusa de uma consulta atual com o motivo que originou uma cobrança anterior. GET local nunca faz chamada externa e é o primeiro passo para ler o registro existente.
Se os detalhes da recusa antiga não foram capturados, o diagnóstico permanece nulo. Não há como reconstruí-lo a partir do valor, do horário ou do status genérico failed. Um novo POST financeiro cria outra tentativa e não recupera o erro histórico; não use uma nova chave apenas para investigar.
JSON de criação e corpo preparado para envio
GET /v1/transactions/{id}/request consulta dois registros guardados na criação: request, o corpo normalizado com a conta resolvida, e provider_request, o corpo preparado para o provedor. Ambos são persistidos na reserva, antes de uma possível chamada externa. A resposta usa source: "captured" e missing_fields: []; provider_request é null para o simulador. Isso não prova que a transmissão ocorreu, que o provedor aceitou o pedido ou que houve pagamento.
Registros anteriores retornam source: "legacy_partial": somente campos recuperáveis, como valor, referência e pagador conhecido, sem reconstruir o payload remoto ou supor opções antigas. missing_fields descreve as lacunas, incluindo expiração Pix ou opções de boleto não armazenadas. Consulte o contrato e os campos exatos. Não crie uma nova cobrança para preencher esse histórico nem use o objeto parcial como payload de retry.
A autorização é a mesma da leitura de transação: scope transactions:read para chave e isolamento pela hierarquia, com 404 fora do escopo. Tokens, headers e chave de idempotência não fazem parte dos snapshots expostos; dados pessoais do pagador fazem. Os corpos ficam no endpoint de detalhe, não nas listagens gerais. Restrinja acesso, cópias, retenção e exportação desses dados; não registre o JSON completo em logs ou chamados de suporte.
Como o refresh se comporta
O refresh preserva estados finais: paid só avança para refunded; expired só avança para paid, quando o provedor de pagamento confirma um pagamento tardio; failed e refunded não reabrem. Respostas que regrediriam o estado são recusadas. A atualização é condicionada ao estado anterior, para que consultas concorrentes não sobrescrevam uma confirmação mais recente.
O refresh segue o mesmo caminho da conciliação automática: aplica a mesma regra de prazo do Pix e gera o mesmo evento transaction.updated quando o status muda. Em uma cobrança já expired cujo provedor de pagamento ainda não registra pagamento, o refresh responde 200 sem alterar nada.
Na consulta ao provedor, ID externo, valor exato e referência interna txn_... precisam coincidir com o registro local. Uma divergência retorna provider_refresh_failed sem alterar o estado. Um status externo não reconhecido não é tratado como confirmação de cobrança pendente ou paga.
No simulador e no modo de teste, a transação pendente expira sozinha quando o prazo passa; /refresh aplica a mesma regra, sem consultar o provedor de pagamento.
Fora do simulador, um Pix pendente é conferido com o provedor de pagamento poucos minutos depois de expires_at e só então passa a expired — ou a paid, se o pagamento tiver acontecido. Enquanto o provedor de pagamento não responde, a cobrança continua pending.
Em boleto, due_date é uma data de calendário. No simulador, cancel_after_due=true calcula expiração no início do dia posterior ao vencimento mais days_before_cancel, em São Paulo; sem essa opção expires_at é nulo. Fora do simulador, a consulta confirma o status bancário. Boleto real nunca é expirado pelo prazo local. O Pix que acompanha o boleto deixa de valer no fim do dia do vencimento sem mudar o status do boleto; pago esse Pix, a cobrança passa a paid com paid_with: "pix" (veja Pix no boleto). Um boleto em processing indica intenção de pagamento, sem comprovar liquidação. Artefatos de boleto recebidos são validados: URL HTTPS, linha de 47–48 dígitos, código de 44 dígitos e vencimento esperado.
Saúde, limites e rastreabilidade
GET /healthindica que o serviço está respondendo.- Toda resposta traz o header
X-Request-ID, repetido emerror.request_id. Informe esse valor ao falar com o suporte. - O limite é de 120 requisições por minuto por credencial. Acima disso a API responde
429 rate_limit_exceeded.
Limites desta versão
A API pública cobre Pix, boleto e cartão de crédito quando habilitados para a conta, checkouts, assinaturas com Pix Automático, centros de custo, o cadastro de subtenants e a consulta de tudo isso, além de webhooks de saída para as mudanças de status. A lista completa, com o escopo de cada operação, está em Tudo o que dá para fazer pela API. Estornos integrais e alteração de vencimento estão disponíveis nos endpoints abaixo. Não há endpoints públicos de cancelamento ou captura posterior: confirme o estado no seu backend, pelo webhook assinado ou por consulta. O estado de uma cobrança não representa saldo disponível, e nem o modo de teste nem o simulador certificam uma integração financeira real.
Estornos e vencimento
POST /v1/transactions/{id}/refund solicita estorno integral de cartão, Pix ou boleto pago. Envie Idempotency-Key e {} para cartão/Pix. Exige transactions:read e a escrita do método original (pix:write, credit_card:write ou boleto:write). Chaves com access_profile: developer não podem usar essas operações. No painel, a permissão é charges.manage.
Boleto quitado pela linha digitável exige uma conta bancária do próprio pagador:
{
"bank_data": {
"bank_code": "001",
"agency": "0001",
"agency_digit": "",
"account": "12345",
"account_digit": "9",
"account_type": "CC"
}
}
CC é conta corrente; PP, poupança. Boleto quitado pelo Pix acoplado (paid_with: pix) é estornado pelo Pix e aceita {}; não informe dados bancários nesse caso.
A resposta pode ser 202: refund.status será processing, pending ou unknown, e a cobrança continua paid até uma consulta autenticada confirmar refunded. Acompanhe a leitura local (GET /v1/transactions/{id}) e os eventos transaction.updated, que também informam a situação da operação. Se o banco devolver o pedido para o estado pago depois de processá-lo, a operação fica failed, com refund_not_completed, e uma nova solicitação deliberada pode ser feita.
PATCH /v1/transactions/{id}/boleto altera somente um boleto pending:
{ "due_date": "2026-10-20" }
Exige Idempotency-Key, transactions:read e boleto:write. Escolha uma data válida entre hoje e +365 dias em Brasília, diferente da atual. Enquanto boleto_update.status estiver em confirmação, boleto_due_date continua sendo o vencimento confirmado. O PDF e a página do pagador passam a usar a nova data depois da confirmação e da conferência dos artefatos. O Pix existente não é reemitido nem prorrogado; se antecipar o boleto, ele deixa de ser exibido no novo prazo.
As duas operações usam a conta/subconta que emitiu a cobrança. Mesma cobrança, operação, corpo e chave consultam o pedido registrado sem reenviar o comando; corpo diferente na mesma chave retorna 409 idempotency_conflict. Uma operação em confirmação bloqueia outra chave com 409 operation_in_progress. Recusa confirmada retorna 502 provider_operation_rejected; timeout mantém o resultado incerto. Consulte a cobrança antes de decidir por nova tentativa.
No modo de teste, a conclusão é local e gera o mesmo webhook, sem movimentação financeira. Estornar um ciclo não cancela a assinatura; alterar o vencimento de seu boleto não altera o calendário dos próximos ciclos. Abra Assinaturas → Ciclos → Gerenciar cobrança para essas ações.