Webhooks

A webhook routine starts when an external service sends a signed request to the routine's URL. An agent routine and a channel routine both support webhooks. A host administrator configures them in the routine editor. The host must be online to receive requests.

Set up a webhook routine

  1. Open the routine editor of an agent or a channel.
  2. Set When to run to Webhook.
  3. Optional: enter an event type. With no event type, all event types start the routine.
  4. Optional: add filters.
  5. Save the routine. The host makes the URL and the signing secret.
  6. Copy the secret into the secret store of the sending service. OpenBot shows it one time only.
  7. Copy the URL. The URL is empty until the account service registers the route. The host must be signed in to its OpenBot account.

Each webhook routine has its own URL and secret. A host can have at most 64 webhook routines.

You can also ask an agent in chat to make a webhook routine. The agent calls openbot.create_webhook_routine and tells you the URL and the request format. It does not get the secret. A card in the chat shows the URL; press the regenerate button on the card to make the secret, then copy it. Only a person who can manage the host's webhook routines sees the card. When the agent makes the routine for another agent, get the secret in that agent's routine editor.

To change the event type or the filters from the chat, ask the agent: it calls openbot.update_webhook_routine. You can also change them on the card and press Save. The URL and the secret stay the same. Only a person's message can start these tools: a routine run or another agent's message cannot make or change a webhook routine.

To replace the secret, make a new one in the routine editor and update the sender. The old secret stops working immediately. A secret has the format whsec_<43 base64url characters>.

When you change the routine to Schedule, or delete the routine, its URL stops working. If you change it back to Webhook, the routine gets a new URL and a new secret.

The URL also changes when you sign in to a different OpenBot account on the host. The secret stays the same. Copy the new URL into the sending service.

Send a test event

In the routine editor, select Send test event. The host makes an event that matches the event type and the filters, signs it with the routine secret, and receives it as it receives a relayed request. The event type is openbot.test when the routine accepts all types. The test shows in the history, and a match starts one run. A paused routine does not run. The test does not go through the relay, so it does not show whether a sender can reach the URL.

Send a request

Send a POST request with Content-Type: application/json to the routine URL. The body has this shape:

{
  "type": "deployment.completed",
  "occurredAt": "2026-10-07T10:00:00.000Z",
  "data": { "environment": "production", "revision": "abc123" }
}
Field Rule
type Required. A string of 1 to 256 characters.
occurredAt Optional. A date string. If you do not send it, OpenBot uses the receipt time.
data Optional. Any JSON value. If you do not send it, the value is null.

The body must be UTF-8 and at most 64 KiB.

Event type and filters

In the routine editor, select Filter events to add an event type or a filter. With no event type and no filter, each signed request starts the routine.

If the routine has an event type, type must be equal to it. A filter has a JSON Pointer into data and a value. The value is a string, number, boolean, or null. The value at the pointer must be equal to the filter value, with the same JSON type. For example, /environment with production reads data.environment. All filters must match. Use ~1 for / and ~0 for ~ in a pointer. An empty pointer selects all of data. A routine can have at most 128 filters.

Headers and signature

Header Value
X-OpenBot-Timestamp The current Unix time in seconds
X-OpenBot-Delivery-Id A unique ID for this event. Use 1 to 256 ASCII letters, digits, ., _, :, or -. The first character is a letter or a digit.
X-OpenBot-Signature sha256= and the lowercase hexadecimal HMAC-SHA256

The HMAC key is the routine secret. The signed bytes are <timestamp>.<deliveryId>.<body>. Use the exact body bytes that you send. Do not parse and serialize the body again after you sign it. The timestamp must be within 5 minutes of the host clock.

Example with Node.js:

import { createHmac, randomUUID } from "node:crypto";

const body = JSON.stringify({ type: "deployment.completed", data: { environment: "production" } });
const deliveryId = randomUUID(); // Keep this ID and the body for a retry.
const timestamp = String(Math.floor(Date.now() / 1000));
const signature = createHmac("sha256", process.env.OPENBOT_WEBHOOK_SECRET)
  .update(`${timestamp}.${deliveryId}.${body}`)
  .digest("hex");
