REST API

Call Ingest API

Send calls from your dialer, meeting tool or call export into Zime. Post each call with its transcript, a link to its recording, or both. Zime adds it to the rep's calls, builds its summary and insights, and maps it to a CRM deal.

Base URL
https://nodejs-overhaul-prod.zime.ai

Before you start

  • An ingest key. A company admin generates it in Zime. It is separate from your REST API key: it can send calls and check their status, and it cannot read calls, transcripts or insights.
  • A source. Zime registers a source name for each system that sends calls, such as your dialer, sets its call type (dialer or meeting) and its rules with you, and turns it on. Until then the API answers 422 unknown_source.
  • Your reps in Zime. Every rep on your calls must be an active Zime user in your company. Zime matches them by email.

Get your ingest key

1
Open Admin, then APIs

In Zime, click Admin in the sidebar and open the APIs tab. Only company admins see it.

2
Generate the key

In the Ingest key (write) card, press Generate key. Copy the key and keep it in your secret store. You can press Show key or Copy again whenever you need it.

3
Send it on every request

Put the key in the X-API-Key header. A key in the URL or in an Authorization header is not read.

cURL
curl -G 'https://nodejs-overhaul-prod.zime.ai/api/v1/calls/ingest' \
  -H 'X-API-Key: YOUR_INGEST_KEY' \
  --data-urlencode 'status=failed'

A company has one ingest key at a time. Generate new key stops the old key the moment the new one is created, and Revoke stops it with no replacement. Calls Zime has already accepted keep processing. Keep the key server side, never in browser code.

POST/api/v1/calls/ingest

Sends one call. Zime checks it, applies your source's rules and answers at once; it processes the call in the background. Send the body as JSON with Content-Type: application/json, up to 1 MB.

Body fields

FieldTypeRequiredDescription
sourcestringYesYour source name, registered by Zime. 2 to 40 lowercase letters, digits or underscores.
external_call_idstringYesYour system's ID for the call: 1 to 128 letters, digits, dots, underscores, colons or hyphens. Not case sensitive. With source, it identifies the call.
started_atstringYesISO 8601 with a zone, such as 2026-10-19T09:43:35Z. Up to 400 days in the past and 1 day in the future.
ended_atstringYesISO 8601 with a zone. Not before started_at, and at most 24 hours after it.
repobjectYesemail of an active Zime user in your company, plus optional name and phone.
participantsarrayYes1 to 100 people, each with a role (prospect, rep or other) and optional name, email and phone. At least one who is not the rep needs an email or a phone.
transcriptobjectThis or recordingformat set to segments, and segments: 1 to 5,000, ordered by start_seconds. See the segment fields below.
recordingobjectThis or transcriptaudio_url: an https link to the audio or video file that Zime can download with no extra header.
call_typestringNodialer or meeting. Defaults to your source's type. The other of the two returns 422 call_type_mismatch; any other value returns 400 invalid_payload.
directionstringNooutbound (default) or inbound.
titlestringNoUp to 255 characters. Without it Zime names the call, such as [OUTBOUND] [CALL] with Pat Prospect.
dispositionstringNoThe call outcome from your dialer, up to 120 characters.
metadataobjectNoUp to 50 keys with string values of up to 500 characters.

Segment fields

FieldTypeRequiredDescription
speaker_rolestringYesrep, prospect or other.
speaker_namestringYesThe speaker's name. Send "" when you do not know it.
start_secondsnumberYesSeconds from the start of the call, 0 or more.
end_secondsnumberNoNot before start_seconds. Left out, Zime estimates it from the length of the text.
textstringYesUp to 10,000 characters.

Fields not listed here are ignored. Line breaks in text become spaces.

Example request

call.json
{
  "source": "your_source",
  "external_call_id": "call-0001",
  "started_at": "2026-10-19T09:43:35Z",
  "ended_at": "2026-10-19T09:48:35Z",
  "disposition": "Meeting Set",
  "rep": { "email": "rep@yourcompany.com", "name": "Riley Rep" },
  "participants": [
    { "role": "prospect", "name": "Pat Prospect", "email": "pat@example.com", "phone": "+14155550123" }
  ],
  "transcript": {
    "format": "segments",
    "segments": [
      { "speaker_role": "rep", "speaker_name": "Riley Rep", "start_seconds": 0, "text": "Hi Pat, thanks for taking the call." },
      { "speaker_role": "prospect", "speaker_name": "Pat Prospect", "start_seconds": 4, "end_seconds": 9, "text": "Sure, I have a few minutes." }
    ]
  }
}
cURL
curl -X POST 'https://nodejs-overhaul-prod.zime.ai/api/v1/calls/ingest' \
  -H 'X-API-Key: YOUR_INGEST_KEY' \
  -H 'Content-Type: application/json' \
  --data @call.json

For a recording instead of a transcript, replace transcript with a recording link:

Recording
"recording": { "audio_url": "https://storage.example.com/calls/call-0001.mp3?signature=..." }

Responses

