Webhooks
Webhooks
Receba os eventos da sua conta em tempo real, com assinatura e reenvio automático.
Webhooks avisam o seu servidor quando algo acontece na sua conta: um Pix foi pago, expirou, foi devolvido ou um valor ficou disponível. A Koku faz um POST com JSON assinado para a URL que você cadastrar.
Cadastrar pelo painel
- No painel Koku (abre em nova aba), abra Desenvolvedores › Webhooks.
- Clique em Novo endpoint, informe a URL HTTPS e escolha os eventos (ou deixe todos).
- Confirme com o código do seu aplicativo autenticador.
- Copie o segredo de assinatura exibido: ele aparece uma única vez. Guarde-o como
KOKU_WEBHOOK_SECRETno seu servidor.
Na mesma área você acompanha as Entregas (resultado de cada tentativa) e os Eventos emitidos.
Cadastrar pela API
POST /v1/webhooks/endpoints com o escopo webhooks:write. Referência: Cadastrar endpoint.
curl -X POST 'https://api.kokupay.com/v1/webhooks/endpoints' \
-H "Authorization: Bearer $KOKU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://loja.example.com/webhooks/koku",
"event_types": [
"charge.paid",
"charge.expired",
"charge.failed",
"charge.refunded"
]
}'const response = await fetch('https://api.kokupay.com/v1/webhooks/endpoints', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KOKU_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://loja.example.com/webhooks/koku',
event_types: ['charge.paid', 'charge.expired', 'charge.failed', 'charge.refunded'],
}),
});
const data = await response.json();
console.log(response.status, data);import os
import requests
response = requests.request(
"POST",
"https://api.kokupay.com/v1/webhooks/endpoints",
headers={
"Authorization": f"Bearer {os.environ['KOKU_API_KEY']}",
},
json={
"url": "https://loja.example.com/webhooks/koku",
"event_types": ["charge.paid", "charge.expired", "charge.failed", "charge.refunded"],
},
timeout=30,
)
print(response.status_code, response.json())<?php
$ch = curl_init('https://api.kokupay.com/v1/webhooks/endpoints');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('KOKU_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://loja.example.com/webhooks/koku',
'event_types' => [
'charge.paid',
'charge.expired',
'charge.failed',
'charge.refunded',
],
]),
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo $status, PHP_EOL;
print_r($data);{
"id": "71c3e9a5-4f2d-4b8e-a06c-9d5e2f1b7a38",
"signing_secret": "whsec_Zq4m8Tn2Vb7Lk1Xc9Rd5Hs3Wf6Jp0Ga2Ye8Ui4Ko7"
}url: HTTPS público. Endereços de rede privada, locais e de metadados de nuvem são recusados (422,details.reason = "ssrf_blocked").event_types: os tipos que você quer receber. Vazio ou omitido = todos.- Até 10 endpoints ativos por conta e ambiente.
Para ver os endpoints cadastrados e a saúde de cada um:
curl -X GET 'https://api.kokupay.com/v1/webhooks/endpoints' \
-H "Authorization: Bearer $KOKU_API_KEY"const response = await fetch('https://api.kokupay.com/v1/webhooks/endpoints', {
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/endpoints",
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/endpoints');
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": "71c3e9a5-4f2d-4b8e-a06c-9d5e2f1b7a38",
"url": "https://loja.example.com/webhooks/koku",
"event_types": [
"charge.paid",
"charge.expired",
"charge.failed",
"charge.refunded"
],
"status": "active",
"secret_last4": "4Ko7",
"created_at": "2026-10-07T14:10:00.000Z",
"consecutive_failures": 0,
"paused_until": null
}
]
}Para desligar um endpoint:
curl -X DELETE 'https://api.kokupay.com/v1/webhooks/endpoints/71c3e9a5-4f2d-4b8e-a06c-9d5e2f1b7a38' \
-H "Authorization: Bearer $KOKU_API_KEY"const response = await fetch('https://api.kokupay.com/v1/webhooks/endpoints/71c3e9a5-4f2d-4b8e-a06c-9d5e2f1b7a38', {
method: 'DELETE',
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(
"DELETE",
"https://api.kokupay.com/v1/webhooks/endpoints/71c3e9a5-4f2d-4b8e-a06c-9d5e2f1b7a38",
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/endpoints/71c3e9a5-4f2d-4b8e-a06c-9d5e2f1b7a38');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
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);{
"ok": true
}O que chega no seu endpoint
Cada entrega é um POST com o evento no corpo e estes cabeçalhos:
| Cabeçalho | Conteúdo |
|---|---|
x-koku-signature | t=<unix>,v1=<hex>: a assinatura. Veja Verificar assinatura. |
x-koku-event-id | Id do evento (evt_...), o mesmo em todas as tentativas. |
x-koku-event-type | Tipo do evento, ex.: charge.paid. |
x-koku-event-version | Versão do formato, hoje v1. |
content-type | application/json |
user-agent | Koku-Webhooks/1 |
POST /webhooks/koku HTTP/1.1
content-type: application/json
user-agent: Koku-Webhooks/1
x-koku-signature: t=1791473531,v1=a735379a545698a02021820ddff8136ca623148267e128cf0ab6107e94dc3d96
x-koku-event-id: evt_2f6b1c9e8a4d4e7f9b3c5a1d0e6f8b27
x-koku-event-type: charge.paid
x-koku-event-version: v1
{"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}}Todo evento tem o mesmo envelope: id, type, version, created_at, environment e data (o conteúdo do tipo). Os formatos de cada tipo estão em Eventos e payloads.
Resumo do que o seu endpoint deve fazer
- Ler o corpo bruto e conferir a assinatura com o seu segredo.
- Responder
2xxem até 5 segundos, antes de qualquer processamento demorado. - Ignorar evento já processado (mesmo
id). - Processar em segundo plano e, para liberar pedido, consultar a cobrança.
Detalhes em Reenvios e boas práticas.