> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.askelephant.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.askelephant.ai/_mcp/server.

# Dialer integration

## Overview

The AskElephant public API lets any dialer or call recording system push calls for processing and retrieve AI-generated insights (action items, signals, transcripts) when they are ready. The integration flow is:

1. **Ingest** — Your dialer submits a call recording via `POST /v2/engagements`
2. **Process** — AskElephant transcribes the recording and runs AI analysis
3. **Notify** — A webhook fires when the transcript is ready
4. **Retrieve** — Your system fetches insights via `GET /v2/engagements/{id}?expand=action_items,signals`

The `external_url` field on each engagement lets you deep-link from AskElephant back to the call in your dialer's UI, and from your dialer into the AskElephant engagement.

## Prerequisites

* An AskElephant workspace with API access enabled
* An API key (`sk-apik_<id>.<secret>`) — see [Authentication](/authentication)
* A webhook endpoint configured in **Settings > Webhooks** — see [Webhooks](/webhooks)
* Publicly accessible HTTPS URLs for your call recordings

## Step 1 — Submit a call recording

When a call ends and a recording is available, submit it to AskElephant:

```bash
curl --request POST \
  --url 'https://app.askelephant.ai/api/v2/engagements' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "CALL",
    "title": "Sales call with Jane Doe",
    "source_system": "my-dialer",
    "external_id": "call-20260513-001",
    "media_url": "https://recordings.example.com/call-20260513-001.mp3",
    "external_url": "https://my-dialer.example.com/calls/call-20260513-001",
    "run_workflows": true,
    "participants": [
      { "name": "Alice Smith", "email": "alice@example.com", "is_owner": true },
      { "name": "Jane Doe", "email": "jane@customer.com", "phone": "+14155550100" }
    ]
  }'
```

### Request fields

| Field           | Required | Description                                                                                                                                                                             |
| --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`          | Yes      | `CALL` for phone calls, `MEETING` for video meetings                                                                                                                                    |
| `title`         | Yes      | Display title for the engagement                                                                                                                                                        |
| `source_system` | Yes      | Identifier for your dialer (e.g. `wavv`, `my-dialer`). Stored as metadata for traceability                                                                                              |
| `external_id`   | Yes      | Unique ID for this call in your system. Used as the workspace-scoped idempotency key — submitting the same `external_id` again returns the existing engagement                          |
| `media_url`     | Yes      | Publicly accessible HTTPS URL to the audio or video recording                                                                                                                           |
| `external_url`  | No       | URL linking back to this call in your dialer's UI. Accepts `https://`, `http://`, and custom deep-link schemes (e.g. `wavv://call/123`). Returned in API responses and webhook payloads |
| `run_workflows` | Yes      | Set `true` to enable automation (recap emails, workflow triggers). Set `false` for historical imports                                                                                   |
| `participants`  | No       | List of call participants with name, email, and/or phone. Mark the agent as `is_owner: true`                                                                                            |
| `start_at`      | No       | ISO 8601 timestamp for when the call started. Defaults to the current time                                                                                                              |
| `is_private`    | No       | Mark the engagement as private (restricted visibility)                                                                                                                                  |

### Response

```json
{
  "object": "engagement",
  "id": "ngmt_01JWAS...",
  "title": "Sales call with Jane Doe",
  "engagement_type": "CALL",
  "data_source": "PUBLIC_API",
  "processing_status": "PENDING",
  "is_internal": false,
  "is_private": false,
  "external_url": "https://my-dialer.example.com/calls/call-20260513-001",
  "engagement_at": "2026-05-13T14:00:00.000Z",
  "start_at": "2026-05-13T14:00:00.000Z",
  "owner_user_id": "usr_01JWAS...",
  "created_at": "2026-05-13T14:00:00.000Z",
  "updated_at": "2026-05-13T14:00:00.000Z"
}
```

Save the `id` — you will need it to fetch insights later.

### Idempotency

The `external_id` field serves as the workspace-scoped idempotency key. If you submit the same `external_id` again (even with different field values), the API returns the existing engagement unchanged. This means you can safely retry failed submissions without creating duplicates.

## Step 2 — Subscribe to the transcript webhook

In **Settings > Webhooks**, subscribe to the `v1.engagement.transcript_timeline.completed` event type. This event fires when the engagement transcript has been processed and is ready for retrieval.

Example webhook payload:

```json
{
  "id": "evt_01JWAS...",
  "type": "v1.engagement.transcript_timeline.completed",
  "api_version": "v1",
  "created_at": "2026-05-13T14:15:00.000Z",
  "workspace_id": "wrks_01JWAS...",
  "data": {
    "object": {
      "object": "engagement",
      "id": "ngmt_01JWAS...",
      "title": "Sales call with Jane Doe",
      "engagement_type": "CALL",
      "data_source": "PUBLIC_API",
      "processing_status": "COMPLETED",
      "external_url": "https://my-dialer.example.com/calls/call-20260513-001",
      "engagement_at": "2026-05-13T14:00:00.000Z",
      "start_at": "2026-05-13T14:00:00.000Z",
      "end_at": "2026-05-13T14:30:00.000Z",
      "duration_seconds": 1800,
      "created_at": "2026-05-13T14:00:00.000Z",
      "updated_at": "2026-05-13T14:15:00.000Z"
    }
  }
}
```

The `data.object` is the full engagement, including the `external_url` you provided at creation time.

### Webhook delivery

