GET /events¶
Para: Dev — Conferido em 2026-10-18.
GET /v1/events devolve os eventos da sua operação, em ordem de criação, com o mesmo envelope do webhook. É a rede de segurança dos webhooks: com um job a cada 5 minutos você recupera qualquer evento perdido, esgotado na DLQ, não assinado ou anterior ao cadastro da URL.
O que é¶
Uma listagem paginada por cursor, só de leitura, com a mesma autenticação das demais rotas da API (Autenticação HMAC). Ela exige uma chave com escopo de leitura e devolve apenas eventos da operação da chave: um evento de outra operação nunca aparece.
GET /v1/events?after=evt_5a9c1f0e7b3d4c28a6e1f4b07d92c385&limit=200
GET /v1/events?object_id=dep_0198b7c24d5e7a1b8c2d3e4f5a6b7c8d
| Parâmetro | Regra |
|---|---|
after |
Id de evento (evt_…) da sua operação. Devolve os eventos criados depois dele. Sem after, a listagem começa no primeiro evento da operação. |
object_id |
Filtra os eventos de um objeto (dep_… ou wdr_…). Aceita letras, dígitos, _ e -, de 1 a 64 caracteres. |
limit |
De 1 a 200; padrão 50. |
A resposta é { "data": [Evento…], "next_cursor": "evt_…" | null }. next_cursor traz o id do último evento da página quando há mais eventos depois dele; use-o como after da próxima chamada. null significa que acabou. Veja o formato do evento em Envelope.
Quando usar¶
Rode um job a cada 5 minutos, a partir do último evento que você processou, e guarde esse cursor no seu banco. Esse job fecha as lacunas de:
- webhook perdido por falha de rede ou queda do seu receptor;
- evento na DLQ depois de 72 h sem 2xx;
- evento que não chegou por webhook porque o tipo não está na sua assinatura (
event_not_subscribed); - evento anterior ao cadastro da URL (
no_endpoint); - evento de ambiente diferente do da URL cadastrada.
Use também object_id para investigar um objeto específico: "o que o Dracma registrou para este depósito?".
Eventos filtrados continuam aqui
A assinatura por tipo de evento só controla o que sai por webhook. GET /events devolve todos os eventos do catálogo, tenham ou não sido entregues. Se você assina só alguns tipos, o job precisa tratar todos.
Como funciona¶
Ordem, cursor e página¶
Os eventos vêm em ordem de criação (desempate estável por id). O cursor é o id de um evento: o Dracma busca a posição desse evento e devolve os posteriores, de modo que novos eventos criados enquanto você pagina entram no fim e não deslocam as páginas. Cada evento é gravado na mesma transação que muda o estado do objeto, então a lista não perde uma transição.
Sem assinatura individual¶
Os eventos de GET /events não vêm assinados um a um: a autenticidade vem da sua chamada autenticada à API. Mesmo assim, passe cada evento pelo mesmo caminho do webhook, a partir da deduplicação: dedup por id, consulta do objeto (fetch-back), coerência, versão e efeito idempotente. O Dracma-Event-Id de um webhook é o id do mesmo evento em GET /events, então uma entrega por webhook e uma por listagem do mesmo evento são deduplicadas juntas.
Limite de taxa¶
GET /events está no balde de listagem: 5 requisições por segundo por chave. Pagine com limit=200 e uma cadência calma; não varra tudo a cada execução do job. Ao exceder o limite você recebe 429 com Retry-After; veja Limites e quotas.
Retenção¶
Não disponível
O Dracma não publica um prazo de retenção para os eventos de GET /events, e portanto não o garante. Mantenha o job de 5 minutos funcionando para nunca depender de reler eventos antigos, e guarde no seu banco o que precisar auditar.
Exemplos¶
Resposta com dois eventos do mesmo depósito (dados fictícios; a resposta de produção traz até limit eventos):
{
"data": [
{
"id": "evt_686599cbb91280cbb34334f16f643047",
"type": "deposit.pending",
"created_at": "2026-10-18T14:00:02Z",
"operation_id": "0198b7c2-3d4e-7f50-8a61-92b3c4d5e6f7",
"object_id": "dep_0198b7c24d5e7a1b8c2d3e4f5a6b7c8d",
"object_version": 2,
"data": {
"object": {
"id": "dep_0198b7c24d5e7a1b8c2d3e4f5a6b7c8d",
"external_id": "pedido-10482",
"status": "pending",
"version": 2,
"amount": {
"amount": "10000",
"currency": "BRL"
},
"paid_amount": null,
"method": "pix",
"player_ref": "jogador-7731",
"payer": null,
"instructions": {
"pix": {
"copy_paste": "00020126580014br.gov.bcb.pix0136exemplo-chave-ficticia5204000053039865406100.005802BR5909EXEMPLO6009SAO PAULO62070503***6304ABCD",
"expires_at": "2026-10-18T14:30:00Z"
}
},
"failure_code": null,
"review_reasons": [],
"created_at": "2026-10-18T14:00:00Z",
"updated_at": "2026-10-18T14:00:02Z",
"paid_at": null,
"expires_at": "2026-10-18T14:30:00Z"
}
}
},
{
"id": "evt_e12d177892007213962d29c4b3b60580",
"type": "deposit.paid",
"created_at": "2026-10-18T14:05:13Z",
"operation_id": "0198b7c2-3d4e-7f50-8a61-92b3c4d5e6f7",
"object_id": "dep_0198b7c24d5e7a1b8c2d3e4f5a6b7c8d",
"object_version": 3,
"data": {
"object": {
"id": "dep_0198b7c24d5e7a1b8c2d3e4f5a6b7c8d",
"external_id": "pedido-10482",
"status": "paid",
"version": 3,
"amount": {
"amount": "10000",
"currency": "BRL"
},
"paid_amount": {
"amount": "10000",
"currency": "BRL"
},
"method": "pix",
"player_ref": "jogador-7731",
"payer": {
"document": {
"type": "CPF",
"number": "12345678909",
"country": "BR"
},
"name": "Maria Exemplo",
"matches_player": true
},
"instructions": {
"pix": {
"copy_paste": "00020126580014br.gov.bcb.pix0136exemplo-chave-ficticia5204000053039865406100.005802BR5909EXEMPLO6009SAO PAULO62070503***6304ABCD",
"expires_at": "2026-10-18T14:30:00Z"
}
},
"failure_code": null,
"review_reasons": [],
"created_at": "2026-10-18T14:00:00Z",
"updated_at": "2026-10-18T14:05:13Z",
"paid_at": "2026-10-18T14:05:12Z",
"expires_at": "2026-10-18T14:30:00Z"
}
}
}
],
"next_cursor": "evt_e12d177892007213962d29c4b3b60580"
}
Resposta de 400 para um after desconhecido ou de outra operação (o Dracma não confirma se o id existe em outra operação):
{
"type": "https://docs.dracma.test/errors/validation_failed",
"title": "validation_failed",
"status": 400,
"code": "validation_failed",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"errors": [
{
"field": "after",
"message": "parâmetro inválido",
"reason": "unknown"
}
]
}
Esqueleto do job (a função aplicar_evento é a mesma do receptor, a partir da deduplicação):
def rodada(banco, api):
cursor = banco.ler_cursor() # último evt processado, ou None
while True:
params = {"limit": 200}
if cursor:
params["after"] = cursor
pagina = api.get("/v1/events", params) # com a sua chave
for ev in pagina["data"]:
aplicar_evento(ev) # dedup, fetch-back, coerência, versão, efeito
banco.gravar_cursor(ev["id"]) # só depois de aplicar
cursor = ev["id"]
if pagina["next_cursor"] is None:
break
Erros e casos de borda¶
| Resposta | Quando |
|---|---|
400 validation_failed, errors[0].reason = "format" |
after fora do formato evt_ mais letras e dígitos, ou object_id com caracteres inválidos. |
400 validation_failed, errors[0].reason = "unknown" |
after de um evento que não existe ou que pertence a outra operação. |
400 validation_failed, errors[0].reason = "range" |
limit fora de 1 a 200 ou não numérico. |
401 |
Chave ou assinatura da chamada inválida. |
429 |
Mais de 5 requisições por segundo na listagem. Respeite Retry-After. |
503 |
Dependência indisponível. Tente de novo; a resposta traz Retry-After. |
- Cursor perdido: se você perdeu o cursor, recomece de um evento conhecido ou do início e deixe a deduplicação descartar o que já foi aplicado. É seguro, só mais lento.
- Lista vazia com
next_cursornulo significa que não há eventos novos. - Eventos de objetos terminais continuam na lista; o job não deve tratá-los como novos efeitos se a versão já foi aplicada.
- Timestamps: o
created_atde eventos de saque pode trazer frações de segundo. Veja Envelope.
Veja também¶
- Receptor passo a passo: o caminho que cada evento percorre.
- Retentativas, skipped e DLQ: por que um evento pode não chegar por webhook.
- Paginação e filtros: convenções de cursor da API.
- Limites e quotas: limites de taxa.