Your AI Connector Docs

Webhooks API

Webhooks let the platform notify your other systems the moment something happens — a new contact, a reply, an appointment booked, and more. This API manages the subscriptions themselves: which URLs receive which events. For how to receive and verify the payloads your endpoint gets, see Webhooks.

All paths below are relative to the API base URL:

https://api.youraiconnector.com/v1

Every request must be authenticated. See Authentication for the four accepted methods. The examples here use the X-API-Key header (and one query-parameter form for cURL).

Note: Webhooks must be enabled for your account. If they are not, these endpoints return a 403.


How subscriptions are addressed

Each subscription has an id and an optional name. Either one can be used as the {webhookId} in the path for update, delete, test, health, and re-enable.

Prefer the name. Subscription ids are positional, so they can shift after another subscription is deleted. If you set a stable name when creating a subscription, address it by name to avoid surprises.


List subscriptions

GET /webhooks

cURL

curl "https://api.youraiconnector.com/v1/webhooks?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/webhooks", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/webhooks",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "webhooks": [
    {
      "id": "0",
      "name": "Order updates hook",
      "url": "https://hooks.example.com/incoming",
      "subscribed_to": ["Contact Created", "Replies"],
      "subscribed_to_tags": [],
      "created_at": "2026-06-09T12:00:00.000Z",
      "signing_enabled": true,
      "signing_secret_created_at": "2026-07-15T09:30:00.000Z",
      "retries_enabled": true,
      "enabled": true,
      "apply_to_sub_accounts": false
    }
  ]
}

signing_enabled and retries_enabled are per-subscription opt-ins, both off unless you turn them on. See Signed payloads and Retries.

apply_to_sub_accounts is the agency-inheritance opt-in — see One subscription for all client accounts. Off by default, and inert on accounts that have no client accounts.

enabled is the subscription’s on/off switch — see Switching a subscription off. Switched-off subscriptions are still listed here.

The signing secret itself is never included here — read it from GET /webhooks/{id}/signing-secret.


List subscribable event types

Returns the exact strings you may use in subscribed_to. Use this to discover valid event names rather than hard-coding them.

GET /webhooks/events

cURL

curl "https://api.youraiconnector.com/v1/webhooks/events" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/webhooks/events", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/webhooks/events",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

The response is {"success": true, "events": [...]}, where events currently holds 22 exact strings: Contact Created, Human Alerted, Appointment Booked, Replies, Reads, Deliveries, Credits Spent, Credits Recharged, Low Credit Balance, Contact Paused, Contact Do Not Disturb, Contact Unarchived, New Message, Contact Resumed, Chat Concluded, Task Created, Task Updated, Task Completed, Daily Summary Created, Channel Connected, Broadcast Started, and Broadcast Completed (Channel Connected is accepted in subscribed_to but nothing emits it today, so don’t build against it).

For what each event means and the event code it sends in the payload, see The 22 Webhook Events. This endpoint is the authoritative list at any moment — read it live rather than hard-coding the names.


Create a subscription

POST /webhooks

Field Required Description
url Yes HTTPS URL that will receive event payloads via POST. Must be publicly reachable.
subscribed_to Yes A non-empty array of event names (see /webhooks/events).
name No A display name. Also usable as the {webhookId} later. Defaults to a timestamped name.
subscribed_to_tags No Tag IDs that narrow which tags produce a conversation-summary notification. It does not scope the subscription’s events to those tags — to get a request when a specific tag is applied, set a webhook URL on that tag in the Tags tab of the agent (or campaign).
retries_enabled No Boolean, defaults to false. Opt in to retries of failed deliveries.
generate_signing_secret No Boolean, defaults to false. Mint an HMAC signing secret with the subscription. The secret is returned once, as a top-level signing_secret on the response.
enabled No Boolean, defaults to true. Pass false to create the subscription switched off. See Switching a subscription off.
apply_to_sub_accounts No Boolean, defaults to false. On an agency account, true makes this subscription also receive events from every client account — see One subscription for all client accounts.

