Receba eventos da Kernos
Cadastre um endereço HTTPS e a Kernos manda um POST assinado a cada evento: package.close, pix.paid, receipt.close, return.close, sales_order.finish_packing, shipment.dispatch.
/guias/receber-eventos.mdÍndice das páginas: /llms.txtConfigure o endereço
O endereço precisa ser HTTPS, público e sem usuário e senha. Um por empresa.
curl -X PUT "https://stag.kernos.com.br/v1/webhook-endpoint" \
-H "Authorization: Bearer ksk_test_…" \
-H "Kernos-Version: 2026-10-03" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://seu-sistema.com.br/kernos/webhook",
"events": ["sales_order.finish_packing", "shipment.dispatch"]
}'import { Kernos } from '@kernosbr/kernos'
const kernos = new Kernos(process.env.KERNOS_API_KEY)
const result = await kernos.integrationWebhook.save({
url: 'https://seu-sistema.com.br/kernos/webhook',
events: ['sales_order.finish_packing', 'shipment.dispatch']
})import os
from kernos import Kernos
kernos = Kernos(os.environ["KERNOS_API_KEY"])
result = kernos.integration_webhook.save({
"url": "https://seu-sistema.com.br/kernos/webhook",
"events": ["sales_order.finish_packing", "shipment.dispatch"]
})<?php
$kernos = new Kernos\Kernos(getenv('KERNOS_API_KEY'));
$result = $kernos->integrationWebhook->save([
'url' => 'https://seu-sistema.com.br/kernos/webhook',
'events' => ['sales_order.finish_packing', 'shipment.dispatch']
]);Kernos kernos = new Kernos(System.getenv("KERNOS_API_KEY"));
JsonNode result = kernos.integrationWebhook().save(Map.of(
"url", "https://seu-sistema.com.br/kernos/webhook",
"events", List.of("sales_order.finish_packing", "shipment.dispatch")
));var kernos = new KernosClient(Environment.GetEnvironmentVariable("KERNOS_API_KEY"));
var result = await kernos.IntegrationWebhook.SaveAsync(new {
url = "https://seu-sistema.com.br/kernos/webhook",
events = new[] {"sales_order.finish_packing", "shipment.dispatch"}
});client, err := kernos.New(os.Getenv("KERNOS_API_KEY"))
result, err := client.IntegrationWebhook.Save(ctx, map[string]any{
"url": "https://seu-sistema.com.br/kernos/webhook",
"events": []any{"sales_order.finish_packing", "shipment.dispatch"},
})let kernos = Kernos::new(&std::env::var("KERNOS_API_KEY")?)?;
let result = kernos.integration_webhook().save(&json!({
"url": "https://seu-sistema.com.br/kernos/webhook",
"events": ["sales_order.finish_packing", "shipment.dispatch"]
}), None)?;Na primeira gravação a resposta traz o secret (whsec_…). Ele aparece uma vez. GET /v1/webhook-endpoint lê o endereço, os eventos e as últimas 50 entregas.
Confira a assinatura
Cada entrega traz Kernos-Signature: t=<unix>,v1=<hex>, com v1 = HMAC-SHA256(segredo, "<t>.<corpo cru>"). Confira antes de confiar no corpo, com o corpo CRU, e recuse t velho (mais de 5 minutos): é o que barra o replay.
A assinatura vale sobre o texto exato que chegou. Se o seu framework reparseia o JSON antes de você conferir, a conferência falha.
# Sem SDK, confira à mão: v1 = HMAC-SHA256(segredo, "<t>.<corpo cru>"), em hexadecimal.
# O cabeçalho é: Kernos-Signature: t=<unix>,v1=<hex> (na troca do segredo vêm dois v1: aceite qualquer um)import express from 'express'
import { Kernos } from '@kernosbr/kernos'
const app = express()
// O corpo CRU: a assinatura vale sobre o texto exato que chegou.
app.post('/kernos/webhook', express.raw({ type: 'application/json' }), (req, res) => {
let event
try {
event = Kernos.webhooks.constructEvent(req.body.toString(), req.header('Kernos-Signature'), process.env.KERNOS_WEBHOOK_SECRET)
} catch {
return res.sendStatus(400)
}
res.sendStatus(200) // responda 2xx logo; o trabalho pesado vai para uma fila
console.log(event.type, event.id)
})from kernos import webhooks
# raw_body é o texto exato que chegou, antes de qualquer json.loads
event = webhooks.construct_event(raw_body, request.headers.get("Kernos-Signature"), os.environ["KERNOS_WEBHOOK_SECRET"])
print(event["type"], event["id"])<?php
// $rawBody é o texto exato que chegou: file_get_contents('php://input')
$event = Kernos\Webhook::constructEvent($rawBody, $_SERVER['HTTP_KERNOS_SIGNATURE'] ?? null, getenv('KERNOS_WEBHOOK_SECRET'), 300);
echo $event['type'], ' ', $event['id'];// rawBody é o texto exato que chegou, antes de qualquer parser
JsonNode event = Webhook.constructEvent(rawBody, request.getHeader("Kernos-Signature"), System.getenv("KERNOS_WEBHOOK_SECRET"), Duration.ofMinutes(5));
System.out.println(event.get("type").asText());// rawBody é o texto exato que chegou, antes de qualquer parser
var evt = Webhook.ConstructEvent(rawBody, Request.Headers["Kernos-Signature"], Environment.GetEnvironmentVariable("KERNOS_WEBHOOK_SECRET"), TimeSpan.FromMinutes(5));
Console.WriteLine(evt["type"]);// rawBody é o texto exato que chegou, antes de qualquer parser
event, err := kernos.ConstructEvent(rawBody, r.Header.Get("Kernos-Signature"), os.Getenv("KERNOS_WEBHOOK_SECRET"), 5*time.Minute)
if err != nil {
http.Error(w, "assinatura inválida", http.StatusBadRequest)
return
}
fmt.Println(event.Type, event.ID)// raw_body é o texto exato que chegou, antes de qualquer parser
let event = kernos::webhook::construct_event(&raw_body, signature.as_deref(), &secret, Some(300))?;
println!("{}", event["type"]);Responda 2xx logo e faça o trabalho pesado depois. O Kernos-Event-Id é o id do fato e é o mesmo em cada nova tentativa: use-o para não processar duas vezes.
Teste sem esperar o galpão
POST /v1/webhook-endpoint/ping manda um evento de teste ao seu endereço, assinado como em produção, com data de exemplo e test: true. Sem type, é um ping; com ele, um dos eventos públicos.
curl -X POST "https://stag.kernos.com.br/v1/webhook-endpoint/ping" \
-H "Authorization: Bearer ksk_test_…" \
-H "Kernos-Version: 2026-10-03" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "type": "ping" }'import { Kernos } from '@kernosbr/kernos'
const kernos = new Kernos(process.env.KERNOS_API_KEY)
const result = await kernos.integrationWebhook.ping({ type: 'ping' })import os
from kernos import Kernos
kernos = Kernos(os.environ["KERNOS_API_KEY"])
result = kernos.integration_webhook.ping({"type": "ping"})<?php
$kernos = new Kernos\Kernos(getenv('KERNOS_API_KEY'));
$result = $kernos->integrationWebhook->ping(['type' => 'ping']);Kernos kernos = new Kernos(System.getenv("KERNOS_API_KEY"));
JsonNode result = kernos.integrationWebhook().ping(Map.of("type", "ping"));var kernos = new KernosClient(Environment.GetEnvironmentVariable("KERNOS_API_KEY"));
var result = await kernos.IntegrationWebhook.PingAsync(new { type = "ping" });client, err := kernos.New(os.Getenv("KERNOS_API_KEY"))
result, err := client.IntegrationWebhook.Ping(ctx, map[string]any{"type": "ping"})let kernos = Kernos::new(&std::env::var("KERNOS_API_KEY")?)?;
let result = kernos.integration_webhook().ping(&json!({ "type": "ping" }), None)?;Depure as entregas
Cada entrega é Entregue, Pendente ou Falhou, com o status HTTP e a hora da próxima tentativa.
| Resposta do seu servidor | Causa | O que fazer |
|---|---|---|
| Não conectou | A Kernos não alcançou o seu servidor. | Confira se o domínio é público e responde na internet. |
| 3xx | O seu servidor redirecionou. Redirecionamento conta como falha. | Cadastre o endereço final, sem redirecionar. |
| 4xx | O endereço recusou o POST. | Aceite POST e deixe a rota sem login. |
| 5xx | O seu código falhou ao processar. | Veja o log do seu servidor e responda 2xx antes do trabalho pesado. |
| Tempo esgotado | O endereço demorou mais de 10 s. | Responda na hora e processe numa fila. |
Como a entrega se comporta
- Novas tentativas. A Kernos tenta de novo por até 3 dias, com espera crescente (30 s × 2ⁿ, até 1 hora).
- Reenvio.
POST /v1/webhook-endpoint/deliveries/{delivery}/resendpõe a entrega de volta na fila agora, com os 3 dias contando de novo. Pela CLI:kernos events resend <entrega>. - Ordem. Os eventos não têm ordem garantida. Use o id do evento para não processar duas vezes.
- Trocar o segredo.
PUT /v1/webhook-endpointcomrotate_secret: truetroca owhsec_. Comgrace_hours(0 a 168), o segredo antigo continua valendo e a assinatura traz doisv1, o novo e o antigo: o seu receptor aceita qualquer um. Os SDKs já aceitam. - Ler eventos sem endereço.
GET /v1/eventsdevolve os mesmos corpos, das últimas 24 horas ou depois de umafter.
Os eventos têm uma página cada, com o corpo e o exemplo. Para o endereço lido: GET /v1/webhook-endpoint.