StatusMeaning
202Accepted and queued: status is queued. Also the answer when a failed call is sent again, or a call a rule skipped is now kept.
200No new work: status skipped with a reason when a rule drops the call, duplicate: true when Zime already has it, or updated: true when it replaced a queued copy.
4xx, 5xxRefused. See Errors.
202 Accepted
{
  "ingest_id": 1042,
  "external_call_id": "call-0001",
  "status": "queued",
  "received_at": "2026-10-19T09:50:02.000Z"
}
200 Skipped by a rule
{
  "ingest_id": 1043,
  "external_call_id": "call-0002",
  "status": "skipped",
  "reason": "rule:min_duration_seconds"
}
200 Duplicate
{
  "ingest_id": 1042,
  "external_call_id": "call-0001",
  "status": "completed",
  "duplicate": true
}

GET/api/v1/calls/ingest/{source}/{external_call_id}

Returns the status of one call you sent. A call Zime has no record of returns 404 not_found.

Example request

cURL
curl 'https://nodejs-overhaul-prod.zime.ai/api/v1/calls/ingest/your_source/call-0001' \
  -H 'X-API-Key: YOUR_INGEST_KEY'

Example response

200 OK
{
  "ingest_id": 1042,
  "source": "your_source",
  "external_call_id": "call-0001",
  "call_type": "dialer",
  "status": "completed",
  "zime_call_id": 212322,
  "started_at": "2026-10-19T09:43:35.000Z",
  "received_at": "2026-10-19T09:50:02.000Z",
  "processed_at": "2026-10-19T09:50:11.000Z"
}

Response fields

FieldDescription
statusqueued, processing, completed, skipped or failed.
reasonWhy a call was skipped, or what it is waiting for, such as Waiting for speech to text.
errorWhy a call failed. Present on failed calls only.
zime_call_idThe call's ID in Zime, the same as call_id in the REST API. Present once the call is in Zime.
recording_statusstored or failed, when you sent a recording.
received_at, processed_atWhen Zime accepted the call, and when it finished with it.

GET/api/v1/calls/ingest

Lists the calls you sent, oldest call first, with the same fields as a single call. Use it to follow a backfill or to find calls that failed.

Query parameters

ParameterTypeRequiredDescription
sourcestringNoOnly calls from this source.
statusstringNoOnly calls in this status: queued, processing, completed, skipped or failed.
start-datestringNoCalls that started on or after it. YYYY-MM-DD in UTC, or ISO 8601 with a zone.
end-datestringNoCalls that started on or before it. A YYYY-MM-DD date includes the whole day in UTC.
page-sizeintegerNoRecords per page, 1 to 1000. Default 100.
after-keystringNoPagination cursor: the next_cursor of the previous page.

Example request

cURL
curl -G 'https://nodejs-overhaul-prod.zime.ai/api/v1/calls/ingest' \
  -H 'X-API-Key: YOUR_INGEST_KEY' \
  --data-urlencode 'source=your_source' \
  --data-urlencode 'status=failed' \
  --data-urlencode 'start-date=2026-10-01' \
  --data-urlencode 'end-date=2026-10-31'

Example response

200 OK
{
  "data": [
    {
      "ingest_id": 1044,
      "source": "your_source",
      "external_call_id": "call-0003",
      "call_type": "dialer",
      "status": "failed",
      "error": "Recording fetch got status 404",
      "zime_call_id": 212327,
      "recording_status": "failed",
      "started_at": "2026-10-20T14:02:00.000Z",
      "received_at": "2026-10-20T14:10:41.000Z",
      "processed_at": "2026-10-20T14:11:03.000Z"
    }
  ],
  "pagination": {
    "page_size": 100,
    "returned_count": 1,
    "next_cursor": null,
    "has_more": false
  }
}

Errors

Errors return an error code. A 400 also lists each field that failed and why:

Error response
{
  "error": "invalid_payload",
  "errors": [
    { "field": "started_at", "message": "started_at must be ISO 8601 with a zone, for example 2026-10-19T09:43:35Z" }
  ]
}
StatusErrorWhat to do
400invalid_payloadFix the fields named in errors. Also returned for a body that is not valid JSON or not sent as application/json.
400transcript_or_recording_requiredSend a transcript, a recording link, or both.
401api_key_requiredSend the key in the X-API-Key header.
403invalid_api_keyThe key is unknown, revoked or replaced, or it is a REST API key. Use the current ingest key.
403company_inactiveYour company is not active in Zime. Email support@zime.ai.
404not_foundNo call with that source and external_call_id.
413payload_too_largeKeep the body under 1 MB.
422unknown_sourceThe source is not registered for your company, or not turned on yet. Check the name, or ask Zime to turn it on.
422call_type_mismatchLeave call_type out, or send your source's type.
422rep_not_foundNo Zime user in your company has rep.email. Add the rep in Zime, or fix the email, then resend.
422rep_inactiveThe rep's Zime user is not active, for example deactivated or pending approval. Activate or approve it, then resend.
429rate_limitedWait the number of seconds in the Retry-After header, then resend.
500server_errorRetry with growing waits.
503not_readyRetry later.

A call refused with a 4xx error is not stored, so you can fix it and send it again as a new call.

Limits