URL rules: The URL must use https:// and be publicly reachable. Plain http://, localhost, private-network addresses, and platform-internal addresses are rejected with a 400.

cURL

curl -X POST "https://api.youraiconnector.com/v1/webhooks?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/incoming",
    "subscribed_to": ["Contact Created", "Replies"],
    "name": "Order updates hook"
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/webhooks", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://hooks.example.com/incoming",
    subscribed_to: ["Contact Created", "Replies"],
    name: "Order updates hook",
  }),
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/webhooks",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "url": "https://hooks.example.com/incoming",
        "subscribed_to": ["Contact Created", "Replies"],
        "name": "Order updates hook",
    },
)
data = res.json()

Response

{
  "success": true,
  "webhook_id": "1",
  "webhook": {
    "id": "1",
    "name": "Order updates hook",
    "url": "https://hooks.example.com/incoming",
    "subscribed_to": ["Contact Created", "Replies"],
    "subscribed_to_tags": [],
    "created_at": "2026-06-09T12:00:00.000Z"
  }
}

Update a subscription

Provide at least one of url, subscribed_to, name, subscribed_to_tags, retries_enabled, enabled, or apply_to_sub_accounts. Omitted fields keep their current values. subscribed_to and subscribed_to_tags are replacements, not merges.

PUT /webhooks/{webhookId}

Updating a subscription never disturbs its signing secret — manage that through the signing-secret routes.

When the URL changes, delivery for the new URL is automatically re-enabled, giving a previously failing endpoint a fresh start.

cURL

curl -X PUT "https://api.youraiconnector.com/v1/webhooks/Order%20updates%20hook" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/v2/incoming",
    "subscribed_to": ["Replies", "Chat Concluded"]
  }'

JavaScript

const res = await fetch(
  `https://api.youraiconnector.com/v1/webhooks/${encodeURIComponent("Order updates hook")}`,
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://hooks.example.com/v2/incoming",
      subscribed_to: ["Replies", "Chat Concluded"],
    }),
  }
);
const data = await res.json();

Python

import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/webhooks/Order updates hook",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "url": "https://hooks.example.com/v2/incoming",
        "subscribed_to": ["Replies", "Chat Concluded"],
    },
)
data = res.json()

Response

{
  "success": true,
  "webhook_id": "0",
  "webhook": {
    "id": "0",
    "name": "Order updates hook",
    "url": "https://hooks.example.com/v2/incoming",
    "subscribed_to": ["Replies", "Chat Concluded"],
    "subscribed_to_tags": [],
    "created_at": "2026-06-09T12:00:00.000Z"
  }
}

An unknown id or name returns 404 with { "success": false, "error": "Webhook not found" }.


Delete a subscription

Removes the subscription so its URL stops receiving payloads. Its delivery-health counters are reset, so re-adding the same URL later starts with a clean record.

DELETE /webhooks/{webhookId}

cURL

curl -X DELETE "https://api.youraiconnector.com/v1/webhooks/0" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/webhooks/0", {
  method: "DELETE",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/webhooks/0",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true
}

Send a test payload

Sends a sample payload to the subscription’s URL so you can verify your receiver end-to-end. Optionally pass an event to control which event type the sample simulates. Test deliveries never affect the subscription’s health counters.

POST /webhooks/{webhookId}/test

The response always returns 200 and reports the outcome with a delivered flag — a failed test does not return an error status. When delivered is false, the response includes the failure details.

Field Required Description
event No Event type to simulate (must be one of /webhooks/events). Defaults to a delivery event.

cURL

curl -X POST "https://api.youraiconnector.com/v1/webhooks/0/test?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "event": "Contact Created" }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/webhooks/0/test", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ event: "Contact Created" }),
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/webhooks/0/test",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"event": "Contact Created"},
)
data = res.json()

Response (delivered)

{
  "success": true,
  "webhook_id": "0",
  "delivered": true
}

Response (failed)

{
  "success": true,
  "webhook_id": "0",
  "delivered": false,
  "failure_type": "permanent",
  "status_code": 404,
  "error_message": "Request failed with status code 404"
}

