Skip to main content
Webhook endpoints let Vetta push events to your server as they happen, instead of you polling. Each delivery is signed so you can verify it came from Vetta and was not tampered with.
This page covers outbound delivery (Vetta → your server). Inbound email/SMS receiving lands on an identity’s message feed today; the agent wake from a stored inbound message is coming soon — see Inbound events.

The webhook endpoint object

string
Unique id (e.g. whk_01H...).
string
Your HTTPS URL that receives deliveries.
string[]
Event types to deliver (e.g. session.idle, budget.exceeded).
string
Signing secret. Returned only at creation and on rotate.
boolean
Whether the endpoint receives deliveries. Set via update; auto-disabled after sustained failures.
string
Creation timestamp.

Create an endpoint

POST /v1/webhooks201 Created. The signing secret is always minted server-side and returned once, on this response.
string
required
HTTPS URL to receive events.
string[]
required
Event types to subscribe to. Use ["*"] for all.

Retrieve, list & delete

Delete response
See Pagination.

Update an endpoint

PATCH /v1/webhooks/{id}200 OK. Change the url, subscribed events, or pause/resume delivery with enabled.
string
New HTTPS URL.
string[]
Replace the subscribed event types.
boolean
Set false to pause delivery, true to resume.

Send a test delivery

POST /v1/webhooks/{id}/test202 Accepted. Queues one signed delivery to the endpoint so you can prove your verifier works before a real event depends on it. It ignores the endpoint’s events filter — you asked for this one — and carries the type webhook.test, so a handler that switches on type can ignore it safely. A disabled endpoint is refused rather than queued.
Response
The response is the delivery record, not the receiver’s answer — the POST happens asynchronously. Poll the delivery log for the outcome.

Rotate the signing secret

POST /v1/webhooks/{id}/rotate200 OK. Issues a new secret (shown once). During the overlap window, deliveries are signed with both the old and new secrets, so you can roll your verifier with zero missed events. When the window closes, only the new secret signs.
integer
How long both secrets stay valid. Defaults to 24; 24 is the max. Use 0 to cut over immediately.
Response

Delivery payload

Each delivery is a POST to your URL with a JSON body wrapping the event, plus signature headers.
Body

Signature verification

Each signature is an HMAC-SHA256 over {timestamp}.{raw_body} keyed by your endpoint secret, encoded as v1=<hex>. The scheme version (v1) lets us evolve the algorithm without breaking verifiers, and multiple signatures let a secret rotation overlap. To verify:
  1. Read the raw request body (do not re-serialize it).
  2. Build the signed payload: `${timestamp}.${rawBody}`.
  3. Compute your own v1 HMAC-SHA256 and check it against any signature in the header (both secrets are valid during an overlap window).
  4. Reject the delivery if the timestamp is too old (e.g. > 5 minutes).
Verify against the raw bytes of the body. Parsing and re-stringifying JSON changes whitespace and key order, which breaks the HMAC. Always length-check before timingSafeEqual — it throws a RangeError on mismatched buffer lengths, which an attacker could trigger with a malformed header.

Delivery log & redelivery

Every attempt is recorded. Inspect it, drill into one delivery, and manually resend.
A redelivery re-sends the same envelope, so the id your handler dedupes on is unchanged. It is recorded as a new delivery record with its own attempt count, which leaves the original attempt’s outcome intact in the log.
string
Delivery id.
string
The evt_... id delivered.
string
The event type.
string
pending, succeeded, or failed.
integer
Number of attempts so far.
integer | null
HTTP status your server returned on the last attempt.
string | null
First bytes of your server’s response body, for debugging.
string | null
When the delivery first succeeded.
string | null
When the next retry is scheduled, if still pending.
Response

Delivery, retries & auto-disable

Respond 2xx within 10 seconds to acknowledge; slower responses count as a timeout. Any non-2xx response or timeout is retried with exponential backoff over ~24 hours. Deliveries carry a stable id — dedupe on it, since a delivery may occasionally arrive more than once. If an endpoint returns only failures for 24 hours, Vetta flips it to enabled: false and stops delivering. Fix your endpoint, re-enable it with update, and redeliver any missed events.

Inbound receivers

Three routes under /v1/webhooks/ point the other way: they are inbound receivers the platform exposes for a provider to call, not endpoints you register or invoke.
These take a provider signature, not your API key, and there is no CLI or SDK verb for them by design — a caller with an API key has a first-class route for everything these do. They are listed here so the route table has no unexplained entries, not because you should call them.
Each verifies its provider signature and answers 2xx on acceptance; an unverifiable payload is rejected without side effects and never reaches an agent.