LimitValue
RequestsAbout 600 a minute for each key on each Zime server instance; plan for 600 a minute per key. Above that, 429 with Retry-After
Request body1 MB (1,048,576 bytes) of JSON
Participants1 to 100
Transcript1 to 5,000 segments, each up to 10,000 characters
RecordingUp to 1.5 GB; WAV, MP3, M4A, OGG, FLAC, WebM, MP4 or MOV; up to 5 redirects; download within 30 minutes
Call timesStarted up to 400 days ago and up to 1 day ahead; up to 24 hours long
Title, disposition255 and 120 characters
Metadata50 keys; string values up to 500 characters
ProcessingQueued calls are picked up every 30 seconds, up to 5 at a time
Status listUp to 1000 records a page; default 100

What happens after you send a call

  1. Checked. Zime validates the body, the source and the rep, then applies your source's rules. A call the rules drop is answered skipped and its content is not stored.
  2. Queued. Every other new call is answered 202 with status: queued.
  3. Processed. In the background Zime creates the call, stores your transcript or downloads and transcribes your recording, and moves the status to completed. The worker picks up queued calls every 30 seconds, up to 5 at a time. Measured in production, a call with a transcript reached completed 9 to 24 seconds after it was sent, and a recording-only call 1 to 3 minutes; these are measured times, not a guarantee. A call that cannot be added ends as failed with an error; resend it once the cause is fixed.
  4. Analysed. Zime matches the call to your checklists, builds the summary and insights (within 3 minutes in our tests), and maps the call to a CRM deal about 10 minutes after it is created. The call shows on the rep's Calls page; tick Dialer in the filters to see dialer calls.

A resend never creates a second copy. The same source and external_call_id is the same call: Zime answers duplicate: true, retries it if it failed, and replaces it only while it is still queued.

Nooks exports in Amazon S3

If you use Nooks and export transcripts to Amazon S3, Zime has a Python sender script that reads those exports and sends each call to this API. It runs in your own AWS account, so your bucket access stays with you. Zime shares the sender script and the setup steps over a secure channel. Your company admin generates the ingest key in Zime under Admin, then APIs; keep it in your secret store and never send it by email or chat in plain text.

Settings

VariableRequiredMeaning
ZIME_INGEST_KEYYesYour ingest key, from your secret store, given as an environment variable. Never type it into a command.
NOOKS_BUCKETScheduled runThe export bucket.
WATERMARK_URIScheduled run on LambdaWhere the run keeps its place: a file path, or s3://bucket/key.
START_DATENoOldest call to send, YYYY-MM-DD. Default 2026-08-06.
SETTLE_MINUTESNoIgnore exports younger than this. Default 15.
MAX_RUN_SECONDSNoStop a run after this long and continue on the next one.

Run it

  • AWS permissions, for the Lambda role or the credentials the shell uses: s3:ListBucket on the export bucket and s3:GetObject on its objects, always. With the watermark in the export bucket, add s3:PutObject on the watermark key. With the watermark in a separate bucket, add s3:ListBucket on that bucket, and s3:GetObject and s3:PutObject on the watermark key. s3:ListBucket on the watermark's bucket is required: without it every scheduled run fails before sending anything. A watermark in a local file needs no S3 permission.
  • On AWS Lambda: handler nooks_ingest.lambda_handler, timeout 900 seconds, memory 512 MB. Always run it on a schedule, every 15 minutes or once a day after 09:30 UTC: the scheduled run keeps a watermark and stops on a refused key or lasting errors without moving it. An s3:ObjectCreated:* trigger can run beside it for faster delivery, but not alone, because a call an event cannot send is dropped after Lambda's retries.
  • Test first: python3 nooks_ingest.py --dry-run sends nothing and reports what it would send. Then send one call with --key and its object key.

What a run reports

OutcomeMeaning
queuedAccepted; Zime processes it in the background.
skippedValid, but your source's rules drop it.
duplicateZime already has this call.
updatedZime replaced a copy it had not processed yet.
rejected:<error>Refused for this call, such as rejected:rep_not_found. Logged with the call ID; the run continues and its place moves past the call, so resend it after fixing the cause.
bad_exportThe zip could not be read; logged with its key.
stopped_for_timeThe run used its time; the next run continues.

A refused key (401, 403), an unknown source, or 429, 5xx, network or S3 read errors that outlast the retries stop the run. A scheduled run saves its watermark on the last call done, so the next run starts with the call that failed; on Lambda the invocation ends in an error, and from the command line the script exits with status 1. An S3 event that fails is retried by Lambda, twice by default, and then dropped; the scheduled run sends that call. A call Zime refuses for itself, such as rep_not_found, does not stop the run: it is logged, and the watermark moves past it. Zime stores nothing for a refused call, so after you fix the cause, resend it with --key and its object key, or resend its window: load the key, then run the scheduled mode once with START_DATE set to the day before the problem began and a watermark file that does not exist yet.

The script reads the key only from the environment. Never type the key into a command, because it would then be saved in the shell history. Load it in one of these ways; the commands below then run without the key.

  1. Export it from your secret store.
  2. In bash (run it in bash, e.g. start bash first), paste the key at the hidden prompt and press Enter:
bash
read -rs ZIME_INGEST_KEY && export ZIME_INGEST_KEY
  1. In any POSIX shell, such as sh or dash, paste the key at the Ingest key prompt and press Enter. The key is not shown on screen and is not saved in the history:
