Import a call from another platform

Create an AskElephant engagement from a recording and an existing transcript
View as Markdown

Need AskElephant to handle your import? Contact support@askelephant.ai to get started. AskElephant-managed imports are approximately $0.99 per hour of recorded calls.

Overview

You can import a call from any platform that can make an HTTPS request to the AskElephant public API. Use POST /v2/engagements to create the engagement and include the call metadata, recording URL, and transcript timeline.

The basic flow is:

  1. Collect the call’s metadata, recording URL, and transcript from the source platform.
  2. Convert the transcript into AskElephant’s structured transcript_timeline format.
  3. Submit the call with POST /v2/engagements.
  4. Use the returned engagement ID to retrieve the engagement and its AI-generated insights.

Billing warning: Please include a transcript_timeline with every imported call. Without one, AskElephant will process the call through its transcription process, which may result in a transcription charge on your next invoice at a rate of $0.50 per hour of transcription handled by AskElephant.

Choose your import mode

You may provide both media_url and transcript_timeline. The recording is retained for playback, while the supplied timeline is used as the call transcript. If you do not have recording media, you can submit a transcript-only import with transcript_timeline.

transcript_text is deprecated and should not be used for new imports. It does not replace transcript_timeline for a structured import.

Prerequisites

  • An AskElephant API key with the engagements:write scope. Add engagements:read if you want to retrieve the imported engagement and its insights; private engagements also require engagements:private:read.
  • A recording URL that AskElephant can access over HTTPS, if importing media
  • A transcript with speaker attribution and timestamps, if you want to avoid transcription charges

See Authentication and scopes for API key details.

Decide who owns the engagement

The engagement owner is the AskElephant user responsible for the imported call. Ownership affects attribution in AskElephant and determines the user used as the engagement’s owner and host during downstream processing. It is separate from the list of attendees.

Mark the owner in the participants array:

1{
2 "name": "Alex Smith",
3 "email": "alex@example.com",
4 "is_owner": true
5}

AskElephant resolves an owner participant in this order:

  1. Match the participant’s email to an active AskElephant user in the authenticated workspace.
  2. If there is no email match, match the participant’s phone number.
  3. Use the matched user as both the engagement owner and host.

The matched user must be active and invited to the workspace. Users with INACTIVE or NOT_INVITED roles cannot be selected as the owner.

If no participant is marked as the owner

When no participant has is_owner: true, ownership defaults to the user associated with the API key or OAuth credential. This is usually the right choice when the credential belongs to the person or service account responsible for the import.

An API key or OAuth credential without an associated user can still create an engagement, but only if one participant is marked as is_owner: true and resolves to an active workspace user.

Owner validation rules

  • At most one participant can have is_owner: true.
  • The owner participant must resolve by email or phone to an active workspace user.
  • A name alone is not enough to resolve ownership; include the user’s email or E.164 phone number.
  • If there are multiple owners, or the owner cannot be resolved, the API returns 400.
  • Owner validation happens before media ingestion, engagement persistence, or processing events, so a failed owner claim does not create a partial import.

Attendees and contacts

Use participants to send the people who attended the call. Each participant can include a name, email, and/or E.164 phone number. Include the same identifying details in the transcript timeline’s speakers when you want AskElephant to match transcript speakers to participants.

If you already have AskElephant contact IDs and want to link those existing contacts after creation, replace the engagement’s managed contacts with:

$curl --request PUT \
> --url 'https://app.askelephant.ai/api/v2/engagements/ngmt_01JWAS.../contacts' \
> --header 'Authorization: sk-apik_<id>.<secret>' \
> --header 'Content-Type: application/json' \
> --data '{
> "contact_ids": ["cnt_01CUSTOMER", "cnt_02CUSTOMER"]
> }'

This endpoint also requires engagements:write. Private engagements additionally require engagements:private:write. Use contacts:read only when you need to read expanded contact details in a response.

Create the import

The following request imports a call with its recording and existing transcript:

$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": "Discovery call with Jane Doe",
> "source_system": "my-crm",
> "external_id": "my-crm:call_12345",
> "media_url": "https://recordings.example.com/call_12345.mp3",
> "external_url": "https://my-crm.example.com/calls/12345",
> "start_at": "2026-06-12T17:00:00Z",
> "run_workflows": false,
> "participants": [
> { "name": "Alex Smith", "email": "alex@example.com", "is_owner": true },
> { "name": "Jane Doe", "email": "jane@example.com" }
> ],
> "transcript_timeline": {
> "speakers": [
> {
> "index": 0,
> "name": "Alex Smith",
> "email": "alex@example.com"
> },
> {
> "index": 1,
> "name": "Jane Doe",
> "email": "jane@example.com"
> }
> ],
> "entries": [
> {
> "speaker_index": 0,
> "start_seconds": 0,
> "end_seconds": 4.5,
> "sentences": [
> {
> "text": "Thanks for taking the time to meet today.",
> "start_seconds": 0,
> "end_seconds": 4.5
> }
> ]
> },
> {
> "speaker_index": 1,
> "start_seconds": 4.5,
> "end_seconds": 8,
> "sentences": [
> {
> "text": "Happy to be here.",
> "start_seconds": 4.5,
> "end_seconds": 8
> }
> ]
> }
> ]
> }
> }'

