Calls
List, create, view, and cancel voice calls.
The Call Object
Every calls endpoint returns call objects with the following fields:
| Field | Type | Description |
|---|---|---|
id | string | Unique call ID. Use it with GET /calls/{id}. |
status | string | new, scheduled, processing, ended, cancelled, error |
direction | string | outbound — your assistant placed the call. inbound — someone called your number. |
callFrom | string | Your organization's phone number |
callTo | string | The other party: the person called (outbound) or the caller (inbound) |
caseId | string | null | Case used as the call script |
contactId | string | null | Linked contact |
firstMessage | string | null | The assistant's opening message |
apiCallId | string | null | Voice-platform call ID, set once the call starts |
apiCallStatus | string | null | Live platform status: queued, ringing, in-progress, ended, forwarding |
endedReason | string | null | Why the call ended, e.g. customer-ended-call, assistant-ended-call, voicemail-reached |
resultStatus | string | null | AI analysis verdict: Answered, Voicemail, Not Answered |
recordingUrl | string | null | Audio recording of the call |
durationSeconds | number | null | Call length in seconds, null until the call has ended |
variableValues | object | null | Variables injected into the call |
createdAt, updatedAt | string | ISO 8601 timestamps |
GET /calls/{id} additionally returns a transcript array — see Get Call.
List Calls
Retrieve a paginated list of calls for your organization — both outbound and inbound. Use each call's direction field to tell them apart.
GET /api/v1/callsRequired scope: calls:read
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 25 | Results per page (1–100) |
cursor | string | — | Pagination cursor from a previous response |
status | string | — | Filter by status: new, scheduled, processing, ended, cancelled, error |
contactId | string | — | Filter by contact ID |
caseId | string | — | Filter by case ID |
from | string | — | ISO 8601 datetime — only calls after this time |
to | string | — | ISO 8601 datetime — only calls before this time |
Example Request
curl -X GET "https://app.erzycall.com/api/v1/calls?limit=10&status=ended" \
-H "X-API-Key: ek_live_abc123"Example Response
{
"data": [
{
"id": "jd7bk3xw9q2m5n8p1r4t6v0y2c",
"status": "ended",
"direction": "outbound",
"callTo": "+14155551234",
"callFrom": "+14155559999",
"caseId": "case_789",
"contactId": "contact_456",
"firstMessage": "Hi, this is a follow-up call regarding your inquiry.",
"apiCallId": "019c1b2e-8f3a-4c5d-9e6f-7a8b9c0d1e2f",
"apiCallStatus": "ended",
"endedReason": "customer-ended-call",
"resultStatus": "Answered",
"recordingUrl": "https://storage.example.com/recordings/019c1b2e.wav",
"durationSeconds": 45,
"variableValues": { "name": "Ahmad" },
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:31:02.000Z"
}
],
"pagination": {
"cursor": "eyJwb3...",
"hasMore": true,
"pageSize": 10
}
}Create Call
Trigger a new outbound call or schedule one for later.
POST /api/v1/callsRequired scope: calls:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Destination phone number in E.164 format (e.g., +14155551234) |
from | string | Yes | Caller ID — must be a phone number assigned to your organization |
contactId | string | No | Associate with an existing contact. Also enables variable resolution — {{name}}, {{email}}, {{phone}}, and {{notes}} are automatically populated from the contact record. |
caseId | string | No | Use an existing case as the call script. The case provides the first message, system prompt, tools, and variable definitions. See Using Cases below. |
firstMessage | string | No | Opening message for the AI assistant (max 1,000 chars). Overrides the case's first message if both are provided. |
scheduledAt | string | No | ISO 8601 datetime to schedule the call for later. Omit for an immediate call. |
variableValues | object | No | Key-value pairs to inject into the first message and system prompt. Variables use {{variableName}} syntax in templates. |
Using Cases with Calls
A case is a reusable call script that defines the AI assistant's first message, system prompt (personality, instructions, conversation flow), tools (e.g., calendar booking), and variable definitions. When you pass a caseId, the assistant follows the case structure automatically.
The priority for firstMessage and system prompt is: caller-supplied → case → generic default.
| Scenario | First Message | System Prompt | Tools | Best For |
|---|---|---|---|---|
caseId only | ✅ From case | ✅ From case | ✅ From case | Standard scripted calls — the AI follows the full case flow |
firstMessage only | ✅ Your message | ⚠️ Generic default | ❌ None | Simple one-liner calls with no conversation structure |
Both caseId + firstMessage | ✅ Your message (overrides case) | ✅ From case | ✅ From case | Scripted calls with a custom opener per contact |
| Neither | ⚠️ Generic default | ⚠️ Generic default | ❌ None | Not recommended — results in a bare assistant |
Tip: For most use cases, pass a caseId and let the case handle everything. Use firstMessage only when you need a custom opener that differs from the case template.
Variable Resolution
Variables in the case's first message and system prompt (e.g., {{name}}, {{email}}) are resolved automatically from multiple sources:
variableValuesyou pass in the request body (highest priority)- Contact data from the
contactId— maps{{name}}→ contact title,{{email}}→ contact email, etc. - Case variable defaults defined in the case configuration
For example, if your case's first message is "Hello, is this {{name}}?" and you pass a contactId for "Artur Temirov", the AI will say: "Hello, is this Artur Temirov?"
Example: Call with Case
curl -X POST "https://app.erzycall.com/api/v1/calls" \
-H "X-API-Key: ek_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"to": "+60111234567",
"from": "+60360431879",
"caseId": "case_789",
"contactId": "contact_456",
"variableValues": {
"name": "Ahmad",
"company": "TechCorp"
}
}'Example: Simple Call (No Case)
curl -X POST "https://app.erzycall.com/api/v1/calls" \
-H "X-API-Key: ek_live_abc123" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-call-12345" \
-d '{
"to": "+14155551234",
"from": "+14155559999",
"firstMessage": "Hi, this is a follow-up call regarding your inquiry.",
"contactId": "contact_456"
}'Example: Scheduled Call
curl -X POST "https://app.erzycall.com/api/v1/calls" \
-H "X-API-Key: ek_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"to": "+60111234567",
"from": "+60360431879",
"caseId": "case_789",
"contactId": "contact_456",
"scheduledAt": "2025-01-16T14:00:00.000Z"
}'Example Response (201 Created)
{
"data": {
"id": "jd7bk3xw9q2m5n8p1r4t6v0y2c",
"status": "new",
"direction": "outbound",
"callTo": "+14155551234",
"callFrom": "+14155559999",
"caseId": "case_789",
"contactId": "contact_456",
"firstMessage": null,
"apiCallId": null,
"apiCallStatus": null,
"endedReason": null,
"resultStatus": null,
"recordingUrl": null,
"durationSeconds": null,
"variableValues": { "name": "Ahmad", "company": "TechCorp" },
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
}
}apiCallId, recordingUrl, durationSeconds, and the other result fields are null until the call is picked up and completed — poll GET /calls/{id} or subscribe to webhooks for updates.
Immediate calls have status new and are picked up by the system within ~60 seconds. Scheduled calls have status scheduled and fire at the specified scheduledAt time.
Errors
| Status | Code | Description |
|---|---|---|
| 422 | INVALID_PHONE | The from number is not assigned to your organization |
| 404 | NOT_FOUND | Referenced contactId or caseId not found |
Get Call
Retrieve details for a single call, including the full conversation transcript.
GET /api/v1/calls/{id}Required scope: calls:read
Example Request
curl -X GET "https://app.erzycall.com/api/v1/calls/jd7bk3xw9q2m5n8p1r4t6v0y2c" \
-H "X-API-Key: ek_live_abc123"Example Response
The example below is an inbound call: callFrom is the organization's number and callTo is the person who called it.
{
"data": {
"id": "jd7bk3xw9q2m5n8p1r4t6v0y2c",
"status": "ended",
"direction": "inbound",
"callTo": "+60111234567",
"callFrom": "+60360431879",
"caseId": null,
"contactId": "contact_456",
"firstMessage": null,
"apiCallId": "019c1b2e-8f3a-4c5d-9e6f-7a8b9c0d1e2f",
"apiCallStatus": "ended",
"endedReason": "customer-ended-call",
"resultStatus": "Answered",
"recordingUrl": "https://storage.example.com/recordings/019c1b2e.wav",
"durationSeconds": 47,
"variableValues": null,
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:31:02.000Z",
"transcript": [
{
"role": "assistant",
"content": "Hello! Thanks for calling. How can I help you today?",
"timestamp": 1736937000000
},
{
"role": "user",
"content": "Hi, I'd like to book an appointment for tomorrow morning.",
"timestamp": 1736937004500
},
{
"role": "assistant",
"content": "Of course — let me check tomorrow's availability.",
"timestamp": 1736937008200
}
]
}
}Transcript
transcript is an array of conversation messages in order:
| Field | Type | Description |
|---|---|---|
role | string | assistant (your AI), user (the other party), or system |
content | string | What was said |
timestamp | number | null | Epoch milliseconds, when available |
The transcript fills in while the call is in progress and is complete once status is ended. Calls that never connected (e.g. Not Answered) return an empty array.
Cancel Call
Cancel a scheduled or pending call. Only calls with status new or scheduled can be cancelled.
DELETE /api/v1/calls/{id}Required scope: calls:write
Example Request
curl -X DELETE "https://app.erzycall.com/api/v1/calls/abc123" \
-H "X-API-Key: ek_live_abc123"Response
Returns 204 No Content on success.
Errors
| Status | Code | Description |
|---|---|---|
| 404 | NOT_FOUND | Call not found |
| 422 | INVALID_STATE | Call cannot be cancelled in its current status (e.g., already ended) |