Skip to main content
Status: Developer preview.
Webhooks let you receive a run’s progress — including approval.required — without holding a stream connection open. Register an HTTPS endpoint once; Praxa signs and retries every delivery to it. This is Praxa’s first outbound webhook lane, so treat every delivery as untrusted until you’ve verified it: nothing enforces that a POST to your endpoint actually came from Praxa except the signature you check yourself.

1. Register an endpoint

The response includes a signing secret. It is shown once — store it in your own secret manager immediately, the same way you’d store any other API credential:
Each delivery’s body is one run event, in the same AiPlatformEventV1 shape (type, sequence, at, plus type-specific fields) you’d see on GET /v1/runs/:id/events — just pushed to you instead of streamed. Only subscribe to the event types you’ll actually act on; every subscribed type is a delivery you’re committing to verify and acknowledge.

2. Verify the signature

Every delivery carries three headers: Praxa-Signature, Praxa-Timestamp, and Praxa-Delivery-Id. The signature is an HMAC-SHA256 over {delivery id}.{timestamp}.{raw body}, keyed with your endpoint’s secret — always sign and verify the raw, unparsed body. If you JSON.parse first and re-serialize before checking, whitespace and key-ordering differences will make a legitimate delivery fail verification. The timestamp guards against replay: reject anything more than a few minutes old, using a clock you control rather than trusting the header alone.
Wire it into your handler before any parsing happens:

3. Handle deliveries idempotently

Retries WILL resend a delivery — a failed attempt, a slow response, a network blip on either side all look identical to Praxa and all get retried. Praxa-Delivery-Id stays the same across every retry of the same delivery, so use it as your dedup key rather than trying to derive one from the event body:
Mark the delivery seen before you do slow or side-effecting work, and make processEvent itself safe to no-op on a second call if you can — a crash between “processed” and “marked seen” should not be able to double-apply an effect like sending a notification twice.

4. Know the retry and dead-letter behavior

  • Acknowledge fast: return a 2xx as soon as you’ve durably queued the delivery, and do slow processing afterward. A handler that blocks on slow work looks like a timeout to Praxa, and a timeout is indistinguishable from a failure — it gets retried.
  • A non-2xx response or a timeout counts as a failed attempt. Praxa retries on an exponential backoff schedule; check your endpoint’s delivery history in the developer console for the exact schedule and current attempt count for any given delivery.
  • A delivery that keeps failing past its final retry moves to a dead-letter state instead of disappearing. Inspect and manually replay dead-lettered deliveries from the console rather than assuming a run’s history is complete without them.
  • Delivery order is best-effort, not guaranteed — a retried delivery can land after a newer one. If ordering matters to you, key your handling off the event’s own sequence field, not the order deliveries arrive in.

5. Rotate your signing secret

Request a new secret for the endpoint when you need to rotate — on a schedule, after a suspected leak, or as part of offboarding anyone who had access to it. During rotation, the previous secret keeps validating deliveries for a short overlap window so in-flight retries signed before the rotation don’t start failing the instant you switch: verify against both the old and new secret until you’ve confirmed the new one is live in your deployment, then delete the old one from your code and let it lapse. Never log a secret while you’re debugging a rotation — treat it exactly like the API key you use to call /v1/execute, because a leaked webhook secret lets anyone forge deliveries to your endpoint.

Next steps