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

# Conversation ended trigger

> Trigger a workflow when an AI conversation ends — voice calls, chat on any messaging channel, or web agent sessions. Access transcript and summary.

Fires when any AI conversation ends — voice calls, chat conversations on any connected messaging channel, or web agent sessions. This is one of the most powerful triggers — it gives you access to the full conversation data including transcript, summary, disposition, and any extracted fields.

<Note>
  This trigger was previously called **Call Completed** (voice-only) and **Conversation Completed**. Existing workflows continue to work unchanged. The internal type identifier is still `conversationCompleted`.
</Note>

## When it fires

The trigger fires immediately after a conversation ends:

| Agent type | Fires after                                                                                                      |
| ---------- | ---------------------------------------------------------------------------------------------------------------- |
| Voice      | A phone call ends, regardless of outcome (answered, voicemail, no answer, or failed). Both inbound and outbound. |
| Chat       | An SMS, WhatsApp, Instagram DM, Messenger, email, or web chat conversation is finalized.                         |
| Web        | A web agent session (voice or chat widget embedded on your site) ends.                                           |

## Configuration

### Filters

Use filters to only trigger the workflow for specific conversations. All filters use AND logic.

| Filter field     | Options                                                                                                                                                          | Description                                                                                                                                                                                                   |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Agent Type       | voice, chat, web (multi-select)                                                                                                                                  | Only fire for the selected agent types. Leave empty to fire for all types.                                                                                                                                    |
| AI Agent         | Select agents                                                                                                                                                    | Only fire for conversations handled by specific agents                                                                                                                                                        |
| Call direction   | Inbound, Outbound                                                                                                                                                | **Voice only.** Only fire for calls in a specific direction                                                                                                                                                   |
| Call disposition | Free-text with `is` / `is not` / `contains` operators                                                                                                            | Match against the AI-classified disposition label on the conversation. Case-insensitive.                                                                                                                      |
| Call evaluation  | Pass, Fail                                                                                                                                                       | **Voice only.** Only fire based on the success evaluation result                                                                                                                                              |
| Min duration     | Number (seconds)                                                                                                                                                 | **Voice only.** Only fire if the call lasted at least this long                                                                                                                                               |
| Max duration     | Number (seconds)                                                                                                                                                 | **Voice only.** Only fire if the call was shorter than this                                                                                                                                                   |
| Extracted data   | Free-text per extraction field with `equals` / `not equals` / `contains` / `not contains` / `is empty` / `is not empty` / `greater than` / `less than` operators | Match against any extraction field configured on the agent (budget, lead status, appointment date, etc.). Available across voice, chat, and web. See [Extracted data filters](#extracted-data-filters) below. |

<Note>
  Voice-only filters (direction, evaluation, duration) only apply when Agent Type includes voice. If you select only `chat` or `web`, those filters are ignored. The Call disposition and Extracted data filters work on every channel.
</Note>

### Call disposition

Disposition is a free-text label generated by the AI at the end of every conversation, based on the agent's disposition prompt. Because the prompt produces arbitrary strings (`Hot Lead`, `Spanish Speaker — Transferred`, `Wrong Person`, etc.), the filter is a free-text input rather than a fixed dropdown.

| Operator   | Behavior                              |
| ---------- | ------------------------------------- |
| `is`       | Exact match, case-insensitive         |
| `is not`   | Negated exact match, case-insensitive |
| `contains` | Substring match, case-insensitive     |

Existing workflows configured with the legacy outcome dropdown values (`completed`, `positive`, `appointment_booked`, etc.) keep working — they're treated as exact-match `is` filters against the stored value.

For voice-specific routing on raw call ending reasons (voicemail, no answer, transferred, etc.), use a [Condition](/workflows/actions/condition) step on `{{trigger.conversation.endedReason}}` — see the voice-only fields table further down.

### Extracted data filters

Filter on any extraction field configured on the agent — budget, lead status, appointment date, sentiment, anything you've set up. The filter picker shows every extraction field defined on the agents your workflow listens to, and works across voice, chat, and web conversations.

| Operator                     | Behavior                                                                                                    |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `equals` / `not equals`      | Exact match, case-insensitive. Booleans and numbers are compared as strings (`true`, `42`).                 |
| `contains` / `not contains`  | Substring match, case-insensitive.                                                                          |
| `is empty` / `is not empty`  | Matches when the AI didn't extract a value (missing key, null, empty string, empty array, or empty object). |
| `greater than` / `less than` | Numeric comparison. Both sides must be numeric — non-numeric values never match.                            |

Behavior notes:

* Filtering only fires the workflow when the field matches. Filters are AND-combined — every filter must match.
* `not equals` and `not contains` against a missing value match (vacuous truth) — useful for "fire unless the AI said X".
* Extracted values are also available as variables in your actions — see [`{{trigger.conversation.metadata.fieldName}}`](#conversation-variables) further down.

### Examples

**Only completed outbound voice calls:**

* Filter: Agent Type → voice
* Filter: Call direction → Outbound
* Filter: Call disposition → `is` `completed`

**All chat conversations from a specific agent:**

* Filter: Agent Type → chat
* Filter: AI Agent → Select your support agent

**Voice or web sessions longer than 30 seconds:**

* Filter: Agent Type → voice, web
* Filter: Min duration → 30

**Every conversation on every channel:**

* No filters. Leave it empty.

**Hot leads only (any channel):**

* Filter: Extracted data → `lead_status` `contains` `hot`

**Conversations where appointment date was captured:**

* Filter: Extracted data → `appointment_date` `is not empty`

**High-budget leads:**

* Filter: Extracted data → `budget` `greater than` `5000`

## Data available

This trigger provides the richest data of any trigger type. Some fields are only present for certain agent types — use the Agent Type filter or a Condition step to branch safely.

### Contact variables

Available for every conversation regardless of channel.

| Variable                | Legacy alias    | Description            | Example            |
| ----------------------- | --------------- | ---------------------- | ------------------ |
| `{{contact.contactId}}` | `{{contactId}}` | The contact's ID       | `a1b2c3d4-...`     |
| `{{contact.firstName}}` | `{{firstName}}` | Contact's first name   | `John`             |
| `{{contact.lastName}}`  | `{{lastName}}`  | Contact's last name    | `Doe`              |
| `{{contact.phone}}`     | `{{phone}}`     | Contact's phone number | `+14155551234`     |
| `{{contact.email}}`     | `{{email}}`     | Contact's email        | `john@example.com` |

<Note>
  Dot notation (`{{contact.firstName}}`) is now the standard. The flat aliases on the right still work for backwards compatibility — existing workflows continue to render unchanged.
</Note>

### Conversation variables

The canonical namespace for all channels. Use these in new workflows.

| Variable                                   | Description                                                                                                                                              | Example                                                                          |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `{{trigger.conversation.conversationId}}`  | Unique conversation ID                                                                                                                                   | `0a9b8c7d-...`                                                                   |
| `{{trigger.conversation.channel}}`         | Channel the conversation happened on                                                                                                                     | `voice`, `sms`, `whatsapp`, `instagram`, `messenger`, `email`, `web_chat`, `web` |
| `{{trigger.conversation.agentType}}`       | Which agent type handled it                                                                                                                              | `voice`, `chat`, or `web`                                                        |
| `{{trigger.conversation.agentId}}`         | Agent that handled the conversation                                                                                                                      | `x1y2z3-...`                                                                     |
| `{{trigger.conversation.agentName}}`       | Agent's name                                                                                                                                             | `Sales Agent`                                                                    |
| `{{trigger.conversation.startedAt}}`       | When it started (ISO 8601)                                                                                                                               | `2026-04-23T14:30:00Z`                                                           |
| `{{trigger.conversation.endedAt}}`         | When it ended (ISO 8601)                                                                                                                                 | `2026-04-23T14:34:05Z`                                                           |
| `{{trigger.conversation.summary}}`         | AI-generated summary                                                                                                                                     | `Discussed pricing...`                                                           |
| `{{trigger.conversation.transcript}}`      | Full transcript (text-channel lines are timestamp-prefixed in the workspace timezone, e.g. `[2026-06-15 14:23:07] User: Hi...`; voice/web lines are not) | `[2026-06-15 14:23:07] User: Hi...`                                              |
| `{{trigger.conversation.disposition}}`     | AI-classified disposition label (when enabled on the agent)                                                                                              | `Interested - Demo Scheduled`                                                    |
| `{{trigger.conversation.evaluationScore}}` | Success evaluation result (voice and web only)                                                                                                           | `true`                                                                           |
| `{{trigger.conversation.metadata}}`        | Custom extraction fields                                                                                                                                 | `{"budget": "5000"}`                                                             |

Extraction fields configured on the agent (budget, timeline, etc.) are available under `{{trigger.conversation.metadata.fieldName}}`.

### Voice-only fields

Present when `agentType` is `voice`.

| Variable                                   | Description                      | Example               |
| ------------------------------------------ | -------------------------------- | --------------------- |
| `{{trigger.conversation.callId}}`          | Voice Engine call ID             | `c1d2e3f4-...`        |
| `{{trigger.conversation.duration}}`        | Call length in seconds           | `245`                 |
| `{{trigger.conversation.direction}}`       | Inbound or outbound              | `outbound`            |
| `{{trigger.conversation.endedReason}}`     | Raw reason the call ended        | `customer-ended-call` |
| `{{trigger.conversation.appointmentDate}}` | Booked appointment date (if any) | `2026-04-28`          |

<Note>
  The legacy `outcome` field has been removed from the trigger payload. Use `{{trigger.conversation.disposition}}` for the AI-classified outcome label, or `{{trigger.conversation.endedReason}}` for the raw call ending reason.
</Note>

### Chat-only fields

Present when `agentType` is `chat`.

| Variable                                 | Description                               | Example                |
| ---------------------------------------- | ----------------------------------------- | ---------------------- |
| `{{trigger.conversation.messageCount}}`  | Number of messages exchanged              | `8`                    |
| `{{trigger.conversation.lastMessageAt}}` | Timestamp of the final message (ISO 8601) | `2026-04-23T14:45:00Z` |

Chat conversations do not include `direction` or `duration` — use `disposition` and timestamps instead.

### Web-only fields

Present when `agentType` is `web`. Web sessions use the voice-style fields (`callId`, `duration`, `endedReason`) plus `channel: "web"`.

### Legacy `trigger.call.*` namespace

For backwards compatibility, existing voice workflows can still use the `{{trigger.call.*}}` namespace. It maps 1:1 to `{{trigger.conversation.*}}` for voice-only fields (`callId`, `duration`, `direction`, `summary`, `transcript`, `agentId`, `agentName`, `agentType`, `evaluationScore`, `startedAt`, `endedAt`, `endedReason`, `appointmentDate`, `metadata`). New workflows should use `{{trigger.conversation.*}}` since the legacy namespace only resolves for voice conversations.

## Deduplication

Uses the conversation ID as the dedup key. Default window: 1 minute. Each conversation can only trigger the workflow once within the window.

## Use case examples

### Follow-up based on voice disposition

Different AI-classified dispositions get different follow-up strategies.

1. **Trigger:** Conversation ended → Agent Type: voice
2. **Action:** [Condition](/workflows/actions/condition) — Check `{{trigger.conversation.disposition}}`
   * **Path: contains `Interested`**
     * [Send email](/workflows/actions/send-email) — *"Hi `{{firstName}}`, great chatting! Here are the details we discussed..."*
     * [Update contact](/workflows/actions/update-contact) — Add "interested" tag
   * **Path: contains `Voicemail`**
     * [Wait](/workflows/actions/wait) — 4 hours
     * [Voice call](/workflows/actions/voice-call) — Retry
   * **Path: contains `No Answer`**
     * [Send SMS](/workflows/actions/send-sms) — *"Hi `{{firstName}}`, I just tried to reach you. Let me know a good time."*
   * **Default**
     * [Update contact](/workflows/actions/update-contact) — Add "needs-review" tag

### Post SMS summary to Slack

Every finalized SMS conversation gets a summary posted to your team channel.

1. **Trigger:** Conversation ended → Agent Type: chat
2. **Action:** [Condition](/workflows/actions/condition) — `{{trigger.conversation.channel}}` equals `sms`
3. **Action:** [Slack message](/workflows/actions/slack-message) — #sms-conversations
   * Message: *"SMS with `{{firstName}}` `{{lastName}}` (`{{trigger.conversation.messageCount}}` messages, `{{trigger.conversation.disposition}}`)\n\n`{{trigger.conversation.summary}}`"*

### Post-call summary to Slack

Every completed voice call gets a summary posted to your team channel.

1. **Trigger:** Conversation ended → Agent Type: voice, Call disposition: `contains` `completed`
2. **Action:** [Slack message](/workflows/actions/slack-message) — #call-updates
   * Message: *"Call with `{{firstName}}` `{{lastName}}` (`{{trigger.conversation.duration}}`s, `{{trigger.conversation.disposition}}`)\n\n`{{trigger.conversation.summary}}`"*

### Sync conversation data to CRM

Push conversation details to your CRM after every conversation, regardless of channel.

1. **Trigger:** Conversation ended (no filters)
2. **Action:** [Send webhook](/workflows/actions/send-webhook) — POST to CRM
   ```json theme={null}
   {
     "contact": "{{firstName}} {{lastName}}",
     "phone": "{{phone}}",
     "channel": "{{trigger.conversation.channel}}",
     "agentType": "{{trigger.conversation.agentType}}",
     "disposition": "{{trigger.conversation.disposition}}",
     "summary": "{{trigger.conversation.summary}}",
     "agent": "{{trigger.conversation.agentName}}"
   }
   ```

### DNC compliance

Automatically handle "do not call" requests on voice calls.

1. **Trigger:** Conversation ended → Agent Type: voice, Call disposition: `contains` `do_not_call`
2. **Action:** [Update contact](/workflows/actions/update-contact) — Add "dnc" tag
3. **Action:** [Slack message](/workflows/actions/slack-message) — #compliance
   * Message: *"`{{firstName}}` `{{lastName}}` (`{{phone}}`) requested Do Not Call."*

### Tag engaged web visitors

When a web agent session finishes with a positive disposition, mark the contact as engaged.

1. **Trigger:** Conversation ended → Agent Type: web
2. **Action:** [Condition](/workflows/actions/condition) — `{{trigger.conversation.disposition}}` contains `Interested`
3. **Action:** [Update contact](/workflows/actions/update-contact) — Add "engaged-web" tag
