Skip to main content
A webhook endpoint is a URL of yours that Invent calls when something in your workspace needs your systems to react. Instead of polling the API, your server is told. The catalog is deliberately narrow: events you have to act on — a contact who can no longer be reached, a send that did not land. Routine create/read/update traffic belongs to the API, not to a push feed. Use these to suppress a bad address in your own CRM, keep an opt-out in sync, or alert whoever owns a failed campaign.

Two secrets, two directions

Webhooks involve two different credentials. They are not interchangeable. Each endpoint gets its own signing secret. Rotating one endpoint’s secret never affects any other endpoint, and a leaked signing secret grants no access to your workspace.

Create an endpoint

Manage endpoints from Settings → Webhooks, or over the API with your API key:
Response
The full secret is returned only on create and on rotate. Every later read returns it masked. Store it in your secret manager right away — if you lose it, rotate to get a new one.
Endpoint URLs must be HTTPS, must not embed credentials (https://user:pass@…), and must not resolve to a private or loopback address.

The payload

Every delivery is a POST with a JSON body in one envelope shape: metadata plus a type and its data.
type tells you what happened; data is that event’s payload. Switch on type and you can add new subscriptions later without changing how you parse the envelope.

Events

chat.assigned covers real customer conversations only — internal workspace chats and the assistant playground never produce it.The payload is the full chat object. Assignment lives under state: state.assigned_user is who it landed on (absent means it was unassigned), alongside state.assigned_at, state.assistant, state.integration_id and state.ai_enabled.
broadcast.bounced, broadcast.complained and broadcast.suppressed are per recipient, not per broadcast — one delivery per affected address, carrying email, message_id, the provider reason, and contact_id when the address still maps to a contact.
The live list is always available at GET /orgs/c/webhooks/events.

Verifying a request

Always verify the signature before trusting a payload. Anyone can POST JSON at your URL; the signature is what proves Invent sent it. Every delivery carries these headers: The signature is an HMAC-SHA256, keyed with your endpoint’s signing secret, over the raw request body joined to the id and timestamp:
Two rules matter for a correct implementation:
  1. Sign the raw body bytes, exactly as received. Parsing the JSON and re-serializing it changes the bytes and the signature will not match.
  2. Compare in constant time (crypto.timingSafeEqual, hmac.compare_digest), and reject a timestamp older than a few minutes so a captured request cannot be replayed later.

Responding

Return any 2xx as soon as you have durably accepted the event — do the real work afterwards, out of the request. Requests time out after 10 seconds. Anything that is not a 2xx, plus timeouts and connection errors, counts as a failure and is retried.

Retries

Failed deliveries are retried with a widening backoff: 5s → 5m → 30m → 2h → 5h → 10h (7 attempts total). After the last attempt the delivery is marked FAILED. Delivery is at-least-once: the same event can arrive twice if your 2xx was lost on the way back. Deduplicate on id (or the x-webhook-id header) and make your handler idempotent. Ordering is not guaranteed — a retry can land after a newer event. Order by created_at if sequence matters. An endpoint that fails 30 deliveries in a row is disabled automatically, and the reason is shown on the endpoint. Re-enabling it (PATCH enabled: true) clears the failure streak.
Deliveries also pause while a workspace has no balance, and resume once it is topped up.

Inspecting and replaying

Every attempt is recorded for 7 days: status, response code, response body and duration.
Replay is how you backfill after an outage on your side, or reprocess events once your handler is fixed.

Rotating a secret

The response contains the new secret in full. The old secret stops signing immediately, so deploy the new one first, or accept a short window of rejected deliveries — they will be retried.