POSIX sh
stty -echo; printf 'Ingest key: '; IFS= read -r ZIME_INGEST_KEY; stty echo; printf '\n'; export ZIME_INGEST_KEY

If you press Ctrl-C at the prompt, your typing stays hidden; type stty echo and press Enter to see it again.

Resend a window
NOOKS_BUCKET=<bucket> START_DATE=<YYYY-MM-DD> WATERMARK_URI=nooks_resend_watermark.json python3 nooks_ingest.py

Never point WATERMARK_URI at the watermark of your scheduled run: a run with an existing watermark starts from that watermark, not from START_DATE. Calls Zime already has answer duplicate.

Frequently asked questions

About the API

What does the Call Ingest API do?

It sends calls from your own dialer, meeting tool or call export into Zime. You post one call at a time with its transcript, a link to its recording, or both. Zime adds the call to the rep's calls, builds its summary and insights, and maps it to a CRM deal.

What does it not do?

It does not read your calls, transcripts or insights: use the REST API for that. It does not create Zime users: every rep must already be an active Zime user in your company. It does not take deal or CRM IDs: Zime maps the deal itself. It does not change or delete a call once Zime has started processing it.

How is it different from the Zime REST API?

The REST API reads data out of Zime. The Call Ingest API sends calls into Zime. They share the base URL https://nodejs-overhaul-prod.zime.ai but use separate keys: your API key cannot send calls, and your ingest key cannot read calls, transcripts or insights.

Which tools can send calls?

Any system that can make an HTTPS request: a dialer, a meeting tool, a data pipeline or a script. Each one sends under a source name that Zime registers for your company. If you use Nooks and export transcripts to Amazon S3, Zime has a sender script for those exports.

What do I need before I send the first call?

Three things. An ingest key, which a company admin generates in Zime. A source, which Zime registers and turns on for your company. And every rep on your calls set up as an active Zime user. Until Zime turns the source on, the API answers 422 unknown_source.

Ingest key

Who can generate an ingest key?

Any company admin. In Zime, go to Admin, then APIs, and press Generate key in the Ingest key (write) card. Only company admins see the APIs tab.

Where do I find the key again later?

Go to Admin, then APIs, and press Show key or Copy in the Ingest key (write) card. A shown key hides again after a minute. Zime records who showed or copied it, and when, under See history.

What does the key look like?

A 43 character string of letters, digits, hyphens and underscores. Zime shows its last four characters so you can tell keys apart.

How do I send the key?

In the X-API-Key header of every request. Header names are not case sensitive, so x-api-key works too. A key in the URL or in an Authorization: Bearer header is not read, and the request returns 401 api_key_required.

Is the ingest key the same as my API key?

No. They are separate keys on the same screen. The ingest key can send calls and check their ingest status; it cannot read calls, transcripts or insights. Your API key cannot send calls: the Call Ingest API answers it with 403 invalid_api_key.

Can we have more than one ingest key?

No. A company has one active ingest key at a time, and all of its sources use it.

How do I rotate the key?

Press Generate new key. The old key stops working the moment the new one is created, so update your scripts with the new key before you close the screen.

What happens to calls already sent when I rotate or revoke the key?

Nothing. A call Zime has accepted keeps processing. The old key only stops new requests, which get 403 invalid_api_key.

How do I revoke the key?

Press Revoke. Your company cannot send calls until an admin generates a new key. Requests with the revoked key get 403 invalid_api_key.

Can I see who generated, showed or revoked the key?

Yes. Press See history. It lists the last 50 changes with the person and the time.

What should I do if the key leaks?

Generate a new key straight away; the leaked key stops working at once. Until then, anyone holding it can send calls into Zime under any active rep in your company, and can read your ingest status list: sources, external_call_id values, call times, errors and zime_call_id. It cannot read calls, transcripts or insights. After you rotate, check GET /api/v1/calls/ingest for calls you did not send, and email support@zime.ai to have them removed.

Sending a call

Which fields are required?

source, external_call_id, started_at, ended_at, rep with an email, at least one participant, and a transcript, a recording or both. At least one participant who is not the rep needs an email or a phone. Everything else is optional.

What are source and external_call_id?

source names the system that sent the call. Zime registers it for your company: 2 to 40 lowercase letters, digits or underscores. external_call_id is that system's own ID for the call: 1 to 128 letters, digits, dots, underscores, colons or hyphens. Together they identify the call. IDs that differ only in letter case are the same call.

Which date and time format do you accept?

ISO 8601 with a time zone, for example 2026-10-19T09:43:35Z or 2026-10-19T15:13:35+05:30. Seconds and fractions of a second are optional. A time without a zone, or a date alone, returns 400 invalid_payload. Write each call's times the same way every time you send it: the same moment with a different offset counts as changed content.

Are there limits on call dates and length?

Yes. started_at can be up to 400 days in the past and up to 1 day in the future. ended_at cannot be before started_at, or more than 24 hours after it. Zime can also set an oldest date for your source; calls before it are skipped with the reason rule:backfill_from.

What format should phone numbers use?

Zime does not check phone formats. It shows the number as you send it, for example in the title of a call whose prospect has no name. Sending E.164, such as +14155550123, keeps numbers consistent.

Which email addresses are accepted?

