WebhooksNew
Receive job events at your HTTPS endpoint, verify the signature on every delivery, and handle retries and duplicates.
Webhooks push events to a URL you control, so you do not have to poll GET /v2/jobs.
Events
Register an endpoint
sextant webhooks create https://hooks.example.com/sextant --events job.failed,job.deadThe response prints the signing secret once, as whsec_.... Store it as an environment variable in the service that receives the events.
The delivery
Sextant sends a POST with a JSON body and these headers:
POST /sextant HTTP/1.1
Content-Type: application/json
Sextant-Event: job.dead
Sextant-Delivery: dlv_91b0c2f4
Sextant-Signature: t=1760951778,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd{
"id": "evt_4c1e0a",
"type": "job.dead",
"created_at": "2026-10-20T09:16:18Z",
"data": { "job": { "id": "job_8f3k2m9q", "queue": "emails", "state": "dead", "attempt": 5 } }
}Verify the signature
The signature is an HMAC-SHA256 of {t}.{raw body} with your secret. Compute it over the raw bytes of the body, before any JSON parsing, and compare in constant time. Reject timestamps older than five minutes.
import { Sextant } from '@sextant/node';
export async function POST(req: Request) {
const body = await req.text();
const event = Sextant.webhooks.verify(body, req.headers.get('sextant-signature')!, process.env.SEXTANT_WEBHOOK_SECRET!);
if (event.type === 'job.dead') await pager.notify(event.data.job.id);
return new Response(null, { status: 204 });
}from sextant import Sextant, SignatureError
def webhook(request):
try:
event = Sextant.webhooks.verify(
request.body, request.headers["Sextant-Signature"], os.environ["SEXTANT_WEBHOOK_SECRET"]
)
except SignatureError:
return HttpResponse(status=400)
if event["type"] == "job.dead":
pager.notify(event["data"]["job"]["id"])
return HttpResponse(status=204)func handler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
event, err := sextant.VerifyWebhook(body, r.Header.Get("Sextant-Signature"), os.Getenv("SEXTANT_WEBHOOK_SECRET"))
if err != nil {
http.Error(w, "bad signature", http.StatusBadRequest)
return
}
if event.Type == "job.dead" {
pager.Notify(event.Data.Job.ID)
}
w.WriteHeader(http.StatusNoContent)
}Retries and duplicates
Any response outside 2xx, or no response within 10 seconds, counts as a failed delivery. Sextant retries with growing waits for 24 hours: 10 s, 1 min, 5 min, 30 min, then every 2 hours. After 24 hours the delivery is dropped and the webhook is flagged in sextant webhooks list.
Deliveries are at least once. Use Sextant-Delivery or the event id to ignore ones you already handled, and reply quickly: do the real work after answering with 204.
Test an endpoint
sextant webhooks test whk_5d02absends a signed ping and prints the status and duration of the attempt.