const response = await fetch(process.env.OPENBOT_WEBHOOK_URL, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-OpenBot-Timestamp": timestamp,
    "X-OpenBot-Delivery-Id": deliveryId,
    "X-OpenBot-Signature": `sha256=${signature}`,
  },
  body,
});
if (response.status !== 200 && response.status !== 202) {
  throw new Error(`OpenBot did not accept the event: ${response.status}`);
}

Responses

Status Meaning Retry
202 The host saved the event. The routine started, or the host ignored the event because the event type or a filter did not match, or because the routine is paused. No
200 The routine already has this delivery ID. No
400 The body is not valid. No. Correct the body.
401 A header or the signature is not valid, or the timestamp is not within 5 minutes of the host clock. No. Correct the secret, the headers, or the clock.
404 The host does not know the route. No
413 The body is larger than 64 KiB, or the routine instruction and the event together are longer than 100,000 characters. No. Send less data or shorten the instruction.
415 Content-Type is not application/json. No
429 Too many requests to this route from your address. Yes, later
503 The host is offline, busy, or did not answer in 2.5 seconds. The relay does not know the route, the host cannot read the secret, or the agent or channel cannot take a run now. Yes, later

A 202 response does not mean that the run is complete. Look at the routine history for the run result. The relay does not keep requests while the host is offline. If you do not retry, the event is lost.

Retries and duplicates

The delivery ID is the deduplication key for each routine. To retry, send the same delivery ID and the same body with a new timestamp and a new signature. A repeated delivery ID gets 200 and does not start a second run. This also applies to an event that the routine ignored. The host keeps receipts for 7 days. After that time, the same delivery ID starts a new run.

What the agent receives

The run instruction is the routine instruction, followed by the event. The event is marked as external data, not as instructions. It contains the delivery ID, the routine ID, type, occurredAt, the receipt time, and data. Event data can go to the model provider of the routine, like all routine input. The data cannot give tools to the agent or skip approvals.

Standard Webhooks senders

The URL also accepts requests in the Standard Webhooks format. Composio, Svix, Clerk, and Resend send this format. These senders make their own signing secret, so you cannot give them the OpenBot secret:

  1. Copy the URL of the routine into the sender.
  2. Copy the signing secret that the sender shows.
  3. In the routine editor or on the chat card, select Use the sender's secret (the key button next to the regenerate button), paste the secret, and select Use secret. The previous secret stops working immediately.
Header Value
webhook-id A unique ID for this event, with the same rules as X-OpenBot-Delivery-Id.
webhook-timestamp The current Unix time in seconds. It must be within 5 minutes of the host clock.
webhook-signature One to eight space-separated v1,<base64 HMAC-SHA256> values. One value must match.

Svix also sends these headers with the svix- prefix. OpenBot accepts both names. The signed bytes are <webhook-id>.<webhook-timestamp>.<body>. The HMAC key is the base64 data after whsec_ in the secret, as the specification says, or the complete secret as UTF-8 text, as Composio uses it.

The body must be a JSON object with a type string. OpenBot reads the request as follows:

Event field Value
type The type field of the body.
occurredAt The timestamp field of the body when it is a date string, otherwise the receipt time.
data The complete body. A filter reads it from the root, for example /metadata/trigger_slug for a Composio trigger.

The responses and the retry rules are the same as for the OpenBot format. A host that has an older OpenBot version cannot check this format, and the relay answers 401.

History

The routine history shows each request that the host ignored, with the reason: the event type or a filter did not match, or the routine was not active.

Data retention

  • Receipts: 7 days. A receipt has the delivery ID, the event type, the result, and the run ID. It does not have the request body.
  • Deleting a routine deletes its receipts.

Security

  • Only host administrators can manage webhook routines.
  • The host verifies the signature before it decodes the body. The relay does not have the secret.
  • Secrets are encrypted with the operating system's secret storage. They are not in logs, errors, or activity.