Plain addresses: letters, digits and . _ % + - before the @, and a domain with a dot. An address with an apostrophe or an accented letter returns 400 invalid_payload, so leave it out and send the phone instead. Zime stores emails in lowercase.

What are the participant roles?

Each participant has a role: prospect, rep or other. A participant with the rep's email counts as the rep. Transcript segments use the same three roles in speaker_role.

What is the difference between a dialer call and a meeting?

call_type is dialer or meeting, and each source has one, set by Zime when it registers the source. Leave call_type out, or send dialer or meeting: the one that is not your source's type returns 422 call_type_mismatch, and any other value returns 400 invalid_payload. Dialer calls are exempt from the short call and no show checks for meetings, never trigger the post call email, and are listed under Dialer on the Calls page.

What does a transcript look like?

An object with format set to segments and a list of segments. Each segment has speaker_role, speaker_name, start_seconds, an optional end_seconds, and text. Send 1 to 5,000 segments, ordered by start_seconds, with up to 10,000 characters of text each. WebVTT, SRT and plain text are not accepted: convert them to segments first.

What if I do not know a speaker's name or when a segment ends?

Send speaker_name as an empty string. Zime then uses the rep's name or the prospect's name from the call, or Sales Rep, Prospect or Speaker. Leave end_seconds out, and Zime estimates it from the length of the text.

Does Zime change the text I send?

Only a little. Line breaks become spaces, and the sequence --> becomes ->, in segment text, speaker names, participant names, phones and the title. Colons in speaker names become spaces.

What is the size limit for one request?

1 MB (1,048,576 bytes) of JSON, sent with Content-Type: application/json. A larger body returns 413 payload_too_large. A request with another Content-Type returns 400 invalid_payload.

Can I add my own fields?

Yes, in metadata: up to 50 keys, with string values of up to 500 characters each. Zime keeps them with the call. Fields this reference does not list are ignored. A resend that changes only metadata does not count as a change.

What happens if I leave out the title or direction?

direction defaults to outbound. Without a title, Zime names the call from its direction and prospect, for example [OUTBOUND] [CALL] with Pat Prospect, using the prospect's name, else phone, else email.

What does disposition do?

It records the outcome from your dialer, such as Meeting Set, in up to 120 characters. Your source's rules can keep or skip calls by disposition. Rules match it exactly, including letter case.

Recordings

Can I send a recording instead of a transcript?

Yes. Put an https link to the file in recording.audio_url. Zime downloads it and transcribes it. Until the transcript is ready, the call's status is processing with the reason Waiting for speech to text. In our tests this took 1 to 3 minutes. A job still pending after 4 hours counts as a failed attempt.

An https link that Zime can download with no extra header and no user name or password in it, on a host on the public internet. Signed links, such as Amazon S3 presigned URLs, work. Zime refuses links to private, loopback, link-local and cloud metadata addresses, and names such as localhost or .internal. It follows up to 5 redirects and checks each one the same way.

Which file formats and sizes are accepted?

WAV, MP3, M4A, OGG, FLAC, WebM, MP4 and MOV, up to 1.5 GB. Zime reads the format from the file itself, or else from its Content-Type. The download must finish within 30 minutes. A link that returns anything else, such as a sign in page, fails with Recording URL did not return audio or video.

Keep it downloadable until the call's status is completed. Zime downloads the recording when its worker picks up the call, not when the API accepts it; in our production tests that was within 30 seconds of sending, and a recording-only call completed within 3 minutes. Waits are longer when many calls are queued. For a call with no transcript, a 4xx answer other than 408 or 429 fails the call at once, and a 5xx, 408 or 429 is tried again, up to 3 attempts in all; a failed call keeps its content, so you can resend it with a fresh link. For a call with a transcript, a failed download is not retried: the call completes with recording_status failed, and a resend cannot add the recording.

What if I send both a transcript and a recording?

Zime keeps your transcript and stores the recording with the call. It does not transcribe the recording again. If the recording cannot be downloaded, the call still completes with your transcript, and recording_status shows failed with the reason. A resend cannot add the recording to a completed call.

How are speakers named when Zime transcribes a recording?

Speech to text labels speakers by voice. When it hears two or more voices, Zime uses AI to tell the rep from the prospect and names them: the rep from rep.name, else the rep's name in Zime, and the prospect from the prospect's name, else their phone. When it hears one voice, or cannot tell who is who, it keeps a generic label such as Speaker 0. If the prospect has no name and no phone, every speaker keeps a generic label. Send the prospect's name, or a transcript with speaker_name, to get named speakers.

Skipped calls

Which calls does Zime skip?

The ones your source's rules drop. Zime sets the rules for each source with you. They can set an oldest call date; dispositions to always keep, to keep only, or to exclude; a minimum duration; a minimum number of turns on each side; a minimum number of transcript characters; and whether to drop calls with no transcript, which is on unless you ask for it off. A skipped call is answered 200 with status skipped and a reason such as rule:min_duration_seconds.

In what order are the rules checked?

The oldest call date first, and nothing bypasses it. Then dispositions to always keep, which bypass every other rule. Then no transcript; minimum turns and minimum characters, only when a transcript is sent; minimum duration; dispositions to keep only; and dispositions to exclude. The first rule that fails names the reason.

What counts as no transcript?

