curl --request POST \
--url https://ingest.zelto.ai/webhooks/calls \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"call": {
"externalId": "<string>",
"startedAt": "2023-11-07T05:31:56Z",
"endedAt": "2023-11-07T05:31:56Z",
"durationSeconds": 1,
"endedReason": "<string>",
"cost": 1,
"recordingUrl": "<string>",
"recordingUploadId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer": {
"number": "<string>",
"name": "<string>"
}
},
"agent": {
"externalId": "<string>",
"name": "<string>"
},
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"version": "<string>",
"companyExternalId": "<string>",
"systemPrompt": "<string>",
"metadata": {},
"versionConfig": {}
}
'import requests
url = "https://ingest.zelto.ai/webhooks/calls"
payload = {
"call": {
"externalId": "<string>",
"startedAt": "2023-11-07T05:31:56Z",
"endedAt": "2023-11-07T05:31:56Z",
"durationSeconds": 1,
"endedReason": "<string>",
"cost": 1,
"recordingUrl": "<string>",
"recordingUploadId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer": {
"number": "<string>",
"name": "<string>"
}
},
"agent": {
"externalId": "<string>",
"name": "<string>"
},
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"version": "<string>",
"companyExternalId": "<string>",
"systemPrompt": "<string>",
"metadata": {},
"versionConfig": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
call: {
externalId: '<string>',
startedAt: '2023-11-07T05:31:56Z',
endedAt: '2023-11-07T05:31:56Z',
durationSeconds: 1,
endedReason: '<string>',
cost: 1,
recordingUrl: '<string>',
recordingUploadId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
customer: {number: '<string>', name: '<string>'}
},
agent: {externalId: '<string>', name: '<string>'},
agentId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
version: '<string>',
companyExternalId: '<string>',
systemPrompt: '<string>',
metadata: {},
versionConfig: {}
})
};
fetch('https://ingest.zelto.ai/webhooks/calls', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://ingest.zelto.ai/webhooks/calls",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'call' => [
'externalId' => '<string>',
'startedAt' => '2023-11-07T05:31:56Z',
'endedAt' => '2023-11-07T05:31:56Z',
'durationSeconds' => 1,
'endedReason' => '<string>',
'cost' => 1,
'recordingUrl' => '<string>',
'recordingUploadId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'customer' => [
'number' => '<string>',
'name' => '<string>'
]
],
'agent' => [
'externalId' => '<string>',
'name' => '<string>'
],
'agentId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'version' => '<string>',
'companyExternalId' => '<string>',
'systemPrompt' => '<string>',
'metadata' => [
],
'versionConfig' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://ingest.zelto.ai/webhooks/calls"
payload := strings.NewReader("{\n \"call\": {\n \"externalId\": \"<string>\",\n \"startedAt\": \"2023-11-07T05:31:56Z\",\n \"endedAt\": \"2023-11-07T05:31:56Z\",\n \"durationSeconds\": 1,\n \"endedReason\": \"<string>\",\n \"cost\": 1,\n \"recordingUrl\": \"<string>\",\n \"recordingUploadId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"customer\": {\n \"number\": \"<string>\",\n \"name\": \"<string>\"\n }\n },\n \"agent\": {\n \"externalId\": \"<string>\",\n \"name\": \"<string>\"\n },\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"version\": \"<string>\",\n \"companyExternalId\": \"<string>\",\n \"systemPrompt\": \"<string>\",\n \"metadata\": {},\n \"versionConfig\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://ingest.zelto.ai/webhooks/calls")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"call\": {\n \"externalId\": \"<string>\",\n \"startedAt\": \"2023-11-07T05:31:56Z\",\n \"endedAt\": \"2023-11-07T05:31:56Z\",\n \"durationSeconds\": 1,\n \"endedReason\": \"<string>\",\n \"cost\": 1,\n \"recordingUrl\": \"<string>\",\n \"recordingUploadId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"customer\": {\n \"number\": \"<string>\",\n \"name\": \"<string>\"\n }\n },\n \"agent\": {\n \"externalId\": \"<string>\",\n \"name\": \"<string>\"\n },\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"version\": \"<string>\",\n \"companyExternalId\": \"<string>\",\n \"systemPrompt\": \"<string>\",\n \"metadata\": {},\n \"versionConfig\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://ingest.zelto.ai/webhooks/calls")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"call\": {\n \"externalId\": \"<string>\",\n \"startedAt\": \"2023-11-07T05:31:56Z\",\n \"endedAt\": \"2023-11-07T05:31:56Z\",\n \"durationSeconds\": 1,\n \"endedReason\": \"<string>\",\n \"cost\": 1,\n \"recordingUrl\": \"<string>\",\n \"recordingUploadId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"customer\": {\n \"number\": \"<string>\",\n \"name\": \"<string>\"\n }\n },\n \"agent\": {\n \"externalId\": \"<string>\",\n \"name\": \"<string>\"\n },\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"version\": \"<string>\",\n \"companyExternalId\": \"<string>\",\n \"systemPrompt\": \"<string>\",\n \"metadata\": {},\n \"versionConfig\": {}\n}"
response = http.request(request)
puts response.read_body{
"received": true
}{
"error": "<string>",
"message": "<string>",
"details": {}
}{
"error": "<string>",
"message": "<string>",
"details": {}
}Upload a conversation
Provider-agnostic conversation ingestion. POST a conversation in Zelto’s canonical shape (the default), or a native Vapi/Retell webhook payload selected with the X-Zelto-Provider header. The delivery is acknowledged immediately and processed asynchronously into a conversation + transcript. Deduplicated by conversation id: re-uploading the same conversation updates it in place and never double-charges. Non-terminal native events (e.g. Vapi status-update) are accepted and ignored.
curl --request POST \
--url https://ingest.zelto.ai/webhooks/calls \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"call": {
"externalId": "<string>",
"startedAt": "2023-11-07T05:31:56Z",
"endedAt": "2023-11-07T05:31:56Z",
"durationSeconds": 1,
"endedReason": "<string>",
"cost": 1,
"recordingUrl": "<string>",
"recordingUploadId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer": {
"number": "<string>",
"name": "<string>"
}
},
"agent": {
"externalId": "<string>",
"name": "<string>"
},
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"version": "<string>",
"companyExternalId": "<string>",
"systemPrompt": "<string>",
"metadata": {},
"versionConfig": {}
}
'import requests
url = "https://ingest.zelto.ai/webhooks/calls"
payload = {
"call": {
"externalId": "<string>",
"startedAt": "2023-11-07T05:31:56Z",
"endedAt": "2023-11-07T05:31:56Z",
"durationSeconds": 1,
"endedReason": "<string>",
"cost": 1,
"recordingUrl": "<string>",
"recordingUploadId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer": {
"number": "<string>",
"name": "<string>"
}
},
"agent": {
"externalId": "<string>",
"name": "<string>"
},
"agentId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"version": "<string>",
"companyExternalId": "<string>",
"systemPrompt": "<string>",
"metadata": {},
"versionConfig": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
call: {
externalId: '<string>',
startedAt: '2023-11-07T05:31:56Z',
endedAt: '2023-11-07T05:31:56Z',
durationSeconds: 1,
endedReason: '<string>',
cost: 1,
recordingUrl: '<string>',
recordingUploadId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
customer: {number: '<string>', name: '<string>'}
},
agent: {externalId: '<string>', name: '<string>'},
agentId: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
version: '<string>',
companyExternalId: '<string>',
systemPrompt: '<string>',
metadata: {},
versionConfig: {}
})
};
fetch('https://ingest.zelto.ai/webhooks/calls', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://ingest.zelto.ai/webhooks/calls",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'call' => [
'externalId' => '<string>',
'startedAt' => '2023-11-07T05:31:56Z',
'endedAt' => '2023-11-07T05:31:56Z',
'durationSeconds' => 1,
'endedReason' => '<string>',
'cost' => 1,
'recordingUrl' => '<string>',
'recordingUploadId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'customer' => [
'number' => '<string>',
'name' => '<string>'
]
],
'agent' => [
'externalId' => '<string>',
'name' => '<string>'
],
'agentId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'version' => '<string>',
'companyExternalId' => '<string>',
'systemPrompt' => '<string>',
'metadata' => [
],
'versionConfig' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://ingest.zelto.ai/webhooks/calls"
payload := strings.NewReader("{\n \"call\": {\n \"externalId\": \"<string>\",\n \"startedAt\": \"2023-11-07T05:31:56Z\",\n \"endedAt\": \"2023-11-07T05:31:56Z\",\n \"durationSeconds\": 1,\n \"endedReason\": \"<string>\",\n \"cost\": 1,\n \"recordingUrl\": \"<string>\",\n \"recordingUploadId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"customer\": {\n \"number\": \"<string>\",\n \"name\": \"<string>\"\n }\n },\n \"agent\": {\n \"externalId\": \"<string>\",\n \"name\": \"<string>\"\n },\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"version\": \"<string>\",\n \"companyExternalId\": \"<string>\",\n \"systemPrompt\": \"<string>\",\n \"metadata\": {},\n \"versionConfig\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://ingest.zelto.ai/webhooks/calls")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"call\": {\n \"externalId\": \"<string>\",\n \"startedAt\": \"2023-11-07T05:31:56Z\",\n \"endedAt\": \"2023-11-07T05:31:56Z\",\n \"durationSeconds\": 1,\n \"endedReason\": \"<string>\",\n \"cost\": 1,\n \"recordingUrl\": \"<string>\",\n \"recordingUploadId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"customer\": {\n \"number\": \"<string>\",\n \"name\": \"<string>\"\n }\n },\n \"agent\": {\n \"externalId\": \"<string>\",\n \"name\": \"<string>\"\n },\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"version\": \"<string>\",\n \"companyExternalId\": \"<string>\",\n \"systemPrompt\": \"<string>\",\n \"metadata\": {},\n \"versionConfig\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://ingest.zelto.ai/webhooks/calls")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"call\": {\n \"externalId\": \"<string>\",\n \"startedAt\": \"2023-11-07T05:31:56Z\",\n \"endedAt\": \"2023-11-07T05:31:56Z\",\n \"durationSeconds\": 1,\n \"endedReason\": \"<string>\",\n \"cost\": 1,\n \"recordingUrl\": \"<string>\",\n \"recordingUploadId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"customer\": {\n \"number\": \"<string>\",\n \"name\": \"<string>\"\n }\n },\n \"agent\": {\n \"externalId\": \"<string>\",\n \"name\": \"<string>\"\n },\n \"agentId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"version\": \"<string>\",\n \"companyExternalId\": \"<string>\",\n \"systemPrompt\": \"<string>\",\n \"metadata\": {},\n \"versionConfig\": {}\n}"
response = http.request(request)
puts response.read_body{
"received": true
}{
"error": "<string>",
"message": "<string>",
"details": {}
}{
"error": "<string>",
"message": "<string>",
"details": {}
}Authorizations
Send Authorization: Bearer YOUR_API_KEY. The key resolves to a single organization.
Headers
Which schema the request body uses. Defaults to zelto (the canonical CallIngest shape, also used by livekit). Native vapi / retell webhook payloads are also accepted.
zelto, vapi, retell, livekit Body
When X-Zelto-Provider is zelto (or omitted), the canonical CallIngest body. When vapi / retell, that provider's native webhook payload.
Zelto's canonical conversation upload shape (X-Zelto-Provider: zelto). Provide either agent.externalId (find-or-create) or agentId (attach to an existing agent).
Show child attributes
Show child attributes
Find-or-create an agent by your own id. Required unless agentId is given.
Show child attributes
Show child attributes
Attach to an existing Zelto agent. Takes precedence over agent.externalId.
Release label of the agent that actually handled this conversation. Optional for ingestion; send it on every conversation for reliable experiments. Case-sensitive, 1–255 characters after trimming. The same agent and label reuse a version; a new label creates one on the first processed conversation. Keep the agent ID stable and change this label for prompt or pipeline changes. Preserve the original label on retries. This is a top-level field, not metadata.version. See /docs/agent-versions.
1 - 255Your stable per-conversation id for the client or brand this conversation belongs to. When Companies is enabled, Zelto maps it to a company; otherwise the agent's default company is used when available.
1 - 255Show child attributes
Show child attributes
Actual system prompt of the version that handled this conversation. A leading system transcript turn is used if omitted. Available text is captured when a version is first observed, or fills a missing prompt later; an existing prompt snapshot is not overwritten. A version label alone does not supply a prompt.
Free-form metadata stored on the conversation.
Context for the version that handled this conversation. Requires the version field. Captured when the version is observed; does not overwrite an existing configuration snapshot. Use config when registering through POST /v1/agents/{id}/versions.
Response
Accepted for processing. Also returned for duplicate deliveries and dropped (non-terminal) events.

