Webhooks
Eventos e payloads
Todos os tipos de evento, quando cada um é enviado e o formato real do conteúdo.
Todo evento chega com o mesmo envelope:
| Campo | Descrição |
|---|---|
id | Id do evento (evt_...). Use para não processar o mesmo evento duas vezes. |
type | Tipo, ex.: charge.paid. |
version | Versão do formato (v1). |
created_at | Momento do fato (UTC). |
environment | production. O valor sandbox está reservado para o ambiente de testes, que ainda está em preparação. |
data | Conteúdo do evento (abaixo, por tipo). |
Valores em centavos vêm como string. Campos novos podem ser acrescentados ao data sem aviso prévio: ignore o que você não conhece.
Tipos de evento
| Tipo | Quando |
|---|---|
charge.pending | O QR foi emitido e a cobrança aguarda pagamento. |
charge.paid | Pagamento confirmado (também em pagamento tardio, com late_payment: true). |
charge.expired | A validade acabou sem pagamento, ou a cobrança foi cancelada antes do pagamento (data.status: "cancelled"). |
charge.failed | A emissão do Pix foi recusada ou não pôde ser feita. |
charge.refunded | Uma devolução foi concluída. |
charge.duplicate_payment | O mesmo QR foi pago de novo; o valor extra fica retido para devolução. |
case.opened | Um caso foi aberto: devolução, contestação (MED) ou revisão. |
case.resolved | Um caso foi resolvido. |
balance.available | Valores liquidados e conciliados ficaram disponíveis. |
reserve.released | Uma reserva contratual foi liberada para o seu saldo. |
pricing.policy_published | Uma nova tarifa foi publicada para a sua conta. |
reserve.policy_published | Uma nova regra de reserva contratual foi publicada. |
A lista oficial, com a versão e o formato da assinatura, sai em Listar tipos de evento. Os tipos payout.* existem na lista, mas não são enviados enquanto os repasses forem feitos pela Koku conforme o contrato.
Cobranças
charge.pending
charge.pending{
"id": "evt_1a7c3e9b5d2f4a8e9c6b0d3f7a1e5c92",
"type": "charge.pending",
"version": "v1",
"created_at": "2026-10-07T15:30:01.000Z",
"environment": "production",
"data": {
"charge_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"order_reference": "PEDIDO-1001",
"status": "pending",
"amount_cents": "15990",
"expires_at": "2026-10-07T16:00:00.000Z"
}
}charge.paid
O evento mais importante: libere o pedido depois de confirmar pela consulta. Traz bruto (amount_cents), tarifa (fee_cents), líquido (net_cents), reserva, data do pagamento e o identificador fim a fim do Pix. Em pagamento tardio, late_payment vem true e case_id aponta o caso de revisão.
charge.paid{
"id": "evt_2f6b1c9e8a4d4e7f9b3c5a1d0e6f8b27",
"type": "charge.paid",
"version": "v1",
"created_at": "2026-10-07T15:32:11.000Z",
"environment": "production",
"data": {
"charge_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"order_reference": "PEDIDO-1001",
"amount_cents": "15990",
"fee_cents": "320",
"net_cents": "15670",
"reserve_cents": "0",
"paid_at": "2026-10-07T15:32:10.000Z",
"end_to_end_id": "E12345678202610071532a1B2c3D4e5F",
"late_payment": false,
"case_id": null
}
}charge.expired
Enviado quando a cobrança deixa de aceitar pagamento: data.status vem expired (a validade acabou) ou cancelled (a cobrança foi cancelada antes do pagamento). Não há evento separado para o cancelamento.
charge.expired{
"id": "evt_3b9d5f1a7c2e4b6d8f0a2c4e6b8d1f37",
"type": "charge.expired",
"version": "v1",
"created_at": "2026-10-07T16:00:05.000Z",
"environment": "production",
"data": {
"charge_id": "b2e7c4d1-9a36-4f58-8c0e-1d5a7f3b6e92",
"order_reference": "PEDIDO-1002",
"status": "expired"
}
}charge.failed
reason sempre vem e é um código estável do motivo, bom para decidir no código. Para exibir, use failure_message da cobrança (consulta), que é um texto fixo da Koku.
reason | Significado |
|---|---|
rejected:fraud | Recusada pela análise de risco. |
rejected:invalid_request | Dados da cobrança ou do pagador inválidos ou incompletos. |
rejected:compliance | Recusada pela análise de conformidade. |
rejected:limit | Limite de recebimento da conta atingido. |
rejected:sector | Segmento da venda não aceito para a conta. |
rejected:registration | Pendência no cadastro da conta. |
rejected:blocked ou rejected:account_suspended | Recebimentos bloqueados ou suspensos para a conta. |
rejected:other | Outra recusa definitiva. |
not_created:<motivo> | Falha técnica na emissão; nada foi criado (ex.: not_created:transient_error). |
unknown_resolved_not_created | A emissão ficou sem resposta e, depois de conferida, não tinha sido criada. |
expired_before_issue | A validade acabou antes de a emissão ser confirmada. |
Em todos os casos, nenhum QR Code válido foi entregue: crie uma nova cobrança com outro order_reference.
charge.failed{
"id": "evt_4c0e6a2b8d3f4c7e9a1b3d5f7c9e2a48",
"type": "charge.failed",
"version": "v1",
"created_at": "2026-10-07T15:40:02.000Z",
"environment": "production",
"data": {
"charge_id": "e41a9c7b-2d58-4e06-b3f1-7c9d0a2e5b18",
"order_reference": "PEDIDO-1003",
"status": "failed",
"reason": "rejected:fraud"
}
}charge.refunded
refunded_cents é o valor desta devolução.
charge.refunded{
"id": "evt_5d1f7b3c9e4a4d8f0b2c4e6a8d0f3b59",
"type": "charge.refunded",
"version": "v1",
"created_at": "2026-10-08T12:00:04.000Z",
"environment": "production",
"data": {
"charge_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"case_id": "3c7e1b95-6d2a-4f80-9e4b-a1d5c8f20e63",
"refunded_cents": "5000"
}
}charge.duplicate_payment
charge.duplicate_payment{
"id": "evt_6e2a8c4d0f5b4e9a1c3d5f7b9e1a4c60",
"type": "charge.duplicate_payment",
"version": "v1",
"created_at": "2026-10-07T15:35:20.000Z",
"environment": "production",
"data": {
"charge_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"order_reference": "PEDIDO-1001",
"payment_id": "f2c8a5e1-3b7d-4e90-a6c4-8d1f5b2e9a73",
"classification": "duplicate",
"amount_cents": "15990",
"end_to_end_id": "E12345678202610071535f6G7h8J9k0L",
"case_id": "6a2f9d14-8e3b-4c57-b1d0-5e9a7c3f2b81"
}
}Casos
case.opened
case.opened{
"id": "evt_7f3b9d5e1a6c4f0b2d4e6a8c0f2b5d71",
"type": "case.opened",
"version": "v1",
"created_at": "2026-10-08T12:00:00.000Z",
"environment": "production",
"data": {
"case_id": "3c7e1b95-6d2a-4f80-9e4b-a1d5c8f20e63",
"case_type": "refund",
"charge_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"amount_cents": "5000",
"reason": "Cliente devolveu um item"
}
}case.resolved
resolution diz o desfecho (ex.: refunded) e uncovered_cents a parte que não tinha saldo para cobrir, quando houver.
case.resolved{
"id": "evt_8a4c0e6f2b7d4a1c3e5f7b9d1a3c6e82",
"type": "case.resolved",
"version": "v1",
"created_at": "2026-10-08T12:00:04.000Z",
"environment": "production",
"data": {
"case_id": "3c7e1b95-6d2a-4f80-9e4b-a1d5c8f20e63",
"case_type": "refund",
"charge_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"amount_cents": "5000",
"resolution": "refunded",
"uncovered_cents": "0"
}
}Saldo
balance.available
amount_cents é o total que ficou disponível nesta liquidação.
balance.available{
"id": "evt_9b5d1f7a3c8e4b2d4f6a8c0e2b4d7f93",
"type": "balance.available",
"version": "v1",
"created_at": "2026-10-08T08:00:02.000Z",
"environment": "production",
"data": {
"settlement_id": "9e2d5b7a-3c18-4f6e-b0a4-7d1c9e5f2a83",
"amount_cents": "15670"
}
}Recuperando eventos
Todos os eventos ficam disponíveis pela API, independentemente da entrega. Use para conferir o que pode ter se perdido enquanto o seu endpoint estava fora do ar:
curl -X GET 'https://api.kokupay.com/v1/webhooks/events?type=charge.paid&limit=20' \
-H "Authorization: Bearer $KOKU_API_KEY"const response = await fetch('https://api.kokupay.com/v1/webhooks/events?type=charge.paid&limit=20', {
method: 'GET',
headers: {
Authorization: `Bearer ${process.env.KOKU_API_KEY}`,
},
});
const data = await response.json();
console.log(response.status, data);import os
import requests
response = requests.request(
"GET",
"https://api.kokupay.com/v1/webhooks/events?type=charge.paid&limit=20",
headers={
"Authorization": f"Bearer {os.environ['KOKU_API_KEY']}",
},
timeout=30,
)
print(response.status_code, response.json())<?php
$ch = curl_init('https://api.kokupay.com/v1/webhooks/events?type=charge.paid&limit=20');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('KOKU_API_KEY'),
],
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo $status, PHP_EOL;
print_r($data);{
"items": [
{
"id": "evt_2f6b1c9e8a4d4e7f9b3c5a1d0e6f8b27",
"type": "charge.paid",
"version": "v1",
"environment": "production",
"object_type": "charge",
"object_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"created_at": "2026-10-07T15:32:11.000Z",
"data": {
"charge_id": "8d3f6a12-4b9e-4c71-a2d5-6e0f1b3c9a47",
"order_reference": "PEDIDO-1001",
"amount_cents": "15990",
"fee_cents": "320",
"net_cents": "15670",
"reserve_cents": "0",
"paid_at": "2026-10-07T15:32:10.000Z",
"end_to_end_id": "E12345678202610071532a1B2c3D4e5F",
"late_payment": false,
"case_id": null
}
}
],
"next_cursor": null
}