A call with no recording whose segments are empty or say only No transcript available. A call with a recording is not dropped by this rule, because Zime transcribes the recording.

Does Zime store anything from a skipped call?

Only its status record, such as the source, external_call_id, start time and reason. The call content is never stored.

How do I change the rules for my source?

Email support@zime.ai. Zime sets the rules for each source. When you resend a call that a rule skipped, Zime judges it again under the rules in force at that time.

How do I find out why a call was skipped?

The response tells you: status skipped and a reason. You can read it again later from the status routes. Reasons that start with rule: come from your source's rules. Other reasons come from processing, for example No integrated attendee when the rep stopped being an active Zime user after the call was accepted.

Duplicates, resends and updates

What happens if I send the same call twice?

Zime keeps one copy and answers 200 with duplicate: true. A call is the same when its source and external_call_id match, so a resend never creates a second copy.

Do you support an idempotency key?

Yes. external_call_id is the idempotency key. There is no separate header.

Can I update a call I already sent?

Only while it is queued. A resend with changed content replaces the queued copy and returns 200 with updated: true. Once processing has started, changes are ignored and the answer is 200 with duplicate: true and content_changed: true.

What counts as changed content?

A change to any field except metadata. The same moment written with a different time zone offset also counts as a change.

How do I retry a failed call?

Send it again with the same source and external_call_id. A failed call goes back to queued, and the answer is 202. Zime keeps a failed call's content, so a resend of the same content works.

Can I resend a call that was skipped?

Yes. A call a rule skipped is judged again under your source's current rules: kept now, it is queued with 202; dropped again, you get 200 skipped. A call skipped during processing, such as No integrated attendee, is final, and a resend answers duplicate.

Can I delete or replace a completed call?

Not through the API: a resend of a completed call is ignored. To delete a call, a company admin can email support@zime.ai. The privacy policy explains how deletion works.

Status, errors and limits

What does 202 mean?

Zime has accepted the call and queued it. It has not processed it yet. Check its status to see when it completes.

How long until a call is processed?

Zime's worker picks up queued calls every 30 seconds. Measured in production on 3 November 2026: a call with a transcript reached completed 9 to 24 seconds after it was sent, and its summary was ready within 3 minutes. A recording-only call reached completed 1 to 3 minutes after it was sent, and its summary about 2 minutes later. These are measured times, not a guarantee: waits grow when many calls are queued, so follow a backfill with the list route.

How do I check one call?

Send GET /api/v1/calls/ingest/{source}/{external_call_id} with your ingest key. It returns the status, the reason or error, and zime_call_id once the call is in Zime. A call Zime has no record of returns 404 not_found.

How do I check many calls?

Send GET /api/v1/calls/ingest with the filters source, status, start-date and end-date. Calls come oldest first, 100 a page by default and up to 1,000 with page-size. While has_more is true, pass next_cursor as after-key.

Which dates do the list filters use?

The call's started_at. Use YYYY-MM-DD, read as UTC with the end date included, or an ISO 8601 time with a zone. An impossible date such as 2026-02-30 returns 400 invalid_payload.

What do the statuses mean?

queued: accepted and waiting. processing: being processed, or waiting for speech to text. completed: the call is in Zime. skipped: not added; see reason. failed: could not be added; see error, fix the cause and resend.

How many times does Zime retry a call that fails?

Up to 3 attempts for errors that can pass, such as a recording server that is briefly down on a call sent without a transcript. A permanent error, such as a recording link that returns 404, fails at once. Either way the call ends as failed with an error, and you can resend it. On a call sent with a transcript, a failed recording download is not retried: the call completes with recording_status failed.

What is zime_call_id?

The ID of the call in Zime. It is the same ID the REST API returns as call_id, so you can join ingest status to calls, insights and transcripts.

Should I retry errors?

Retry 429 after the number of seconds in Retry-After, and 500 or 503 with growing waits. Do not resend 400, 401, 403, 413 or 422 unchanged: fix the cause first. A resend never creates a second copy of a call.

What is the rate limit?

About 600 requests a minute for each key on each Zime server instance. Zime runs more than one instance and the number changes, so plan for 600 a minute per key. Above that Zime may answer 429 rate_limited with a Retry-After header, in seconds.

How many calls can Zime process at once?

The API accepts calls up to the rate limit, up to 1 MB each, and queues them. Zime's worker then takes up to 5 queued calls every 30 seconds, in turn across sources, and in production today one worker does this for all companies. A large backfill is accepted at once and processed over time.

What you see in Zime

Where do the calls appear in Zime?

On the Calls page, in the Recordings tab, as calls of the rep in rep.email. Dialer calls are hidden by default: tick Dialer in the call filters to see them.

How soon does a call show in Zime?

Zime creates the call while it processes it, so the call is in Zime once its status is completed, with its zime_call_id. In our production tests that was 9 to 24 seconds after sending for a call with a transcript, and 1 to 3 minutes for a recording-only call, with the summary following within 3 minutes. The worker picks up queued calls every 30 seconds, so these are measured times, not a guarantee.

How is the rep matched?

By rep.email, to an active Zime user in your company. Letter case does not matter. With no such user the API returns 422 rep_not_found. With a user who is not active, for example deactivated or still pending approval, it returns 422 rep_inactive: activate or approve the user, then resend.

