Divergências conhecidas¶
Para: Dev — Conferido em 2026-10-19.
Esta página lista, sem esconder, onde o Dracma de hoje faz algo diferente do Contrato v1 1.1.0, do documento OpenAPI servido ou de outras páginas do guia. As outras páginas descrevem o que a API faz hoje e apontam para cá quando há diferença.
O que é¶
Cada divergência tem um id estável (D-A1, D-D2...) e três colunas:
| Coluna | Conteúdo |
|---|---|
| Contrato diz | o que o contrato 1.1.0, o OpenAPI ou um documento irmão afirma |
| Dracma faz hoje | o comportamento real, conferido em 2026-10-18 |
| Seu código | o que o conector deve fazer para funcionar nos dois mundos: hoje e depois de uma correção. Quase sempre é seguir o comportamento mais restritivo |
Uma divergência sai da lista, e vai para o histórico no fim, no mesmo momento em que o comportamento ou o contrato muda.
Quando usar¶
- Ao implementar o conector, leia a coluna "Seu código" de cada linha da sua área.
- Ao abrir um chamado com o suporte ou ao comentar uma regra no código, cite o id (
D-W9, por exemplo). - Ao ver o Dracma agir diferente do contrato, procure o assunto aqui antes de supor um defeito.
Autenticação, transporte e erros¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-A1 | §3.6: chave de uma operação com certificado de outra operação resulta em 401 |
mTLS é opcional e, quando ligado, o certificado é verificado na borda, com uma lista de certificados confiáveis por instância. A API não cruza chave × certificado | nunca misture chave e certificado de operações diferentes; trate 401 como possível. Veja Rede, allowlist e mTLS |
| D-A2 | 5 req/s para /events e /reconciliation (por operação) |
balde de listagem de 5 req/s por chave para GET /deposits, GET /withdrawals, GET /events e GET /receipts (sem id). GET /reconciliation usa o balde geral de leitura |
pagine /events e /reconciliation devagar (no máximo 5 req/s); faça o fetch-back sempre por id |
| D-A3 | o catálogo de erros não tem 413 |
corpo acima de 64 KiB resulta em 413 com code: validation_failed e reason: too_large |
trate 413 como um 4xx de validação |
| D-A4 | o nonce é "32 hex" | exige minúsculas (^[0-9a-f]{32}$); maiúsculas resultam em 401 invalid_signature |
gere hexadecimal em minúsculas |
| D-A5 | §4.3: Dracma-Event-Id diferente do id do corpo resulta em 400 |
um documento irmão (SEGURANCA I8) diz 401; é ele que está errado |
responda 400 |
| D-A6 | não consta no contrato | 20 falhas de assinatura por minuto do mesmo IP: as seguintes viram 429 rate_limited |
corrija a assinatura; nunca repita um 401 em laço |
| D-A8 | 503 psp_unavailable e outside_operating_hours |
não são emitidos; a indisponibilidade sai como 503 service_unavailable |
trate todo 503 igual: repita com a mesma chave depois do Retry-After |
| D-A9 | limites por jogador não constam no contrato | POST /deposits: 10 depósitos criados por jogador por hora. POST /withdrawals: 3 saques pedidos por jogador por hora. Acima disso: 429 rate_limited com Retry-After |
mostre "tente mais tarde" ao jogador; no saque, mantenha a reserva e repita com a mesma chave depois do Retry-After |
Idempotência e corpo¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-I2 | chave "em processamento" | a reserva da chave dura 30 s; depois disso, outra execução pode assumir | não faça duas chamadas concorrentes com a mesma chave |
Depósitos¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-D2 | POST /deposits/{id}/cancel cancela o depósito |
muda só o estado no Dracma. Não existe comando para cancelar a cobrança no PSP: o PIX continua pagável até expirar | depósito cancelled com paid_amount preenchido: não credite e trate como exceção |
| D-D3 | paid → refunded e paid → chargeback |
o gatilho existe só no sandbox: POST /v1/sandbox/deposits/{id}/refund e /chargeback (rotas aditivas; só de paid, outro estado resulta em 409; emitem deposit.refunded e deposit.chargeback). Em operação Real não há gatilho |
implemente o tratamento (débito por política, exceção para decisão humana) e teste-o no sandbox com essas rotas |
| D-D5 | catálogo de eventos | não existe deposit.created; o primeiro evento é deposit.pending com object_version 2 |
não espere evento de criação |
| D-D6 | §6.1 não tem transição para pagamento depois de estado terminal | pagamento em expired, cancelled ou failed: o estado não muda; paid_amount, paid_at e o pagador são gravados e o Dracma alerta |
nunca credite fora de paid; paid_amount em estado terminal é exceção para decisão humana |
| D-D7 | REGRAS §12 (catálogo de motivos) | emite também RULES_UNAVAILABLE e RULES_INVALID_RESPONSE, em review_reasons e em decision.reasons |
leitor tolerante: motivo desconhecido não muda nada |
| D-D8 | Documento.type aceita PASSPORT, NATIONAL_ID e TAX_ID |
em PIX só CPF e CNPJ: os outros resultam em 422 document_invalid (depósito) ou em bloqueio (saque) |
em PIX envie só CPF ou CNPJ |
Exigências por operação¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-R1 | player.email e player.name opcionais; Moeda sem method_details; Problema.errors[].reason sem os motivos novos |
GET /currencies traz method_details[] (method, deposit_requirements, psp_notes), um campo aditivo que está no openapi.yaml servido (schema DetalheMetodo) e não no contrato 1.1.0. POST /deposits recusa com 400 (required_by_operation, format, max_length, reserved_domain_required) antes de criar objeto. O PSP de depósito da operação define quais campos são exigidos. player.email, se vier, é sempre validado; player.name acima de 120 resulta em 400 |
leia as exigências em GET /currencies; mande os campos; trate o 400 como recusa |
| D-R2 | o corpo de depósito que o seu front monta (por exemplo {amount, method, cpf}) |
a operação pode exigir player.name e player.email no pedido ao Dracma |
o conector preenche nome e e-mail a partir do cadastro do jogador, no servidor, nunca do corpo que o front envia |
Saques¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-W1 | approved → processing = "enviado ao PSP" |
vira processing no primeiro retorno do PSP; envio recusado antes de sair (caixa, guarda de ambiente) deixa em approved e alerta |
approved pode durar; mantenha a reserva |
| D-W2 | approved → cancelled permitido (cancelar "só antes de processing") |
com o comando de envio já publicado, cancelar resulta em 409 (evita pagar e devolver) |
trate 409 no cancelamento como "espere o desfecho" |
| D-W3 | o diagrama §6.2 não desenha requested → cancelled |
permitido (o texto do §6.2 e o OpenAPI permitem) | — |
| D-W4 | Decisao.mode obrigatório |
em requested (consulta pendente ao diretório do PIX): mode: "manual", reasons: [], decided_at: null |
não conclua nada de mode em requested |
| D-W5 | catálogo de eventos | não existe evento público withdrawal.requested |
— |
| D-W6 | DevolucaoBancaria completa: reason, return_fee, returned_amount, returned_at, reason_code |
em returned por PIX, o objeto return traz só reason_code, returned_amount e returned_at. reason_code pode ser unspecified, e returned_amount pode ser menor que o valor do saque (devolução parcial, que continua returned) |
não dependa de reason nem de return_fee; use returned_amount. Veja Saques |
| D-W7 | ações de sandbox mudam o estado | pay e fail respondem 200 com o estado atual; o desfecho chega por evento |
espere o webhook ou faça o fetch-back |
| D-W8 | player.ip opcional |
sem player.ip o saque nunca é automático: under_review com OWNERSHIP_UNVERIFIED |
mande o IP do jogador em todo saque |
| D-W9 | §2: POST /withdrawals responde 202 se o PSP demorar |
sempre 201: a criação não espera o PSP |
trate 201 e 202 igual |
| D-W10 | motor de regras fora: erro (implícito) | na criação do saque: 201 under_review com RULES_UNAVAILABLE (na dúvida, revisão). No depósito: 503 |
mantenha a reserva em under_review |
| D-W11 | payout_currency diferente da moeda do valor: 422 fx_not_enabled |
purpose, fx_quote_id e payout_currency são aceitos e ignorados; o saque sai em BRL |
não envie esses campos em PIX |
Reconciliação e leitura¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-C1 | digest: itens ordenados por object_id |
empate (o mesmo object_id em dois itens) desempatado por kind crescente |
ordene por (object_id, kind) |
| D-C2 | — | não há vetor normativo do digest nos vetores de assinatura | use o exemplo calculado em Reconciliação para conferir o seu código |
| D-C3 | GET /rules sem currency |
usa a primeira moeda habilitada da operação | — |
| D-C4 | pending_in do caixa |
calculado pela API (depósitos recebidos e ainda em revisão ou retenção), não vem do livro | use só para acompanhamento |
| D-C5 | Reconciliacao.totals cobre os movimentos do dia |
não há total de return: o item return conta em count, mas não em nenhuma soma |
some os itens return à parte. Veja Reconciliação |
| D-C6 | kind: return é "wire/SWIFT devolvido ao caixa" |
return é emitido hoje para depósito returned e para saque PIX returned. fee, recall e receipt não são emitidos |
trate return como movimento de PIX; aplique o leitor tolerante nos demais |
| D-C7 | tarifa em Reconciliacao.items.fee e totals.fees |
fee do item é sempre null e totals.fees é "0": o Dracma não desconta tarifa do crédito |
não espere tarifa no relatório. As tarifas são por contrato |
Webhooks¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-H2 | omisso sobre redirecionamento | 3xx conta como falha (reentrega) |
responda 2xx direto na URL cadastrada |
| D-H3 | lista de IPs de saída publicada e assinada | não há lista publicada nem endpoint, e o Dracma não garante IP fixo de origem | assinatura e fetch-back são a defesa; não filtre o webhook por IP de origem |
Sandbox e conformidade¶
| Id | Contrato diz | Dracma faz hoje | Seu código |
|---|---|---|---|
| D-S1 | o OpenAPI do contrato 1.1.0 não declara Idempotency-Key nas rotas /sandbox/* |
a chave é exigida em todo POST. O openapi.yaml servido a declara |
mande uma chave nova em cada ação de sandbox |
| D-S2 | §10: a suíte de conformidade fica numa pasta do repositório do operador de exemplo | a suíte é do Dracma e é entregue no onboarding. Veja Sandbox | — |
| D-S3 | o OpenAPI do contrato 1.1.0 (cópia fiel) traz um servidor de teste local que não é do Dracma e um e-mail de exemplo de domínio real | o openapi.yaml servido não tem esse servidor nem esse e-mail (usa jogador1@operador.test) |
ignore o servidor e o e-mail da cópia do contrato; fora de produção use TLD reservado (.test, .invalid, .example) |
| D-S4 | a allowlist de IP vazia significa "sem acesso" | a lista de IPs da operação é opcional: lista vazia significa sem restrição, em Teste e em Real. Com ao menos um IP, a chamada de outra origem recebe 403 ip_not_allowed |
cadastre a lista (altamente recomendado, principalmente em Real) quando o IP de saída for fixo. Veja Rede, allowlist e mTLS e IPs do conector |
IP liberado é opcional
Sem IP liberado, nenhuma chamada é recusada por origem. Com IP liberado, a origem fora da lista recebe 403 ip_not_allowed.
Proteja o conector com a assinatura, o escopo da chave e o segredo guardado só no servidor.
Resolvidas (histórico)¶
| Id | O que era | Como ficou |
|---|---|---|
| D-D1 | o executor não publicava charge.paid quando valor ou pagador divergiam (o depósito ficava pending) |
corrigido: o pagamento sempre chega com os valores reais e a API decide under_review |
| D-S0 | a suíte de conformidade chamava /sandbox/* sem Idempotency-Key |
corrigido: a suíte gera uma chave nova em todo POST |
| D-H1 | §5: backoff exponencial com jitter; o Dracma usava intervalos fixos, sem jitter | corrigido: a reentrega soma um jitter positivo ao intervalo base (de 0 a 10 % do atraso). Veja Retentativas, skipped e DLQ |
| D-W6 (antes) | paid → returned quando o banco devolve: só a ação de sandbox return aplicava; o executor não publicava devolução real |
corrigido em 2026-10-10: o Dracma consome a devolução do PSP e leva o saque pago a returned em operação Real, com o objeto return. A divergência atual de D-W6 é outra (campos da devolução) |
| D-OA | rotas fx, receipts, recall e bank-transfers fora do documento servido (404 de rota inexistente) |
declaradas e implementadas no recorte PIX em 2026-10-02. Veja Rotas e campos reservados |
| D-O1 a D-O7 | o documento OpenAPI servido citava um operador de exemplo pelo nome e um e-mail de domínio real; trazia servers com mTLS obrigatório e host sandbox.*; o 401 do webhook como "o Dracma não insiste"; rules.updated no enum de eventos; method_options e fx_quote_id no CriarDeposito; schema vazio em GET /sandbox/webhooks; Reconciliacao sem o caso de return |
corrigido na fonte (dracma_api, 2026-10-19): o documento servido descreve o comportamento real, e a cópia do guia foi sincronizada. Veja OpenAPI |
| D-I1 | OpenAPI: operation_id no corpo "é ignorado se existir" |
corrigido no documento servido: ignorado só na raiz; aninhado resulta em 400 unknown_field. Continue sem enviá-lo |
| D-D4 | OpenAPI CriarDeposito tinha method_options e fx_quote_id |
removidos do documento servido; a API continua recusando os dois com 400 unknown_field (existem só no contrato 1.1.0) |
| D-R3 | os exemplos do OpenAPI usavam um e-mail de domínio real | o documento servido usa jogador1@operador.test; fora de produção só TLD reservado, como antes |
| D-H4 | o OpenAPI mostrava o 401 do webhook como "o Dracma não insiste" |
corrigido no documento servido: qualquer resposta que não seja 2xx é reentregue por até 72 h. Responda 2xx ao que já tratou |
| D-H5 | o OpenAPI listava rules.updated entre os tipos de evento |
removido do documento servido; o Dracma nunca emitiu esse evento |
Exemplos¶
Como citar uma divergência num chamado ao suporte:
Assunto: D-W9 em POST /v1/withdrawals
Operação: <operation_id>
Dracma-Trace-Id: <trace_id da resposta>
Esperado pelo contrato: 202 quando o PSP demora.
Observado: 201 com status approved.
Como proteger o conector nos dois mundos (D-W9, D-A8):
se status in (200, 201, 202): tratar como aceito e seguir pelo estado do objeto
se status == 503: repetir com a mesma Idempotency-Key depois do Retry-After
Erros e casos de borda¶
- A divergência não está aqui. Se o comportamento difere do guia e não há um id, abra um chamado com o
trace_idda resposta. - O leitor tolerante vale para todas as linhas. Campo, estado ou motivo desconhecido nunca devem derrubar o seu conector nem mover dinheiro.
- O contrato é a referência de semântica. Quando o texto de uma página do guia e o do contrato divergem em semântica, vale o contrato. Para a forma que a API aceita e devolve hoje, vale esta página somada às páginas da seção Referência.
Veja também¶
- OpenAPI: o documento servido e como ele se relaciona com o contrato.
- Depósitos e Saques
- Reconciliação
- Rotas e campos reservados
- Changelog