Webhooks

Webhooks

AnyoneIn POSTs a signed confirmation.updated event to your webhookUrl when a confirmation gets an outcome, gives up or is cancelled.

Set webhookUrl when you create a confirmation and AnyoneIn calls it whenever there's something to act on. There's one event type, confirmation.updated, and its data is the whole confirmation, so you rarely need to call back for more.

When it fires

What happeneddata.status
The customer confirmed, or named someone else who'll be inconfirmed
The customer picked one of your rescheduleOptionsrescheduled
A clear answer that needs a person: a different day, cancel, wrong person, opt-out, wants a personaction_needed
Every step went out and nobody answeredunreachable
You cancelled itcancelled

It doesn't fire for each email, text or call going out, or while the conversation is still going: you hear about outcomes, not chatter. If a customer writes again after the confirmation has closed and the agent reads a new answer, you get another event with the new status and outcome.

The request

Content-Typeheader

application/json.

X-AnyoneIn-Signatureheader

sha256= followed by the lowercase hex HMAC-SHA256 of the raw request body, keyed with your webhook signing secret.

typestring

Always confirmation.updated today.

dataobject

The full confirmation object, including events.

Verifying signatures

Always check the signature before you trust a webhook. Compute the HMAC-SHA256 of the raw body bytes with your signing secret, add the sha256= prefix, and compare it with the header using a constant-time comparison.

The two mistakes that break verification:

  • Parsing before hashing. Re-serialising the JSON changes the bytes. Hash the body exactly as it arrived.
  • Comparing with ==. Use timingSafeEqual, hmac.compare_digest or hash_equals, so the comparison doesn't leak timing.

Roadmap

The signature covers the body only; there's no timestamp in it yet. A signed timestamp for replay protection, and secret rotation with two valid secrets at once, are on the roadmap. Until then, treat events as idempotent (below) and only accept them over https.

Handling deliveries

  • Reply fast. Return any 2xx within 8 seconds, then do the work. The request times out after 8 seconds.
  • Be idempotent. You may see the same confirmation more than once, and a later event can change an earlier outcome. Key your handling on data.id with data.updatedAt, and let the newest win.
  • Match it back with data.externalId, your own reference.
  • Use data.outcome.summary as the note for your team. It's one plain sentence written for ops.

Delivery and retries

Each event is sent once. AnyoneIn records the result on the confirmation as webhook: { "status": 200, "at": "…" } for the response it got, or { "error": "…", "at": "…" } if it couldn't connect.

  • The URL must be https, and must resolve to a public address. Private and internal addresses are refused.
  • Redirects aren't followed. Point webhookUrl at the final URL.
  • The request times out after 8 seconds.

Roadmap

Automatic retries with backoff, a delivery log and a "resend" button are on the roadmap. Until they land, reconcile once a day: list recent confirmations and check any that closed without your system hearing about it. webhook.status tells you what we got back.

Acting on the outcome

StatusSuggested action in your system
confirmedMark the job ready for dispatch. Pass outcome.signerName to the crew
rescheduledMove the booking to order.date and order.window. The customer has already been told
action_neededCreate a task with outcome.summary. Check outcome.intent: opt_out means stop messaging them
unreachableHave someone call before dispatch, or hold the job
cancelledNothing

Base URL https://api.anyonein.co.uk/v1 is a placeholder until the production domain is confirmed. Found a mistake? Tell us.