What if a rep is not in Zime yet?

Add them as a Zime user, and make sure the user is active, not pending approval. Then send the call again. A call refused with 422 is not stored, so the resend is accepted as a new call.

Can a prospect have only a phone number?

Yes. At least one participant who is not the rep needs an email or a phone, and a phone alone is enough. A prospect with only a phone makes the call external.

When is a call treated as internal?

When every participant who is not the rep has an email on your company's own domain. Zime builds no insights for an internal call. A prospect sent with the rep's email counts as the rep, so that call is internal too.

How is the call linked to a CRM deal?

Zime maps it to a deal itself, about 10 minutes after the call is created, and checks again later while no deal is linked. You do not send deal IDs. The deal can be changed from the call page in Zime, as for any call.

Will a pushed call get checklists, a summary and insights?

Yes. Zime matches each pushed call to your company's checklists the same way as other calls, including checklists set up for its source, and queues them for processing. It builds the summary and insights like any other call, unless the call is too short, internal or a no show. A call whose stored transcript, timestamps and speaker names included, is under 500 characters for a dialer call or 800 for a meeting gets no insights. A meeting under 5 minutes, or one where only the rep spoke, is treated as short or a no show; dialer calls are exempt from both checks.

Does a pushed call send the post call email?

Not for dialer calls: they never trigger the post call email. A pushed meeting goes through the post call email step the same way as any other meeting.

Data and security

Is the data encrypted?

Yes. Send every request over HTTPS. Once a call is in Zime it is stored like every other call, encrypted in transit and at rest, as the privacy policy describes.

What does Zime keep from a request?

For an accepted call, its content until processing ends. A completed call then lives in Zime like any other call, and the intake copy is cleared. A failed call keeps its content so you can resend it. A skipped call keeps only its status record, never its content.

Where is the data stored?

Zime stores the transcripts of pushed calls in Amazon S3 in the US East (N. Virginia) region, and their recordings in Google Cloud Storage in the US multi-region. The privacy policy covers international transfers.

How long does Zime keep pushed calls?

The same as every other call: as set by your company's agreement with Zime, otherwise until an authorized admin of your company asks us to delete them. The privacy policy has the details.

It needs to be reachable from the internet without an extra header. A signed link that expires is the safer choice: it only needs to work until the call shows completed.

Nooks exports in Amazon S3

If we use Nooks and export transcripts to Amazon S3, how do we send those calls to Zime?

With Zime's sender script, a Python script that reads the call exports Nooks writes to your S3 bucket and sends each call to the Call Ingest API. It runs in your own AWS account, so your bucket access stays with you. Zime shares the sender script and the setup steps over a secure channel. Your company admin generates the ingest key in Zime under Admin, then APIs; keep it in your secret store and never send it by email or chat in plain text.

What does the script need?

Python 3 with boto3, which the AWS Lambda Python runtime already includes. Your ingest key in ZIME_INGEST_KEY, kept in your secret store and given to the script as an environment variable. Read access to the export bucket.

Which AWS permissions does it need?

For the Lambda role or the credentials the shell uses: s3:ListBucket on the export bucket and s3:GetObject on its objects, always. With the watermark in the export bucket, add s3:PutObject on the watermark key; the two above already cover the rest. With the watermark in a separate bucket, add s3:ListBucket on that bucket, and s3:GetObject and s3:PutObject on the watermark key. s3:ListBucket on the watermark's bucket is required: without it, S3 answers a read of a watermark that does not exist yet with AccessDenied instead of NoSuchKey, the script treats only a missing object as a first run, and every scheduled run would fail before sending anything. A watermark in a local file needs no S3 permission.

How do we give the script the key when running it from a shell?

The script reads the key only from the environment. Never type the key into a command, because it would then be saved in the shell history. Load it in one of these ways; the commands in these answers then run without the key. 1. Export it from your secret store. 2. In bash (run it in bash, e.g. start bash first), run read -rs ZIME_INGEST_KEY && export ZIME_INGEST_KEY then paste the key at the hidden prompt and press Enter. 3. In any POSIX shell, such as sh or dash, run stty -echo; printf 'Ingest key: '; IFS= read -r ZIME_INGEST_KEY; stty echo; printf '\n'; export ZIME_INGEST_KEY then paste the key at the Ingest key prompt and press Enter. The key is not shown on screen and is not saved in the history. If you press Ctrl-C at the prompt, your typing stays hidden; type stty echo and press Enter to see it again.

What S3 layout does it expect?

The layout Nooks writes: one folder per day, named YYYY_MM_DD after the call's UTC start date, holding one Nooks-*.zip per call with a metadata JSON file and a transcript text file.

What does it send for each call?

The Nooks callId as external_call_id, the start and end times, the direction, the disposition, the first participant as the rep, the last participant as the prospect, and the transcript. Nooks exports have no audio, so no recording is sent.

How should we run it?

