scoco.ai

Webhooks: events from scoco to your systems

scoco sends every event, such as an ended call or a booked appointment, as a signed JSON message to an HTTPS address of your choice.

For Owner, Admin

Open in dashboard
On this page

With webhooks, scoco tells your own systems what happens as it happens, for example "call ended" or "appointment booked". You enter an HTTPS address, and scoco sends every event there as a signed JSON message. Zapier, Make and n8n connect the same way.

Add an endpoint

  1. Open SettingsIntegrations.
  2. In the Automation card, click Webhook endpoint.
  3. Enter the Address. It must start with https://.
  4. If you like, choose Events. Without a choice you get all of them.
  5. Click Add.
  6. The Signing key window shows the key (whsec_…) exactly once. Save it with Copy key and close with I have saved the key.

The key cannot be shown again. If you lose it, remove the endpoint with Remove in its menu and add it again.

Events

EventSent when
call.completeda call has ended, with its result, duration and the caller's number
call.summary_readythe AI summary of a call is ready
conversation.completeda WhatsApp conversation has ended, with its result and the customer's number; only with WhatsApp active
conversation.summary_readythe AI summary of a WhatsApp conversation is ready; only with WhatsApp active
ticket.status_changedthe status of a call or WhatsApp conversation changes: once automatically after every conversation (with initial: true), then on every change in the Inbox, marking as spam included
ticket.assigneda call or WhatsApp conversation is assigned, reassigned or unassigned (then to: null)
message.sms_receiveda text message arrives at your scoco number
message.sms_sentscoco sends a text message or tries to: check data.status, it can be failed
appointment.bookedyour assistant books an appointment
appointment.cancelledan appointment is cancelled: by the caller on the phone, by the customer on WhatsApp or by your team in the dashboard
appointment.rescheduledan appointment is moved: by the caller on the phone, by the customer on WhatsApp or by your team in the dashboard
contact.createda contact is created, by hand or automatically from a conversation; an import sends nothing

Events are only ever added, none is removed. Your receiver should simply ignore events it does not know.

Test calls made from the dashboard trigger no events, and neither does a change you make directly in your calendar.

What a message looks like

Every delivery is a POST with Content-Type: application/json. The X-Scoco-Event header names the type, here a call.completed:

{
  "id": "5f0c…",
  "type": "call.completed",
  "created_at": "2025-08-06T11:58:39+00:00",
  "tenant_id": "a319…",
  "data": {
    "call_id": "…",
    "channel": "voice",
    "assistant_id": "…",
    "caller_e164": "+4930123456",
    "started_at": "2025-08-06T11:56:01+00:00",
    "ended_at": "2025-08-06T11:58:33+00:00",
    "duration_seconds": 151.48,
    "result": "message_taken",
    "contact_id": null
  }
}

What data holds depends on the event. It always carries the relevant IDs; the dashboard shows all the details. Fields in data are only ever added, too. A field can be null when the value is missing, and duration_seconds is a decimal number.

The id stays the same on every retry, so you can recognise a message you have already processed.

call.summary_ready and conversation.summary_ready contain language and original. If the conversation ran in another language than your assistant's, language is its code, such as ar, otherwise null. Name, company and request arrive in your account's language, names in Latin script. Where that differs from the caller's own words, those are in original, under the same keys, such as the name in Arabic script under caller_name. Otherwise original is null.

call.summary_ready contains caller_silent. It is true when the caller hung up without saying anything. summary is then a fixed sentence instead of an AI summary, and the fields that otherwise come from the conversation are null.

Both appointment changes carry the same appointment_id as the appointment's appointment.booked. appointment.cancelled contains starts_at, ends_at and cancelled_by (caller or user). appointment.rescheduled contains previous_starts_at, the new starts_at and ends_at, and rescheduled_by (caller or user). Both are sent only once the change has reached your calendar.

Verify the signature

Every delivery carries the header X-Scoco-Signature: t=1754481521,v1=5257a869e7…. v1 is an HMAC-SHA256 (hexadecimal) of the string <t>.<raw request body>. The key is your signing key exactly as it was shown, whsec_ included. "Standard Webhooks" libraries strip whsec_ and decode the rest; with them the check fails.

t is the time of signing, not created_at: every retry is signed again. Reject messages whose t is more than about 5 minutes old; that protects you against replayed messages.

Verify the bytes as they arrive, not JSON you serialised again: a single changed space changes the signature. In Node.js, express.raw({ type: "application/json" }) gives you the raw body; in PHP, file_get_contents('php://input').

import hashlib, hmac, time

def verify(secret: str, body: bytes, header: str, tolerance: int = 300) -> bool:
    try:
        parts = dict(p.split("=", 1) for p in header.split(","))
        t, v1 = int(parts["t"]), parts["v1"]
    except (KeyError, ValueError):
        return False
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected.encode(), v1.encode())

Delivery, retries, switching off

  • A delivery counts as successful when your endpoint answers with a 2xx status within 10 seconds. Answer right away and process the message afterwards.
  • If a delivery fails, scoco tries again: up to 5 attempts in total, the first included, with pauses of about 1 minute, 5 minutes, 15 minutes and 1 hour.
  • If 20 deliveries in a row fail and the first of them is at least 24 hours old, scoco switches the endpoint off and emails the owners. Enable switches it back on. Events from the time in between are not delivered later.
  • Log shows the latest 50 deliveries. A delivery is deleted after 30 days, and removing the endpoint deletes its log too.
  • Test sends a signed ping event to your endpoint. It does not appear in the log and does not count as a failure.

Data protection

The messages contain personal data such as callers' numbers, names and summaries. By adding an endpoint, you instruct scoco to send this data to the system you name. Make sure it is protected there.