failure_type is one of permanent, temporary, timeout, network, or unknown.


Check delivery health

Returns the delivery-health record for the subscription’s URL: how many deliveries have succeeded and failed, whether delivery is currently paused after repeated failures, and the details of the most recent failure. Returns "health": null when no deliveries have been attempted yet.

GET /webhooks/{webhookId}/health

cURL

curl "https://api.youraiconnector.com/v1/webhooks/0/health" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/webhooks/0/health", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/webhooks/0/health",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "webhook_id": "0",
  "url": "https://hooks.example.com/incoming",
  "health": {
    "consecutive_failures": 0,
    "total_failures": 2,
    "total_successes": 120,
    "is_disabled": false,
    "disabled_at": null,
    "disabled_reason": null,
    "last_failure": null,
    "last_success_at": "2026-06-09T12:00:00.000Z",
    "created_at": "2026-05-01T08:00:00.000Z",
    "updated_at": "2026-06-09T12:00:00.000Z"
  }
}

When is_disabled is true, delivery to the URL has been paused automatically after repeated failures. Fix your receiver, then re-enable it (below).


Re-enable delivery

Resumes delivery for a webhook whose URL was paused automatically after repeated failures. This resets the paused flag and failure counters but does not attempt a delivery — use the test endpoint afterwards to confirm your receiver is healthy again.

POST /webhooks/{webhookId}/reenable

cURL

curl -X POST "https://api.youraiconnector.com/v1/webhooks/0/reenable?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/webhooks/0/reenable", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/webhooks/0/reenable",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "webhook_id": "0"
}

Switching a subscription off

enabled is the subscription’s own on/off switch. Switching it off stops deliveries while keeping the URL, event list and signing secret intact.

# Off
curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

# Back on
curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'
  • Absent means on. A subscription created before this field existed has no enabled value stored and delivers normally. GET /webhooks always reports a concrete boolean.
  • Switched-off subscriptions are still listed by GET /webhooks — that’s how you find them to switch back on.
  • A retry queued before the switch-off does not resume: the retry re-reads the subscription at send time and drops if it is switched off.
  • Nothing suppressed while switched off is replayed when you switch it back on.

Distinct from the automatic disable after repeated failures, which is reported by GET /webhooks/{id}/health as is_disabled and cleared with POST /webhooks/{id}/reenable. enabled is the account’s switch; is_disabled is ours. Neither overrides the other — a subscription must be both switched on and not auto-disabled to deliver.


One subscription for all client accounts (agencies)

On an agency account, set apply_to_sub_accounts: true on a subscription (at create time or via PUT) and it also receives events that happen on every one of the agency’s client accounts — one endpoint covers the whole agency, instead of re-creating the subscription on each client account.

curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"apply_to_sub_accounts": true}'

How it behaves:

  • The user block tells the accounts apart. Every payload’s user block identifies the account the event actually happened on, so your receiver can route per client.
  • The agency subscription’s own settings apply everywhere. Its event list, signing secret and retry opt-in are used for the inherited deliveries too.
  • A client account’s own subscription to the same URL wins. If a client account has its own subscription pointing at the same URL, that one is used for that account’s events — the same event is never delivered twice to one endpoint.
  • Client accounts don’t see it. Inherited subscriptions do not appear in a client account’s own webhook list, and the client can’t switch them off — only the agency manages them.
  • Delivery health is tracked per client account. An endpoint that keeps failing is auto-disabled for the account whose deliveries failed, not for the whole agency.
  • subscribed_to_tags does not inherit. The tag list references the agency’s own tags, which don’t exist on client accounts — conversation-summary narrowing only applies to the agency’s own events.
  • Inert elsewhere. On an account with no client accounts the flag stores fine and does nothing.

Headers on every delivery

These three headers are sent on every delivery, whether or not the subscription is signed:

Header Meaning
X-Webhook-Delivery Stable id for the logical event. Identical across retries — dedupe on it.
X-Webhook-Attempt 1-based attempt number.
X-Webhook-Event The event name.

Signed payloads

