Open Automations in the left sidebar and pick the Webhooks tab. The page describes itself accurately: "HTTP callbacks that fire when contact, message, or workflow events occur. Configure per-event subscriptions and inspect delivery history per row." A subscription is a standing instruction: when this kind of thing happens, POST it to my server. Every delivery is signed, retried on failure, and recorded — and all three of those are things you will want to understand before you point a production endpoint at it.
Three things are called a webhook — this tab is one of them
The word does a lot of work in IcloneU, and the three surfaces point in different directions. Getting the direction wrong is the single most common reason someone lands on the wrong page.
| Which one | Direction | Where it lives |
|---|---|---|
| Webhook subscriptions | IcloneU → your server | This tab. You give IcloneU a URL and it posts events to it. |
| Channel webhooks | Meta, Telegram → IcloneU | Not a page you manage. These are registrations the channel holds so inbound customer messages reach IcloneU. When one breaks, messages stop arriving. |
| The inbound webhook trigger | Anything → one workflow | A trigger inside the Workflows editor. It hands you a secret URL that an outside system POSTs to in order to start a single flow. |
Everything below is the first row only. If inbound messages from your customers have stopped arriving, the guide you want is When messages stop arriving; if you want an outside system to start one workflow, it is the inbound webhook trigger in Workflow triggers.
Creating a subscription
Click New Subscription
Top right of the Webhooks tab. The dialog has three fields, and only two of them matter to the machine.
Enter the Target URL
Target URL must be an absolute http or https URL — the placeholder shows the shape, https://example.com/webhook. A relative path or a bare hostname leaves the Create button disabled rather than failing later.
Describe it
Description is free text and optional. The grid never shows it, but every API read returns it, so it is the note-to-self that survives a change of staff — name the system on the other end, the way the placeholder suggests.
Tick the Event Types
Event Types is a scrolling list of the twelve subscribable events. At least one must be ticked before Create turns on. Tick only what you will actually process — every extra event is another delivery your endpoint has to answer inside 15 seconds.
Save the signing secret
Creating the subscription shows its signing secret once, in a dialog with a Copy button and a plain-language reminder of why it matters: "Keep this secret somewhere safe. Your system uses it to confirm that the events we send really come from IcloneU." It is 64 hexadecimal characters. Store it as a secret in your own system — it never appears again, in the grid or the API.
Heads upLose the secret and you cannot read it back. The recovery is Rotate secret, which mints a new one and immediately invalidates the old — so your receiver stops verifying until you deploy the new value.
The create dialog has no Status field — a new subscription is always born Active. The field appears when you reopen an existing subscription with Edit (or by double-clicking its row), and that is where you pause one or bring an Unhealthy one back.
The twelve events
The list is fixed — you cannot define your own event — and every name carries a .v1 suffix so a future shape change can ship as .v2 alongside it rather than breaking your parser.
| Event | Fires when |
|---|---|
| message.received.v1 | A message arrived from a contact. |
| message.sent.v1 | A message went out to a contact. |
| conversation.started.v1 | A contact opened a conversation that did not exist before. |
| assistant.reply_generated.v1 | A Clone produced a reply. |
| assistant.handoff_requested.v1 | A conversation asked for a human. |
| assistant.handoff_resolved.v1 | The human handover ended and control went back to the Clone. |
| contact.created.v1 | A new contact record was created. |
| contact.updated.v1 | An existing contact changed. |
| tag.applied.v1 | A tag was put on a contact. |
| tag.removed.v1 | A tag was taken off a contact. |
| custom_field.changed.v1 | A custom field on a contact changed value. |
| automation.run.completed.v1 | A workflow run finished. |
There is no conversation.closed event, because conversations are never closed: a contact who comes back resumes the conversation that already exists, so there is no moment to fire on. Use assistant.handoff_resolved.v1 if what you actually want is "the human is done". And webhook.test.v1 — the event Send test fires — is deliberately not in the subscribable list: it is delivered to any subscription regardless of what is ticked, so your receiver must handle it even though you cannot subscribe to it.
If you would rather read the list from code than from a guide, GET /api/public/webhooks/event-types returns both sets: event_types for the twelve, and system_event_types for the test event.
What your endpoint receives, and how to verify it
Every delivery is an HTTP POST with a JSON body and the same five headers. The body is always the same envelope, with the event-specific detail nested under data.
| Header | What it carries |
|---|---|
| X-IcloneU-Event | The event type, e.g. contact.created.v1. |
| X-IcloneU-Delivery-Id | A GUID for the delivery record. Every retry of the same delivery repeats it, so it is what makes your handler idempotent; a Redeliver is a new record and gets a new id. |
| X-IcloneU-Timestamp | Unix seconds at the moment of sending. It is part of the signed material, so you cannot substitute your own. |
| X-IcloneU-Signature | The proof. sha256= followed by a lowercase-hex HMAC-SHA256. |
| User-Agent | IcloneU-Webhook/1.0. |
The envelope: id (a GUID for the event itself), event (the type), account_id, occurred_at (an ISO-8601 timestamp) and data. Field names are snake_case throughout, and null fields are omitted rather than sent as null.
The signature is computed over the literal string {timestamp}.{body} — the timestamp from the header, a full stop, then the request body exactly as it arrived on the wire. Keyed with your signing secret, HMAC-SHA256, rendered as lowercase hex, prefixed with sha256=. If your framework parses the JSON and you re-serialise it to verify, the bytes will differ and every signature will look wrong. Capture the raw body before anything touches it, and compare the result in constant time.
Answer with any 2xx and the delivery is a success. Anything else — a 3xx, a 4xx, a 5xx, a timeout, a refused connection, an expired certificate — is a failure and starts the retry ladder. IcloneU waits 15 seconds for your response. The first 4 KB of your response body is stored with the delivery, but nothing in the product or the API shows it back to you today — all you can read is the status code, in the Code column of the delivery history. Keep your own logs on the receiving end; that is where the reason will be.
Retries, abandonment, and the Unhealthy trap
A failed delivery is retried on a widening ladder: 1 second, 10 seconds, 1 minute, 10 minutes, 1 hour, 6 hours, then 24 hours. That is eight attempts in total, spread across roughly 31 hours. After the eighth, the delivery is abandoned — it will never be tried again on its own, and only Redeliver can bring it back.
Separately from any single delivery, the subscription keeps a running count of consecutive failures. A success resets it to zero and stamps Last Delivered. Ten consecutive failures flip the subscription's status to Unhealthy.
Only an Active subscription is fed. A Paused or Unhealthy one is skipped when new events are enqueued and skipped when pending retries are picked up — so an Unhealthy subscription receives nothing at all, which means nothing can ever succeed, which means the failure counter never resets. It stays Unhealthy until a person opens it with Edit and sets Status back to Active. Fix your endpoint first, confirm it with Send test, then re-activate.
Events that occur while a subscription is Paused or Unhealthy are not queued for later. They are simply never enqueued for that subscription, so there is no backlog waiting when you re-activate it — the gap in your data is permanent. If continuity matters, pause for as short a window as you can and backfill from the API afterwards.
On the sending side, deliveries are picked up every 5 seconds, up to 50 per cycle with at most 8 in flight at once. A slow receiver therefore slows your own deliveries rather than IcloneU's — another reason to answer 200 immediately and do the real work off a queue.
The grid and the five row actions
The grid lists Target URL with the status pill beside it, then Events (how many are ticked), Failures (the consecutive-failure count), Last Delivered and Created. Select a row and five actions light up.
- Edit — reopens the dialog with Status added. This is where you pause a subscription instead of deleting it, and the only way back from Unhealthy. Double-clicking the row does the same thing.
- Deliveries — the last 100 attempts, newest first, as Event, Status, Code, Attempt and Time, with a Redeliver button on every row.
- Send test — posts a signed webhook.test.v1 to the URL right now, synchronously, and tells you the HTTP status it got back: "Test event sent (HTTP {0})." on success, "Test delivery failed: {0}." otherwise. It ignores the subscription's status, so it works on a Paused or Unhealthy row — which is exactly what makes it the right check before re-activating one. The attempt is logged in the delivery history and is never retried.
- Rotate secret — mints a new signing secret and shows it once. The confirmation states the risk in the right order: "A new signing secret will be generated and shown once. The current secret stops working immediately, so update your receiver before rotating."
- Delete — removes the subscription and stops every event on it. There is no undo and no recycle bin; if you only want to stop deliveries for a while, set Status to Paused instead.
In the delivery history, Status shows OK for a 2xx, Retry while another attempt is still scheduled, and Failed once the attempt count is spent. The three status pills on the grid rows — Active, Paused, Unhealthy — are the same story. All six are hard-coded in the product today and render in English whatever language the interface is in.
Redeliver does not re-run the original attempt. It queues the same event and the same payload as a brand-new delivery, starting again at attempt one with the full retry ladder ahead of it — and it is signed with whatever secret is current, not the one in force when the event first happened.
Doing all of this from code
Every screen above has an API equivalent under /api/public/webhooks, so an integration can provision its own subscription at install time instead of asking a human to click through this tab. Reading is open to any key; every write needs a Full Access key, because a key that can create webhooks can quietly forward your account's traffic somewhere else.
| Call | Level needed |
|---|---|
| List subscriptions, read one, list its deliveries, list the event types | Any key, including Read Only |
| Create, update or delete a subscription | Full Access |
| Send a test event, rotate the signing secret, redeliver a past delivery | Full Access |
Creation returns the signing secret in the response — the one and only time it appears — and every later read deliberately omits it. Rotation returns the new secret the same way. Delivery listings are properly paged: meta.total is the real number of stored deliveries for that subscription, not an estimate, so you can page to the end and know you got there. Access levels are covered in Public API keys.
The redeliver endpoint locates the delivery by scanning the subscription's history newest-first, and it stops after 1,000 rows. On a busy subscription an older delivery is past that horizon and comes back as a 404, even though it is still listed. If you need to replay something old, replay it from your own records rather than from ours.
Frequently asked
Yes. Each subscription is evaluated independently, so an event that matches three subscriptions produces three deliveries, each signed with its own secret. That is the clean way to send the same event to a staging endpoint and a production one.
Yes, if the subscription stayed Active — each failed delivery keeps retrying for about 31 hours, so a short outage catches up by itself. No, if the subscription went Paused or Unhealthy: nothing is enqueued for it at all while it is in either state, and there is no backlog when it returns.
Open the subscription with Edit and set Status to Paused. The URL, the event list and the signing secret all survive; setting it back to Active resumes new events. Deleting is the destructive option and takes the secret with it.
Almost always the signed material. It is {timestamp}.{body} with the raw body — not the body alone, not a re-serialised copy, and not the parsed object. Confirm you are reading the timestamp from X-IcloneU-Timestamp, that you are comparing against the value after sha256=, and that your hex is lowercase. Send test gives you a signed delivery on demand to check against.
No, and they point opposite ways. This tab makes IcloneU call you when something happens. The inbound webhook trigger gives an outside system a secret URL to call so it can start one specific workflow. Neither is the channel webhook that carries your customers' messages into IcloneU.
Last updated August 25, 2026 · Automations