Sandbox

Para: Dev — Conferido em 2026-10-18.

A operação Teste roda contra um simulador de PSP: nenhum dinheiro se move e você controla o desfecho de cada depósito e saque por rotas /v1/sandbox/*. Use-a para provar o seu conector inteiro antes de pedir uma operação Real.

O que é

O "sandbox" do Dracma é o comportamento de uma operação do tipo Teste. Ele não é um servidor separado: quem decide é a operação a que a chave pertence (prefixo key_test_). O GET /v1/ping devolve environment: "sandbox" quando a chave é de uma operação Teste.

Operação Teste Operação Real
Prefixo da chave key_test_ key_live_
Dinheiro Nenhum se move Dinheiro real, em qualquer instância
PSP Simulador do Dracma (sem conexão cadastrada, o simulador é o padrão) PSP real da operação
Rotas /v1/sandbox/* Existem Respondem 404 not_found, sem executar nada
copy_paste do PIX BR Code sintético com o marcador SANDBOX Cobrança real

Onde ela fica: no hml, o host api. atende operações Teste e o host api-live. atende operações Real. Em produção só existem operações Real. A tabela de hosts está em Ambientes, tipos de operação e hosts.

  • PIX sintético. O instructions.pix.copy_paste de um depósito Teste traz o marcador SANDBOX e uma chave de recebedor fictícia (sandbox@dracma.invalid). Nenhum banco aceita esse código. Se um copy_paste sem o marcador aparecer na sua operação Teste, pare e avise o suporte: você pode estar apontando para uma operação Real.
  • Cada tipo recusa a chave do outro. key_test_ contra um host de operação Real (e o inverso) responde 401 unknown_key. Veja Ambientes, tipos de operação e hosts.
  • Mesma autenticação da produção em todas as rotas, inclusive /sandbox/*: assinatura HMAC, nonce e janela de tempo (Autenticação).
  • Mesmos limites de taxa da produção. O sandbox não tem um perfil de limites menor: o mesmo balde de listagem, o mesmo limite de depósitos e de saques por jogador por hora valem na operação Teste (Limites e quotas). Uma suíte que cria muitos saques para o mesmo jogador esbarra em 429 rate_limited; use external_ref de jogadores diferentes.
  • Webhook só para a URL cadastrada na operação. O Dracma nunca entrega para uma URL que venha no corpo de uma requisição (Webhooks).

Dinheiro real

"Sandbox" descreve o tipo da operação, não o hml. No hml existe também operação Real (key_live_), e ela move dinheiro real. Confira o prefixo da chave e o environment do GET /v1/ping antes de rodar uma receita desta seção.

Quando usar

  • Antes de pedir a operação Real: o go-live exige a Suíte de conformidade verde e as Receitas por caminho provadas contra o seu receptor e o seu livro.
  • Para testar um caminho que a produção só produz por acaso: pagamento de terceiro, valor divergente, expiração, estorno, chargeback, saque devolvido, queda do Dracma.
  • Para testar o seu código de repetição e de deduplicação: queda simulada e reentrega de webhook.

Como funciona

Rotas de controle

Rota Escopo exigido O que faz
POST /v1/sandbox/deposits/{id}/pay deposits:write Simula o pagamento do PIX; aceita pagador e valor diferentes
POST /v1/sandbox/deposits/{id}/expire deposits:write pending vira expired
POST /v1/sandbox/deposits/{id}/refund deposits:write paid vira refunded
POST /v1/sandbox/deposits/{id}/chargeback deposits:write paid vira chargeback
POST /v1/sandbox/withdrawals/{id}/{action} withdrawals:write Decisão do revisor ou resultado do PSP no saque (approve, reject, pay, fail, return)
POST /v1/sandbox/outage deposits:write ou withdrawals:write Liga e desliga a queda simulada da operação
POST /v1/sandbox/webhooks/redeliver deposits:write ou withdrawals:write Reentrega um evento já gravado
GET /v1/sandbox/webhooks read Log das últimas entregas de webhook
  • Sem o escopo, a resposta é 403 insufficient_scope. Um objeto de outra operação responde 404 not_found, nunca 403.
  • Uma chave com deposits:write, withdrawals:write e read cobre todas as rotas da tabela.
  • Em operação Real, toda rota /v1/sandbox/* responde 404 not_found a uma chave com o escopo exigido, sem executar nada.

Reservado no contrato

POST /v1/sandbox/bank-transfers/incoming existe só para transferência bancária, que a operação PIX não usa: um corpo válido recebe 400 validation_failed com reason: rail_not_enabled. Veja Rotas e campos reservados.

Idempotência nas rotas de controle

Mande Idempotency-Key em todo POST, inclusive nas rotas /sandbox/*; sem ela a API responde 400 validation_failed com field: Idempotency-Key. Use uma chave nova a cada ação de controle (por exemplo sbx_<uuidv7>). Repetir a mesma chave devolve a resposta guardada (Idempotent-Replayed: true) sem executar a ação de novo. Isso é o que você quer em POST /v1/deposits, mas atrapalha se você quiser, por exemplo, ligar e desligar a queda simulada duas vezes com a mesma chave.

Pagar um depósito

POST /v1/sandbox/deposits/{id}/pay aceita um corpo opcional. Vazio significa "pago pelo próprio jogador, valor exato".

{
  "payer_document": {"type": "CPF", "number": "11144477735", "country": "BR"},
  "paid_amount": {"amount": "4999", "currency": "BRL"}
}

Os dois campos são opcionais: payer_document (type, number e country) e paid_amount (um Money, na moeda do depósito). Se payer_document vier, number é obrigatório. O exemplo simula um terceiro pagando R$ 49,99, valor diferente do depósito.

  • Só funciona em depósito pending; em qualquer outro estado: 409 invalid_state_transition.
  • paid_amount em moeda diferente da do depósito: 400 validation_failed com reason: currency_mismatch.
  • A resposta é 200 com o objeto. A API espera até 3 segundos pelo resultado do simulador; se ele demorar mais, o objeto volta ainda em pending e o desfecho chega pelo webhook. Faça o fetch-back em vez de supor o estado.
  • Simulador indisponível: 503 service_unavailable com Retry-After: 1 e retryable: true. Repita com uma chave nova de controle.

Ações no saque

POST /v1/sandbox/withdrawals/{id}/{action}, sem corpo. A tabela completa de efeitos está em Receitas por caminho.

action Resposta quando aceita
approve, reject 200 com o saque já decidido (como se o revisor tivesse agido no painel)
pay, fail 200 com o saque no estado atual; o desfecho (paid ou failed) chega depois, por webhook
return 200 com o saque já em returned
  • Ação fora do enum: 400 validation_failed.
  • Ação válida para outro estado: 409 invalid_state_transition.
  • compliance_hold, compliance_clear, compliance_reject, send, transit, recall_accept e recall_reject existem no enum por causa de transferências bancárias; em PIX respondem 409 invalid_state_transition.
  • Simulador indisponível: 503 service_unavailable com Retry-After: 5.

Queda simulada

POST /v1/sandbox/outage com {"down": true} responde {"down": true}. A partir daí, toda rota da sua operação fora de /v1/sandbox/* responde 503 service_unavailable, com retryable: true e Retry-After: 5. GET /v1/ping e as próprias rotas de sandbox continuam respondendo. Outras operações não são afetadas. {"down": false} religa. O campo down é obrigatório: sem ele, 400 validation_failed.

sequenceDiagram participant C as Seu conector participant D as Dracma (operação Teste) C->>D: POST /v1/sandbox/outage {"down": true} D-->>C: 200 {"down": true} C->>D: POST /v1/deposits (Idempotency-Key K) D-->>C: 503 service_unavailable, Retry-After 5 C->>D: POST /v1/sandbox/outage {"down": false} (chave nova) D-->>C: 200 {"down": false} C->>D: POST /v1/deposits (mesma chave K, mesmo corpo) D-->>C: 201 ou 202 C->>D: POST /v1/deposits (mesma chave K, mesmo corpo) D-->>C: mesma resposta, Idempotent-Replayed true

Log de entregas e reentrega de webhook

GET /v1/sandbox/webhooks devolve {"data": [...]} com as 50 entregas mais recentes da operação, da mais nova para a mais antiga. Não há filtro nem paginação, e o corpo do evento não vem no log. Cada item traz:

Campo Conteúdo
id Id da entrega
event_id, event_type O evento entregue (Dracma-Event-Id) e o seu tipo
environment sandbox para operação Teste
status pending, delivered, dlq ou skipped
skip_reason Presente só em skipped
attempts, total_attempts Tentativas do ciclo atual e acumuladas
next_attempt_at Próxima tentativa, enquanto pending
last_status_code, last_duration_ms Resposta HTTP do seu receptor e a duração
last_error Erro de rede ou de TLS, truncado em 200 caracteres
created_at, delivered_at, resolved_at, resolution_reason Marcas de tempo e motivo de resolução

Use esse log para ver as suas respostas 401 e 5xx sem precisar de log no receptor. Estados e cronograma: Retentativas, skipped e DLQ.

POST /v1/sandbox/webhooks/redeliver com {"event_id": "evt_..."} reentrega:

  • 200 com {"event_id": "evt_...", "scheduled": n}, onde n é o número de entregas reabertas (uma por URL cadastrada). Cada entrega reaberta volta a pending com as tentativas zeradas.
  • Funciona também para um evento que já estava delivered, o que permite testar a sua deduplicação. A reentrega leva o mesmo corpo e o mesmo Dracma-Event-Id.
  • event_id fora do formato evt_ mais letras e dígitos: 400 validation_failed com reason: format.
  • Evento inexistente ou de outra operação: 404 not_found.

Exemplos

As chamadas abaixo usam o cliente de shell de Exemplos executáveis. Os comandos completos, do ping ao saque, estão em Primeiro depósito e primeiro saque.

# Ligar a queda simulada (chave de controle nova a cada chamada)
echo '{"down":true}' > corpo-queda.json
bash exemplos/shell/assinar.sh POST /sandbox/outage corpo-queda.json "sbx_$(date +%s)_ligar"

# Ver as últimas entregas de webhook
bash exemplos/shell/assinar.sh GET /sandbox/webhooks

Erros e casos de borda

Você vê Causa O que fazer
404 not_found em toda rota /sandbox/* A chave é de operação Real, ou o objeto é de outra operação Confira o prefixo da chave e o environment do GET /v1/ping
403 insufficient_scope A chave não tem o escopo da tabela Peça uma chave com o escopo certo
409 invalid_state_transition O objeto não está no estado que a ação exige Leia o objeto com GET e escolha a ação certa
400 validation_failed com Idempotency-Key Faltou a chave no POST Mande uma chave nova
503 service_unavailable em rotas normais Queda simulada ligada Desligue com {"down": false} ou trate como no Retry
503 service_unavailable em /sandbox/* Simulador do PSP indisponível Repita com Retry-After e chave nova
429 rate_limited Limites iguais aos da produção Espere o Retry-After

Veja também