Always run the scheduled mode: the same Lambda handler on an EventBridge schedule, every 15 minutes or once a day after 09:30 UTC. It keeps a watermark, its place in the bucket. When the key is refused, the source is not on yet, or errors outlast the retries, the run stops and keeps its place, so the next run tries that call again. A call Zime refuses for itself, such as rep_not_found, is logged and passed over; see the next answers. You can add an S3 ObjectCreated trigger beside the schedule for faster delivery; both send the same calls and Zime answers duplicate to the repeats. Do not run S3 events alone: a call an event cannot send is dropped after Lambda's retries. Set the Lambda timeout to 900 seconds and the memory to 512 MB; the 3 second default timeout is too short.

What does the script do with each answer from Zime?

Accepted (queued, skipped by your rules, duplicate or updated): counted, and the run moves on. Refused for that call (a 400, or a 422 such as rep_not_found or rep_inactive), or an export it cannot read: logged and counted as rejected:<error> or bad_export, and the run moves on; in a scheduled run its watermark moves past the call, so no run sends it again by itself. 401, 403, 422 unknown_source, and 429, 5xx, network or S3 read errors that outlast the retries: the run stops; a scheduled run keeps its watermark and the next run starts with that call, while an S3 event is dropped after Lambda's retries.

How do we resend calls Zime refused?

Fix the cause first, for example add the rep in Zime and make sure the user is active. Zime stores nothing for a refused call, so the resend is accepted as a new call. Then, with the key loaded as in the answer on running from a shell, run NOOKS_BUCKET=<bucket> python3 nooks_ingest.py --key <object key> for one call, or resend its window. To resend a window, load the key as in the answer on running from a shell, then run the scheduled mode once with START_DATE set to the day before the problem began and a watermark file that does not exist yet, using NOOKS_BUCKET=<bucket> START_DATE=<YYYY-MM-DD> WATERMARK_URI=nooks_resend_watermark.json python3 nooks_ingest.py as the command. Use a new watermark file, never the one of your scheduled run: a run with an existing watermark starts from that watermark, not from START_DATE. If the run stops early, run the same command again and it continues from its own watermark. It sends every call in the window, and the ones Zime already has answer duplicate; add --dry-run first to see the count as would_send.

Will Nooks's double export create duplicates?

No. Nooks writes each call twice: soon after the call, and again in a 09:00 UTC batch the next day. The second copy answers duplicate.

How do we backfill older calls?

Set START_DATE to the oldest day to send, as YYYY-MM-DD; the default is 2026-08-06. The first scheduled run sends everything from that date and continues on the next run if it runs out of time. Zime still skips anything older than your source's oldest call date.

How do we test it safely?

Run python3 nooks_ingest.py --dry-run first. It reads the bucket, sends nothing, moves no watermark and reports would_send. Then send one call with --key and the object key, and check it with the status route.

Which calls does the script not send?

Exports whose transcript says only No transcript available, calls with one participant, and day folders before START_DATE. Each run counts them. A prospect email of No email found, or one with characters Zime does not accept, is left out and the call is still sent.

What does the script log?

Counts of each outcome per run, and one line per call with the call ID only: never a transcript, an email, a phone or the key.

What if a run gets 401 or 403?

The key was refused, so the script stops sending. A scheduled run saves its watermark on the last call done and stops; on Lambda the invocation ends in an error, and from the command line it exits with status 1. The next run starts with the call that failed and sends nothing before it again. An S3 event run fails the event; Lambda retries it, twice by default, and then drops it; the scheduled run sends that call, which is why it must always run. Fix the key: check that ZIME_INGEST_KEY holds the current key, because a new key or a revoke stops the old one. 403 company_inactive means your company is not active in Zime; email support@zime.ai. Then let the scheduled run continue. Only S3 events running without the scheduled mode miss calls. To resend a window, load the key as in the answer on running from a shell, then run the scheduled mode once with START_DATE set to the day before the problem began and a watermark file that does not exist yet, using NOOKS_BUCKET=<bucket> START_DATE=<YYYY-MM-DD> WATERMARK_URI=nooks_resend_watermark.json python3 nooks_ingest.py as the command. Use a new watermark file, never the one of your scheduled run: a run with an existing watermark starts from that watermark, not from START_DATE. If the run stops early, run the same command again and it continues from its own watermark. It sends every call in the window, and the ones Zime already has answer duplicate; add --dry-run first to see the count as would_send.

What if a run gets 422 unknown_source?

Zime has not turned your source on yet. A scheduled run stops without moving its watermark, and the first run after Zime turns the source on sends everything from that call. An S3 event that gets this answer fails and is dropped after Lambda's retries; the scheduled run sends that call.

How does it handle 429 and server errors?

It tries 429, 5xx and network errors up to 6 times in all, with growing waits, and honours Retry-After up to 30 seconds. If they persist, the run stops the same way as for a refused key: a scheduled run keeps its watermark and the next run starts from the call that failed, while an S3 event is dropped after Lambda's retries. That is why the scheduled mode should always run.

How do we resend one call?

Fix the cause, then, with the key loaded as in the answer on running from a shell, run NOOKS_BUCKET=<bucket> python3 nooks_ingest.py --key <object key> for that call. If Zime already has the call, it answers duplicate.

Support

Who do I contact for help?

Email support@zime.ai with your company name, the source, the external_call_id and the response you got. Never send the key.

Need to read calls, insights and transcripts out of Zime? Use the REST API with your API key. Questions about the Call Ingest API? Email support@zime.ai with the source and external_call_id, never the key.