> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://developers.zorvia.io/webhooks/setup/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.zorvia.io/_mcp/server. # Set up a webhook > Send message events from a Zorvia channel to your application. Use webhooks to keep your application in sync with message activity in Zorvia. When an event occurs, Zorvia sends an HTTP `POST` request to the endpoint configured for that channel. Before you begin, create an HTTPS endpoint in your application that can accept JSON requests. ## Create a channel webhook #### Open the channel In your Zorvia workspace, select the channel that should send events. #### Open Webhook settings Go to **Settings → Webhook**, then select **Create Webhook**. #### Enter the destination Give the webhook a recognizable name and enter your HTTPS endpoint. #### Secure the webhook Add a signing secret so your application can verify each request. You can also add static request headers as a JSON object. #### Enable delivery Select **Save & Enable**. To finish the configuration without starting delivery, turn on **Save As Draft** before saving. If your endpoint requires an API key or another fixed value, add it as a custom header: ```json { "X-API-Key": "your-receiver-api-key", "X-Webhook-Source": "zorvia" } ``` Header values must be strings, numbers, booleans, or `null`. Webhook bodies use `Content-Type: application/json`. ## Receive webhook requests For each delivery, your endpoint should: 1. accept `POST` requests with a JSON body; 2. verify the request signature when a signing secret is configured; 3. process events idempotently; and 4. return a `2xx` response after accepting the event. Zorvia retries a delivery when your endpoint times out or returns a non-`2xx` response. A delivery is attempted up to three times with exponential backoff, and each attempt has a 60-second timeout. Return a response quickly and move long-running work to a background job. Because a delivery can be retried, use a combination of `message.id`, `event.type`, and `event.occurred_at` as an idempotency key. Including the event timestamp prevents a later edit to the same message from being mistaken for a duplicate. ## Verify a signed request When you configure a signing secret, each request includes a `Signature` header. The value is a lowercase hexadecimal HMAC-SHA256 digest of the raw request body, calculated with your signing secret. ```js import crypto from "node:crypto"; export function verifyZorviaWebhook(rawBody, signature, secret) { if (!signature) { return false; } const expected = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); const received = Buffer.from(signature, "hex"); const calculated = Buffer.from(expected, "hex"); return ( received.length === calculated.length && crypto.timingSafeEqual(received, calculated) ); } ``` Use the unmodified request bytes when calculating the digest. Parsing and serializing the JSON again can change the body and cause verification to fail. > **Warning** > > Treat the signing secret and any credential in a custom header as sensitive. Store them in a secret manager and never expose them in browser code. Requests also include a `Timestamp` header containing the Unix time at which the delivery attempt was made. If you do not configure a signing secret, the request does not include a `Signature` header. > **Warning** > > The signature covers only the raw request body; it does not include the `Timestamp` header. It verifies that the body was signed with your secret, but it does not prevent a captured request from being replayed. Do not rely on the timestamp alone for replay protection. Process deliveries idempotently using the event fields described above. ## Manage delivery From **Channel → Settings → Webhook**, select **Manage** on a webhook to update its name, URL, signing secret, or static headers. * **Disable Webhook** pauses delivery without removing the configuration. * **Enable** resumes delivery for a draft or disabled webhook. * **Delete** permanently removes the webhook from the channel. The [webhook events reference](/webhooks/events) lists the event names and payload fields your endpoint can receive. > Send message events from a Zorvia channel to your application.