Webhooks are delivered by [Svix](https://docs.svix.com) with automatic retries and exponential backoff. Your endpoint should:

* Return a `2xx` status within 15 seconds
* Be idempotent — the same event may be delivered more than once
* Verify the Svix signature before processing — see [Verifying webhook signatures](/webhooks#verifying-webhook-signatures)

## Step 3 — Retrieve insights

When you receive the `v1.engagement.transcript_timeline.completed` webhook, fetch the engagement with expanded insights:

```bash
curl --request GET \
  --url 'https://app.askelephant.ai/api/v2/engagements/ngmt_01JWAS...?expand=action_items,signals' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

### Response with insights

```json
{
  "object": "engagement",
  "id": "ngmt_01JWAS...",
  "title": "Sales call with Jane Doe",
  "engagement_type": "CALL",
  "processing_status": "COMPLETED",
  "external_url": "https://my-dialer.example.com/calls/call-20260513-001",
  "action_items": [
    {
      "object": "engagement_action_item",
      "id": "ai_01JWAS...",
      "title": "Send pricing proposal",
      "description": "Alice agreed to send the updated pricing proposal to Jane by Friday.",
      "due_on": "2026-05-16",
      "assigned_to_user_name": "Alice Smith",
      "assigned_to_user_email": "alice@example.com",
      "created_at": "2026-05-13T14:15:00.000Z",
      "updated_at": "2026-05-13T14:15:00.000Z"
    }
  ],
  "signals": [
    {
      "object": "engagement_signal",
      "id": "sig_01JWAS...",
      "annotation_definition_id": "adef_01JWAS...",
      "name": "Budget Discussion",
      "data_type": "BOOLEAN",
      "value": true
    }
  ],
  "created_at": "2026-05-13T14:00:00.000Z",
  "updated_at": "2026-05-13T14:15:00.000Z"
}
```

### Available expand fields

| Expand value   | Description                                            |
| -------------- | ------------------------------------------------------ |
| `action_items` | AI-generated action items with assignees and due dates |
| `signals`      | Extracted signals and annotations                      |
| `transcript`   | Full text transcript                                   |
| `contacts`     | Contact participants linked to the engagement          |
| `companies`    | Companies associated via participants                  |
| `owner`        | The workspace user who owns the engagement             |
| `media_url`    | Temporary signed download URL for the recording        |
| `tags`         | Tags applied to the engagement                         |

Combine multiple expand fields with commas: `?expand=action_items,signals,transcript`

### Timing considerations

The `v1.engagement.transcript_timeline.completed` event fires when the transcript is ready. AI-derived insights (action items, signals) are generated through independent processing pipelines and may still be in progress when the transcript event fires.

**Recommended approach:** When you receive the webhook, wait 30-60 seconds before fetching insights with `?expand=action_items,signals`. If the `action_items` or `signals` arrays are empty, retry after a short delay. Most insights are available within 2-3 minutes of transcript completion.

## WAVV event mapping

If you are integrating WAVV (or a similar dialer), here is how WAVV call lifecycle events map to AskElephant API calls:

| WAVV event     | AskElephant action     | Notes                                                                                                 |
| -------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- |
| `CallStarted`  | No action needed       | Optionally store the call metadata locally for the later submission                                   |
| `CallEnded`    | No action needed       | Wait for the recording to be available before submitting                                              |
| `CallRecorded` | `POST /v2/engagements` | Submit with `type: "CALL"`, the recording URL as `media_url`, and the WAVV call URL as `external_url` |

### WAVV-specific guidance

* Use `"source_system": "wavv"` to identify calls from WAVV in AskElephant
* Set `external_id` to the WAVV call ID for idempotency (e.g. `"wavv-call-12345"`)
* Set `external_url` to the WAVV call detail URL or use a deep-link scheme (e.g. `wavv://call/12345`) if the WAVV app supports deep linking
* Map WAVV agent info to the participant with `is_owner: true`
* Map the WAVV lead/contact to additional participants with their name, email, and/or phone

## Deep linking with `external_url`

The `external_url` field creates a bidirectional link between AskElephant and your dialer:

* **Dialer to AskElephant:** Your dialer can link to the AskElephant engagement using the engagement `id` returned in the creation response
* **AskElephant to dialer:** The `external_url` is stored with the engagement and included in all engagement API responses and webhook payloads, letting downstream systems link back to the call in your dialer

The field accepts:

* Standard URLs: `https://my-dialer.example.com/calls/123`
* Custom app deep-link schemes: `wavv://call/123`, `tel:+14155550100`

Dangerous schemes (`javascript:`, `data:`, `vbscript:`, `blob:`, `file:`) are rejected.

## Error handling

| HTTP status | Meaning                                                             | Action                                                      |
| ----------- | ------------------------------------------------------------------- | ----------------------------------------------------------- |
| `200`       | Idempotent match — engagement already exists for this `external_id` | No action needed; the existing engagement is returned       |
| `201`       | Engagement created successfully                                     | Store the engagement `id` for later retrieval               |
| `400`       | Invalid request body                                                | Check required fields and field formats                     |
| `401`       | Invalid or missing API key                                          | Verify your `Authorization` header                          |
| `422`       | Validation error (e.g. blocked URL scheme, invalid `media_url`)     | Check the error response for details                        |
| `429`       | Rate limited                                                        | Retry after the delay indicated in the `Retry-After` header |

## Complete integration checklist

1. Obtain an API key from **Settings > API Keys**
2. Configure a webhook endpoint in **Settings > Webhooks** and subscribe to `v1.engagement.transcript_timeline.completed`
3. Implement Svix signature verification on your webhook handler
4. When a call recording is ready, `POST /v2/engagements` with the recording URL, call metadata, and `external_url`
5. When you receive the transcript webhook, fetch `GET /v2/engagements/{id}?expand=action_items,signals` to retrieve insights
6. Display insights in your dialer UI alongside the call details