> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agntix.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Event types

> Reference for every webhook event Agntix can deliver, with payload schemas and example bodies.

<Note>
  Every endpoint on this page requires either a Clerk-issued JWT (`Authorization: Bearer <token>`) or
  an organization API key (`x-api-key: pk_…`). Anonymous calls return `401 Unauthorized`. See
  [Authentication](/authentication) for the full setup.
</Note>

Agntix delivers webhooks via [Svix](https://svix.com). Configure endpoints from the dashboard's **Developer** section — see [Webhooks overview](/webhooks/overview).

Voice and chat sessions emit two events **today**: `session.created` (when the call/conversation starts) and `session.ended` (when it closes, with the full post-call analysis attached). The other events on this page are on the roadmap — documented so you can design against them, but **not delivered as webhooks yet**.

<Info>
  **Available now:** `session.created` and `session.ended`. Every event marked <Badge color="orange">Planned</Badge> below is **not emitted to webhook endpoints yet** — don't build hard dependencies on it. `session.updated` and `knowledge_store.updated` currently exist only as in-app real-time (SSE) signals, not as webhooks.
</Info>

<Warning>
  Treat the top-level `id` (session UUID) as the **idempotency key**. Don't double-process the same `id`. Webhooks may be retried on non-`2xx` responses — respond `2xx` once you've durably enqueued the event.
</Warning>

***

## `session.created` <Badge color="green">Available</Badge>

Fired when a session is **initiated** — for outbound voice calls, this is *before pickup*. Use it to lock the originating task as "in-flight".

<ResponseField name="id" type="string">The session id (UUID). Use as idempotency key.</ResponseField>
<ResponseField name="eventType" type="string">Constant: `"session.created"`.</ResponseField>
<ResponseField name="type" type="enum">`VOICE`, `CHAT`.</ResponseField>
<ResponseField name="platform" type="enum">`PHONE`, `WEB`, `WHATSAPP`.</ResponseField>
<ResponseField name="direction" type="enum">`INBOUND`, `OUTBOUND`.</ResponseField>
<ResponseField name="state" type="enum">Session state at emission (`OPEN` for `session.created`).</ResponseField>
<ResponseField name="agentId" type="string">The agent id (UUID).</ResponseField>
<ResponseField name="createdAt" type="string">RFC 3339 UTC timestamp.</ResponseField>

<ResponseField name="metadata" type="object">
  Channel and session metadata. Includes `customerPhoneNumber`, `roomName`, and `sessionVariables` — see below.
</ResponseField>

<ResponseField name="metadata.sessionVariables" type="object">
  The `session_variables` you passed at trigger time, echoed back **verbatim** — regardless of whether the prompt referenced them. Use these as your correlation IDs (e.g. `crmContactId`, `taskId`, `campaignId`).
</ResponseField>

```json theme={null}
{
  "id": "4981804c-2658-4893-84ff-9c2fa90fb3c2",
  "eventType": "session.created",
  "type": "VOICE",
  "platform": "PHONE",
  "direction": "OUTBOUND",
  "state": "OPEN",
  "agentId": "40235145-aa3d-443e-9d0c-841dd0716a2f",
  "createdAt": "2026-04-08T05:00:01.130Z",
  "metadata": {
    "customerPhoneNumber": "+971501234567",
    "roomName": "outbound_sip_40235145-aa3d-443e-9d0c-841dd0716a2f^__^7yoamyb",
    "sessionVariables": {
      "crmContactId": "CRM-7728",
      "customerName": "Alex Morgan"
    }
  }
}
```

***

## `session.ended` <Badge color="green">Available</Badge>

Fired once when a session closes (manually, via timeout, on call hangup, or on `ttl` expiry). Carries the full post-call **`analysis`** block — see [Post-call analysis](/guides/post-call-analysis) for what each field means.

<ResponseField name="id" type="string">The session id (UUID). Same as in `session.created`.</ResponseField>
<ResponseField name="eventType" type="string">Constant: `"session.ended"`.</ResponseField>
<ResponseField name="state" type="enum">`CLOSED`.</ResponseField>
<ResponseField name="createdAt" type="string">Session start time.</ResponseField>
<ResponseField name="lastActivityAt" type="string">Most recent activity timestamp.</ResponseField>
<ResponseField name="endedAt" type="string">When the session closed.</ResponseField>
<ResponseField name="metadata" type="object">Same shape as `session.created`. `metadata.sessionVariables` is echoed verbatim.</ResponseField>
<ResponseField name="analysis.summary" type="string">One-paragraph summary of the call.</ResponseField>

<ResponseField name="analysis.intent" type="object">
  Best-fit intent classification — `conversation.intent`, `confidence`, `explanation`. Present only when **Intent Recognition** is enabled on the agent.
</ResponseField>

<ResponseField name="analysis.resolution" type="object">
  `classification` (`RESOLVED` / `PARTIALLY_RESOLVED` / `UNRESOLVED` / `NOT_APPLICABLE`), `confidenceScore`, `satisfiedChecklistItems`, `evidence`, `explanation`. Present only when **Resolution Analysis** is enabled.
</ResponseField>

<ResponseField name="analysis.sentiment" type="object">
  `conversation.sentiment` (`POSITIVE` / `NEUTRAL` / `NEGATIVE`), `score` (-1.0 to +1.0), `detectedLanguage`. Present only when **Sentiment** is enabled.
</ResponseField>

<ResponseField name="analysis.extractedData" type="object">
  Map of `{ <fieldName>: <value> }` for every field defined in the agent's **Data Extraction** schema.
</ResponseField>

<ResponseField name="recordingUrl" type="string">Link into the dashboard to play back the call recording.</ResponseField>

```json theme={null}
{
  "id": "4981804c-2658-4893-84ff-9c2fa90fb3c2",
  "eventType": "session.ended",
  "type": "VOICE",
  "platform": "PHONE",
  "direction": "OUTBOUND",
  "state": "CLOSED",
  "agentId": "40235145-aa3d-443e-9d0c-841dd0716a2f",

  "createdAt": "2026-04-08T05:00:01.130Z",
  "lastActivityAt": "2026-04-08T05:00:01.130Z",
  "endedAt": "2026-04-08T05:00:10.546Z",
  "updatedAt": "2026-04-08T05:00:01.130Z",

  "metadata": {
    "customerPhoneNumber": "+971501234567",
    "roomName": "outbound_sip_40235145-aa3d-443e-9d0c-841dd0716a2f^__^7yoamyb",
    "sessionVariables": {
      "crmContactId": "CRM-7728",
      "customerName": "Alex Morgan"
    }
  },

  "analysis": {
    "summary": "Customer confirmed delivery slot for Wednesday afternoon.",
    "intent": {
      "conversation": { "intent": "Slot-Confirmed", "confidence": 0.92, "explanation": "…" },
      "message":      { "intent": null, "confidence": 0, "explanation": "…" }
    },
    "resolution": {
      "classification": "RESOLVED",
      "confidenceScore": 0.88,
      "status": "Completed",
      "satisfiedChecklistItems": ["delivery_slot_agreed"],
      "evidence": ["Customer: yes Wednesday afternoon works."],
      "explanation": "Customer agreed to a specific slot."
    },
    "sentiment": {
      "conversation": { "sentiment": "POSITIVE", "score": 0.4, "detectedLanguage": "en", "explanation": "…" },
      "message":      { "sentiment": null, "score": 0, "detectedLanguage": "N/A", "explanation": "…" }
    },
    "extractedData": {
      "slotConfirmed": true,
      "preferredDay": "Wednesday",
      "preferredTimeWindow": "Afternoon",
      "callbackRequested": false,
      "callOutcome": "Confirmed"
    }
  },

  "recordingUrl": "https://app.agntix.ai/en/logs-history?opened=4981804c-2658-4893-84ff-9c2fa90fb3c2"
}
```

<Note>
  The webhook is **summary-grade**, not transcript-grade. The full message-by-message transcript is not included, and some long-call fields may be truncated. For the complete picture, pull from [`GET /api/v1/chat/sessions/{id}/messages`](/api-reference/chat/messages/list).
</Note>

***

## `session.updated` <Badge color="orange">Planned</Badge> <Badge color="gray">chat</Badge>

Emitted for **chat** sessions when state, agent assignment, intelligence summary, or analytics records change mid-session. Voice/outbound calls do **not** emit this event — they go straight from `session.created` to `session.ended`.

<Note>Today this fires only as an **in-app real-time (SSE)** signal for live dashboards. It is **not yet delivered to webhook endpoints**.</Note>

<ResponseField name="id" type="string">Session id (UUID).</ResponseField>
<ResponseField name="eventType" type="string">Constant: `"session.updated"`.</ResponseField>
<ResponseField name="changedFields" type="string[]">Which fields changed (e.g. `["state", "intelligence"]`).</ResponseField>
<ResponseField name="state" type="enum">`OPEN`, `CLOSED`, `EXPIRED`.</ResponseField>

```json theme={null}
{
  "id": "ses_01J2K…",
  "eventType": "session.updated",
  "createdAt": "2026-04-28T11:43:00Z",
  "changedFields": ["intelligence"],
  "state": "OPEN"
}
```

***

## `message.added` <Badge color="orange">Planned</Badge>

A user, assistant, or tool message was appended to a session.

<Note>Not emitted to webhook endpoints yet. Until it ships, poll [`GET /api/v1/chat/sessions/{id}/messages`](/api-reference/chat/messages/list) or consume the in-app real-time stream.</Note>

<ResponseField name="data.sessionId" type="string" />

<ResponseField name="data.messageId" type="string">The message id (`msg_…`).</ResponseField>
<ResponseField name="data.role" type="enum">`USER`, `ASSISTANT`, `TOOL`, `SYSTEM`.</ResponseField>
<ResponseField name="data.content" type="string">The text content (may be empty for tool calls).</ResponseField>
<ResponseField name="data.toolCall" type="object">Present only for `TOOL` role.</ResponseField>

```json theme={null}
{
  "id": "evt_2dQs…",
  "type": "message.added",
  "createdAt": "2026-04-28T11:42:13Z",
  "data": {
    "sessionId": "ses_01J2K…",
    "messageId": "msg_01J2L…",
    "role": "ASSISTANT",
    "content": "Your order 1234 shipped this morning."
  }
}
```

***

## `usage.updated` <Badge color="orange">Planned</Badge>

Aggregate usage metrics for the org changed (rolled up every 60s).

<Note>Not emitted to webhook endpoints yet.</Note>

<ResponseField name="data.period" type="object">`{ "start": ISO, "end": ISO }`.</ResponseField>

<ResponseField name="data.metrics" type="object">
  `{ "chat_messages": number, "voice_seconds": number, "knowledge_chunks": number }`.
</ResponseField>

***

## `subscription.updated` <Badge color="orange">Planned</Badge>

Plan change, payment method update, cancel, or resume.

<Note>Not emitted to webhook endpoints yet.</Note>

<ResponseField name="data.subscriptionId" type="string" />

<ResponseField name="data.status" type="enum">`active`, `past_due`, `canceled`, `paused`.</ResponseField>
<ResponseField name="data.currentPlan" type="string">e.g. `growth`.</ResponseField>

***

## `knowledge_store.updated` <Badge color="orange">Planned</Badge>

A document finished processing (chunked + embedded + indexed).

<Note>Today this fires only as an **in-app real-time (SSE)** signal (named `knowledge.store.updated`). It is **not yet delivered to webhook endpoints**.</Note>

<ResponseField name="data.knowledgeStoreId" type="string" />

<ResponseField name="data.documentId" type="string" />

<ResponseField name="data.status" type="enum">`UPSERTED`, `FAILED`.</ResponseField>

***

## Delivery semantics

* **At-least-once.** Always idempotent on the top-level `id`.
* **Order is not guaranteed.** Use `createdAt` if you need to sort.
* **Retries:** exponential backoff up to 24h. After 24h the event is dead-lettered (visible in the dashboard).
* **Timeout:** your endpoint must respond `2xx` within **15 seconds**.

See [Verifying signatures](/webhooks/verify-signatures) for how to authenticate incoming requests.
