Cases
Create, read, update, and delete case records — reusable call scripts with AI instructions.
A case is a reusable call script that defines everything the AI assistant needs for a conversation:
- First Message — the opening line (supports
{{variables}}) - System Prompt — personality, instructions, and conversation flow
- Tools — actions the AI can take (e.g., book a meeting, end the call)
- Variables — dynamic placeholders resolved from contact data or custom values
Pass a caseId when creating a call to use the case as the call script.
Tools cannot be attached through the API. Create and update the script here, then attach tools to the case in the dashboard.
The Variable Object
Used by variables on create and update, and returned by Get Case.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Placeholder name, referenced as {{name}} in firstMessage and prompt |
source | string | Yes | Where the value comes from — contact.title, contact.email, contact.notes, contact.phone, or custom |
defaultValue | string | No | Used when the source resolves to nothing |
required | boolean | No | Whether a call must supply this value |
List Cases
Retrieve a paginated list of cases.
GET /api/v1/casesRequired scope: cases:read
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 25 | Results per page (1–100) |
cursor | string | — | Pagination cursor from a previous response. Valid only for the same filter set — change a filter and you must restart without a cursor (reusing one returns 400 INVALID_CURSOR). |
search | string | — | Case title search query (2–100 chars) |
Example Request
curl -X GET "https://app.erzycall.com/api/v1/cases?limit=10" \
-H "X-API-Key: ek_live_abc123"Example Response
List results omit prompt and the full variable objects — fetch a single case for those.
{
"data": [
{
"id": "case_789",
"title": "Appointment reminder",
"description": "Confirms tomorrow's booking",
"firstMessage": "Hi, is this {{name}}?",
"variables": ["name"],
"createdAt": "2025-01-12T09:00:00Z",
"updatedAt": "2025-01-12T09:00:00Z"
}
],
"pagination": {
"cursor": "eyJwb3...",
"hasMore": false,
"pageSize": 10
}
}Get Case
Retrieve details for a single case.
GET /api/v1/cases/{id}Required scope: cases:read
Example Request
curl -X GET "https://app.erzycall.com/api/v1/cases/case_789" \
-H "X-API-Key: ek_live_abc123"Example Response
{
"data": {
"id": "case_789",
"title": "Appointment reminder",
"description": "Confirms tomorrow's booking",
"firstMessage": "Hi, is this {{name}}?",
"prompt": "You are a friendly assistant confirming an appointment.",
"systemPromptId": null,
"variables": [
{ "name": "name", "source": "contact.title", "required": true }
],
"tags": ["reminders"],
"toolIds": [],
"composioToolIds": [],
"keyterms": null,
"createdAt": "2025-01-12T09:00:00Z",
"updatedAt": "2025-01-12T09:00:00Z"
}
}Create Case
Create a new case.
POST /api/v1/casesRequired scope: cases:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Case name (1–200 chars) |
prompt | string | Yes | System prompt (1–50,000 chars), supports {{variables}} |
firstMessage | string | No | Opening line (max 1,000 chars), supports {{variables}}. Defaults to empty. |
description | string | No | Internal description (max 2,000 chars) |
tags | string[] | No | Tags (max 20 tags, each max 50 chars) |
variables | object[] | No | Variable definitions (max 50) — see The Variable Object |
keyterms | string | No | Newline-separated terms to bias speech recognition (max 5,000 chars) |
Example Request
curl -X POST "https://app.erzycall.com/api/v1/cases" \
-H "X-API-Key: ek_live_abc123" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-case-appointment-reminder" \
-d '{
"title": "Appointment reminder",
"prompt": "You are a friendly assistant confirming an appointment.",
"firstMessage": "Hi, is this {{name}}?",
"variables": [
{ "name": "name", "source": "contact.title", "required": true }
]
}'Response
Returns 201 Created with the full case object, in the same shape as Get Case.
Update Case
Update a case. Only the fields you send are changed.
PATCH /api/v1/cases/{id}Required scope: cases:write
Request Body
Every field from Create Case is accepted, and all are optional.
A Get Case response can be sent straight back through this endpoint: fields that are unset come back as null, and null is read as "leave it unset" rather than rejected.
If Get Case returns a non-null systemPromptId, the case draws its prompt from a Prompt Library entry and the prompt field you see is only a fallback — calls use the library version. Sending prompt for such a case returns 409 CONFLICT rather than saving a value that would never take effect. Edit the linked prompt, or unlink the case in the dashboard, first.
Example Request
curl -X PATCH "https://app.erzycall.com/api/v1/cases/case_789" \
-H "X-API-Key: ek_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"firstMessage": "Hi {{name}}, quick reminder about tomorrow."
}'Response
Returns 200 OK with the updated case object.
Delete Case
Permanently delete a case.
DELETE /api/v1/cases/{id}Required scope: cases:write
Example Request
curl -X DELETE "https://app.erzycall.com/api/v1/cases/case_789" \
-H "X-API-Key: ek_live_abc123"Response
Returns 204 No Content on success.
Returns 409 CONFLICT while queued calls, recurring schedules, or AutoFollow campaigns still reference the case. Neither a schedule nor a campaign can exist without its case, so deleting one out from under them breaks them permanently. Cancel those calls, or re-point the schedules and campaigns, then retry.
{
"error": {
"code": "CONFLICT",
"message": "Case is still referenced by 2 queued call(s), 1 recurring schedule(s). Cancel the calls, or re-point the schedules and campaigns, first."
}
}