Signing is optional, off by default, and set per subscription. When a subscription has a signing secret, every delivery carries two more headers on top of the three sent on every delivery (X-Webhook-Delivery, X-Webhook-Attempt and X-Webhook-Event):

Header Meaning
X-Webhook-Signature v1=<hex> — HMAC-SHA256 of the string "<timestamp>.<raw request body>", keyed with the per-webhook signing secret you mint and rotate at GET/POST/DELETE /v1/webhooks/{webhookId}/signing-secret.
X-Webhook-Timestamp Send time, Unix seconds. Bound into the signature, so it cannot be altered independently.

To verify, recompute the HMAC-SHA256 over the raw body with your secret and compare it to the header. Verify against the raw request body. Re-serializing parsed JSON changes the bytes and breaks the comparison. Reject deliveries whose timestamp is outside a freshness window (300s is a sensible default) to prevent replay, and compare with a timing-safe function.

See Signed Payloads for full Node and Python verification examples.

Signing is not the same as API authentication. The REST API itself authenticates with API keys rather than OAuth (OAuth 2.1 does exist for MCP servers you register as bot tools), and there are no official npm or PyPI SDK packages yet — call the endpoints with any HTTP client.

Read the signing secret

GET /webhooks/{id}/signing-secret

curl "https://api.youraiconnector.com/v1/webhooks/0/signing-secret?apiKey=YOUR_API_KEY"

Response

{
  "success": true,
  "webhook_id": "0",
  "signing_enabled": true,
  "signing_secret": "whsec_1a2b3c...",
  "signing_secret_created_at": "2026-07-15T09:30:00.000Z"
}

When signing is off, signing_enabled is false and signing_secret is null.

Generate or rotate the signing secret

POST /webhooks/{id}/signing-secret

Creates a secret (turning signing on) or replaces the existing one. Returns the new secret.

curl -X POST "https://api.youraiconnector.com/v1/webhooks/0/signing-secret?apiKey=YOUR_API_KEY"

Response

{
  "success": true,
  "webhook_id": "0",
  "signing_enabled": true,
  "signing_secret": "whsec_9f8e7d...",
  "signing_secret_created_at": "2026-07-15T10:00:00.000Z"
}

Rotation takes effect immediately — the next delivery is signed with the new secret only. Accept both secrets briefly while you roll the change out to a live endpoint.

You can also mint a secret at creation time by passing "generate_signing_secret": true to POST /webhooks; the response then includes a top-level signing_secret field.

Turn signing off

DELETE /webhooks/{id}/signing-secret

curl -X DELETE "https://api.youraiconnector.com/v1/webhooks/0/signing-secret?apiKey=YOUR_API_KEY"

Response

{
  "success": true,
  "webhook_id": "0",
  "signing_enabled": false
}

All three signing-secret routes require the Integrations edit permission, including the GET — the secret is a credential that can forge deliveries, so it is not exposed to read-only roles.


Retries

Optional, off by default, and set per subscription via the retries_enabled boolean on POST /webhooks or PUT /webhooks/{id}.

curl -X PUT "https://api.youraiconnector.com/v1/webhooks/0?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"retries_enabled": true}'

When enabled, a failed delivery is retried at 1m, 5m, 30m, and 2h after the first attempt (about 2h40m of coverage).

  • Retried: 5xx responses, timeouts, and connection failures.
  • Not retried: any 4xx. The receiver is rejecting the request itself, so replaying it unchanged only reproduces the rejection.

Retries make duplicate delivery possible — an endpoint that processed an event but timed out before responding will see it again. Dedupe on X-Webhook-Delivery, which is constant across attempts. This is why retries are opt-in.

The delivery-health counters count a whole delivery, not each attempt: a failure is recorded only once every retry is exhausted, so enabling retries does not make the automatic disable trigger sooner.


Errors

All errors use the standard envelope:

{
  "success": false,
  "error": "Webhook not found"
}

Common cases: a URL that is not allowed, an empty/invalid subscribed_to, or missing fields return 400; an unknown id or name returns 404; and a 403 means webhooks are not enabled for your account. See Errors for the full list.


Next steps