SiteCallDevelopers

Events

Every change, as an event.

Events arrive at your webhooks and stay in your application's event log, which you can page at any time to recover a gap or to poll instead of receiving.

Fields every event carries

FieldMeaning
idUnique per event; dedupe by it (delivery is at least once).
typeThe event type, below.
occurredAtWhen the change was written (ISO 8601). It can precede the commit: order by sequence.
applicationIdYour application.
workspaceIdSiteCall's workspace id.
externalWorkspaceIdYour own id for that workspace (null only for a number in SiteCall's inventory).
sequenceIncreases within the event's stream, in commit order. Gaps are normal.
callIdCall, queue, recording, transcript and voicemail events.
externalUserIdEvents about one of your users, by your own id.

Types

TypeStreamWhen
call.startedcallA call reached the engine (inbound) or was placed (outbound).
call.ringingcallOne ring of people's devices: members and application users rung, the offer, its expiry.
call.answeredcallThe caller's leg was answered; call.state says by what (a person, the AI, a menu, voicemail).
call.connectedcallA party took the call (a device, the AI, a forwarded number, an outbound destination); leg names it. Again after each answered transfer.
call.transferredcallA blind transfer, or a warm one at completion: target, warm, actor.
call.endedcallThe call ended: the state it ended from, whether a person or AI connected, duration.
queue.enteredcallThe call entered a queue (waiting, offered or a callback).
queue.leftcallThe call left a queue: connected, abandoned or overflowed.
agent.state_changedagentAn agent's state changed (available, offered, on_call, wrap_up, away, offline).
recording.readycallA call recording or voicemail is stored; fetch a link with the recordings API.
transcript.readycallA recording's transcript: text, per-party segments (bounded), channels.
voicemail.readycallA caller left a voicemail.
message.receivednumberAn SMS or MMS arrived on a workspace number.
message.statusnumberAn outbound message's status changed (sent, delivered, failed).
number.updatednumberA number's status, lanes, texting registration or workspace changed.

Fields are camelCase and only ever added. A state-like field (a call's state or direction, a queue entry's state, an agent's state, a recording's kind, a number's status) may take a value not listed today, and new event types may appear: ignore fields you do not know, treat an unknown value as "other", and acknowledge an unknown type. The client's verifyWebhook and event log return such an event as { type: "unrecognized" } instead of failing.

Streams and order

Each event belongs to one stream: call:<callId>, number:<numberId> or agent:<memberId>. Within a stream, sequence follows commit order, which is the order webhook deliveries take. Across streams there is no order: two calls' events interleave freely.

The event log

GET /api/v1/events (an application key, scope webhooks:*) pages your events in log order, the order their transactions committed. A position it handed out is never overtaken: an event that commits later is always served after it, so a poller that follows nextCursor misses nothing. The log serves only committed transactions older than every one still running, so it can lag behind a slow transaction, but it never skips.

QueryMeaning
afterA nextCursor (resume exactly after it), or an event id you received by webhook (resume from where it committed: some events around it come again, so dedupe by id). Absent: from the start of the log.
limit1–500, default 50.
typeComma-separated event types; default all.

The answer is { data, nextCursor, hasMore }. hasMore says more events are listed already: ask again at once with after=nextCursor. When it is false, keep nextCursor and ask again later to poll. Within a page one stream's events may interleave with others', so order a stream by sequence. A workspace header narrows the log to that workspace. A position the log never handed out is a 400 invalid_cursor.

gap recovery

import { createSiteCallClient } from "@sitecall/cli";

const sitecall = createSiteCallClient({ apiKey: process.env.SITECALL_API_KEY });

let after = await loadCursor(); // a nextCursor you stored, or the last event id you processed
for await (const page of sitecall.events.pages({ after, type: ["call.ended", "transcript.ready"] })) {
  for (const event of page.data) {
    if (event.type !== "unrecognized" && !(await alreadyHandled(event.id))) {
      await handle(event);
    }
  }
  if (page.nextCursor) await saveCursor(page.nextCursor);
}

GET /api/v1/events

curl "https://sitecall.ai/api/v1/events?after=c1.20314.7&limit=100" \
  -H "authorization: Bearer $SITECALL_API_KEY"

{ "data": [ { "id": "…", "type": "call.ended", "sequence": 6, … } ],
  "nextCursor": "c1.20322.1",
  "hasMore": false }