Saylidocs
Download

Webhooks

Sayli pushes events to your systems, signed, so you can build on top of the call


A call ends, and a brief lands in your own system a second later. No polling, no waiting. Sayli sends the event to an endpoint you own, and your tools pick it up from there.

Webhooks are on the Team plan and up.

Events, pushed to you

You give Sayli an HTTPS endpoint. When something happens on a call, Sayli sends a signed POST to that endpoint. You decide what to do with it: file a ticket, post to a channel, update a record.

Your endpoint stays private

Endpoints must be public HTTPS URLs. Sayli checks the address before sending and will not deliver to internal or private network addresses.

What we emit

Two events are live today.

brief.generated
A meeting brief is ready, with the headline, summary, and action items.
mcp.tool_called
Your inbound MCP server answered a tool call from a connected AI client.

More events are planned, including meeting.started, meeting.ended, and action_item.created. They are not sent yet. Subscribing to them today does nothing until they ship, and shipped events are announced in the changelog.

The delivery

Every delivery is a POST with Content-Type: application/json and a sayli-webhook/1.0 user agent, plus these headers:

Sayli-Event
The event name, like brief.generated.
Sayli-Timestamp
Unix seconds when the delivery was signed.
Sayli-Signature
Hex HMAC-SHA256 of the timestamp and body. See below.
Sayli-Delivery-Id
A unique id for this delivery, useful for idempotency and support.

The body always has the same three fields: the event name, when it happened, and the event's data.

json
{
  "event": "brief.generated",
  "occurred_at": "2026-06-29T15:42:10.482910+00:00",
  "data": {
    "meeting_id": "0d9e07a2-5b1f-4c8e-9f3a-2f6d1c8b4a70",
    "title": "Weekly pipeline review",
    "headline": "Renewal is on track, security review is the open risk",
    "summary": "…",
    "action_items": ["Send the SOC 2 report to Dana"],
    "confidence": 0.86
  }
}

Verify the signature

The signature covers the timestamp and the raw body, joined by a dot: HMAC-SHA256(secret, "{Sayli-Timestamp}.{body}"), hex-encoded, in the Sayli-Signature header. Signing the timestamp lets you reject replays, not just tampering.

To verify a delivery:

  1. Read the raw request body before parsing any JSON.
  2. Compute the HMAC over timestamp + "." + body with your endpoint's secret.
  3. Compare against Sayli-Signature with a constant-time comparison.
  4. Reject the delivery if the timestamp is older than your replay window.
python
import hashlib
import hmac
import time
 
def verify(headers, raw_body: bytes, secret: str) -> bool:
    timestamp = headers["Sayli-Timestamp"]
    signature = headers["Sayli-Signature"]
    signed = timestamp.encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        return False
    return abs(time.time() - int(timestamp)) <= 300  # 5 minute replay window
Reject anything that does not match

Treat a missing or wrong signature as a request you did not send. Do not process the body.

When deliveries fail

Delivery is one attempt, and there are no retries yet. If your endpoint is down or slow (Sayli waits up to 8 seconds), that delivery is gone. Anything a webhook tells you can also be read back from the API, so a missed brief.generated is recoverable by fetching the brief.

After 10 failures in a row, Sayli disables the endpoint automatically so events stop piling up against a dead URL. Fix the endpoint, then turn it back on from settings.

Return fast

Respond with a 2xx as soon as you receive the event, then do the slow work in the background. A response that beats the 8 second window is a delivery that counts.

Next steps