Required fields

FieldDescription
typeCALL or MEETING
titleDisplay title for the engagement
source_systemName of the platform that supplied the call
external_idStable ID for the call in the source platform
run_workflowstrue to run normal automations; use false for historical imports when automations should not fire
media_url or transcript_timelineAt least one is required. Include both when you want recording playback without having AskElephant transcribe the recording

Workflow and processing controls

run_workflows controls downstream automations, not core engagement processing:

ValueResult
trueNormal workflow triggers and recap emails can run. These automations may create additional charges.
falseWorkflow triggers and recap emails are suppressed. This is usually the safer choice for historical imports.

Transcription, summarization, and tagging continue to run regardless of run_workflows. Suppressing workflows does not suppress core AI processing or remove a transcription charge when no transcript timeline is supplied.

Prevent workflows and attendee emails

For historical imports, set run_workflows to false before creating the engagement:

1{
2 "run_workflows": false
3}

This prevents workflow triggers and automated meeting summary or recap emails from being sent to people marked as attendees in participants. In other words, run_workflows: false is the API setting to use when you want run_automations turned off for an imported call.

The engagement can still be transcribed, summarized, and tagged. Setting run_workflows to false only suppresses downstream workflow automation and its related emails.

Recording URL requirements

When media_url is included, it must be a publicly accessible HTTPS URL that serves an audio or video content type. AskElephant fetches the media during request processing, so the URL must be available at the time of the request. Private URLs, internal IP addresses, HTTP URLs, and non-media content types are rejected.

Transcript timeline format

The timeline has two parts:

  • speakers declares each speaker. Each speaker needs a unique numeric index and a non-empty name; email and phone are optional but help match the speaker to an AskElephant participant.
  • entries contains the transcript turns. Each entry references a speaker with speaker_index, and includes offsets in seconds plus one or more sentences.

All timestamps are offsets from the start of the engagement, not absolute timestamps. Every speaker_index must match a declared speaker, and sentence timestamps must fall within their parent entry.

Idempotency and retries

external_id is the idempotency key within the workspace. Repeating a request with the same external_id returns the existing engagement instead of creating a duplicate, so it is safe to retry a timed-out request. If IDs can overlap across source systems, prefix them, for example my-crm:call_12345.

The idempotency key is scoped to the workspace, not to source_system. The same external_id can therefore collide across two platforms unless you include the source in the ID. A new import returns 201; an idempotent match returns 200 when the engagement can be returned, or 204 when the credential can write but not read it. If the matching engagement is inaccessible, the API returns 404. If the matching engagement was deleted, the ID can be reused for a new import.

Timestamps and engagement duration

Set start_at to the original call start time as an ISO 8601 timestamp. If you omit it, AskElephant uses the current server time.

When transcript_timeline is supplied, the engagement end time is derived from the largest end_seconds value in the timeline, even when media_url is also present. For media-only imports, the end time is derived from the recording duration during processing. Timeline offsets are relative to the engagement start, not absolute timestamps.

Response and next steps

On a new import, the API returns 201 and an engagement object containing its AskElephant ID. Save the id from the response:

1{
2 "object": "engagement",
3 "id": "ngmt_01JWAS...",
4 "title": "Discovery call with Jane Doe",
5 "engagement_type": "CALL",
6 "processing_status": "PENDING"
7}

Use the ID to retrieve the engagement after processing:

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

For reliable integrations, subscribe to v1.engagement.transcript_timeline.completed to know when the transcript is available, then poll the engagement until processing_status is COMPLETED before retrieving AI-generated insights. The transcript webhook can arrive while analysis and enrichment are still running. See Webhooks and the Engagements API reference.

Import checklist

  1. Create or use an API key with engagements:write and engagements:read; add engagements:private:read when retrieving private engagements.
  2. Choose the import mode: media only, media plus transcript timeline, or transcript only.
  3. Use a stable, source-prefixed external_id.
  4. Set start_at to the original call time when available.
  5. Add every attendee to participants.
  6. Mark exactly one attendee as is_owner: true when ownership should differ from the API-key user, or when the credential has no associated user.
  7. Include transcript_timeline whenever possible to avoid transcription charges.
  8. Save the returned engagement ID and wait for processing to complete before retrieving insights.

Common errors

StatusMeaning
400Owner participant claims are ambiguous or invalid. This includes multiple owners or an owner who is not an active AskElephant user
401API key is missing or invalid
403The credential does not have a required scope for the route or requested expansion
404The idempotency key matches an engagement the credential cannot access. Private contact updates without engagements:private:write may also return 404
422Request validation failed, or media_url is not a publicly accessible HTTPS audio/video URL
429Rate limit exceeded; retry after the indicated delay