Call Jobs API
A Call Job is the batch-level object for outbound calls. A submitted call job contains one or more Scheduled Requests. Each scheduled request represents one outbound call target from the submitted batch.
Evergrove Labs now groups compatible scheduled requests together so one physical call can serve multiple related requests that share the same name, phone number, and ZIP code. This means the platform handles batch-style outreach more efficiently while still tracking results for each request separately.
Submit a batch with
POST /scheduler/call-jobs.Store the returned
call_job_id.Poll
GET /scheduler/call-jobs/{call_job_id}.Read progress from the returned
scheduled_requests[].
Base URL (Staging) |
|
Base URL (Production) |
|
Auth |
|
Content type |
|
Endpoints overview
Method | Path | Purpose |
|---|---|---|
POST |
| Submit a batch of outbound calls. |
GET |
| Retrieve the current state of a call job and its scheduled requests. |
POST |
| Bulk-read scheduled request snapshots by call job or snapshot ID. |
POST /scheduler/call-jobs
Submits one or more outbound calls as a single batch. The API validates every row before any side effects. Normal submissions are all-or-nothing: if any row is invalid, the API returns 422 and no call job, scheduled requests, call inputs, entities, or calls are created or triggered.
Parameter | Type | Required | Notes |
|---|---|---|---|
| boolean | No | Defaults to |
Append these parameters to the request URL. Example: POST /scheduler/call-jobs?validate_only_without_persistence=true.
Request body
Field | Type | Required | Notes |
|---|---|---|---|
| object[] | Yes | Array of |
| boolean | No | Defaults to |
from pydantic import BaseModel, Field
from typing import Optional, Literal
from datetime import datetime, date
Objective = Literal["CONFIRM_IE_ATTENDANCE", "RESCHEDULE"]
class CallJobItem(BaseModel):
external_reference_id: Optional[str] = Field(
None, min_length=1,
description="Your row identifier. If provided on any row, must be on every row."
)
outbound_phone_number: str = Field(
..., description="Valid US number in E.164 or normalizable format."
)
objective: Objective = Field(
..., description="What the agent should accomplish on the call."
)
organization_name: str = Field(
..., description="Must be configured for the tenant."
)
patient_name: str
patient_ssn: Optional[str] = Field(
None, description="Accepts 999-88-7777, 999887777, or no dashes."
)
patient_dob: date = Field(..., description="ISO 8601 date (YYYY-MM-DD).")
patient_phone_number: Optional[str] = Field(
None, description="Same format as outbound_phone_number."
)
date_of_injury: date = Field(..., description="ISO 8601 date (YYYY-MM-DD).")
expected_appointment_date_time: str = Field(
..., description="Naive local datetime (YYYY-MM-DDTHH:MM:SS). No offset or Z suffix."
)
service_modality: str
claim_number: Optional[str] = Field(
"Not Available", description="Defaults to 'Not Available' if omitted or blank."
)
insurer_group_name: str
body_part: str
provider_office: str = Field(
..., description="Natural key for call entity reuse. Case-sensitive."
)
provider_office_address: str = Field(..., description="Free-text address.")
provider_zip: str = Field(
..., pattern=r"^\d{5}$",
description="5-digit US ZIP. Resolves provider timezone server-side. No ZIP+4."
)
notes: Optional[str] = Field(None, description="Free-text notes for the agent.")
class CallJobBatchRequest(BaseModel):
calls: list[CallJobItem] = Field(..., min_length=1)
trigger_now: Optional[bool] = Field(
None, description="Defaults to false. See Scheduling behavior below."
)Example request
API_KEY="$API_KEY"
URL="https://stage-agent-api.evergrovelabs.com/scheduler/call-jobs"
curl -X POST "$URL" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"calls": [
{
"external_reference_id": "row-001",
"outbound_phone_number": "+16035550100",
"objective": "CONFIRM_IE_ATTENDANCE",
"organization_name": "AcmeNet",
"patient_name": "Jane Sample",
"patient_dob": "1980-02-03",
"patient_ssn": "999-88-7777",
"patient_phone_number": "+12125550100",
"date_of_injury": "2026-01-15",
"expected_appointment_date_time": "2026-08-10T09:00:00",
"service_modality": "occupational therapy",
"claim_number": "CLM-789",
"insurer_group_name": "Acme Insurer Group",
"body_part": "left shoulder",
"provider_office": "Concord Orthopaedics",
"provider_office_address": "1 Pillsbury St, Concord NH 03301",
"provider_zip": "03301"
}
]
}'Response: Normal submission (HTTP 200)
Returned when validate_only_without_persistence is omitted or false and the batch is valid. All attributes are returned as JSON.
Attribute | Type | Required | Description |
|---|---|---|---|
| UUID | Yes | Identifies the whole submitted batch. Use this to poll status. |
| datetime | Yes | Server timestamp when the batch was accepted. |
| object[] | Yes | Array containing one entry per submitted call. See Scheduled request result table below. |
Scheduled request result
Each item in scheduled_requests:
Attribute | Type | Required | Description |
|---|---|---|---|
| UUID | Yes | Identifies an individual outbound call request inside the batch. |
| string | No | Echo of the row id if provided in the request. |
| string | Yes | Echo of the submitted |
| UUID | Yes | Underlying call entity id created for this request. |
| UUID | Yes | Identifies the persisted snapshot for this request. Save it to query this request's status later with |
| OutboundCallStatus | Yes |
|
| string | No | Human-readable reason when |
Response: Validation-only mode (HTTP 200)
Returned when validate_only_without_persistence=true. The response is not all-or-nothing: every row is returned with VALID or INVALID status.
Use this as a dry run to surface every field-level error before you trigger real calls. No call job, scheduled requests, entities, or calls are created.
Attribute | Type | Required | Description |
|---|---|---|---|
| boolean | Yes | Always |
| boolean | Yes |
|
| object[] | Yes | Contains one validation result per submitted row. See Validation result row table below. |
Validation result row
Each item in scheduled_requests:
Attribute | Type | Required | Description |
|---|---|---|---|
| string | No | Echo of the row id if provided in the request. |
| string | Yes | Echo of the submitted |
| string | No | Resolved timezone from |
| enum | Yes |
|
| object[] | Yes | Empty when |
Validation error detail
Each item in the errors array:
Attribute | Type | Description |
|---|---|---|
| string | The request field that failed validation. |
| string | Human-readable description of the error. |
Order preservation
Validation-only results are returned in the same order as the submitted calls[]. The implementation iterates through body.calls in order, appends one validation result per row, and returns that list directly as scheduled_requests[]. There is no sorting or grouping step.
Clients can safely map by position:
request.calls[0] -> response.scheduled_requests[0]request.calls[1] -> response.scheduled_requests[1]...
external_reference_id is still the better stable join key when provided, but positional mapping is preserved.
Error responses
Error responses are returned with a non-200 HTTP status and do not contain the response shapes above.
Status | Meaning | Action |
|---|---|---|
| Missing or invalid bearer token. | Check your |
| Normal submission: one or more rows failed validation. Nothing was persisted or triggered. | Fix the listed fields and resubmit. |
| Tenant onboarding is incomplete server-side. | Contact Evergrove support. |
GET /scheduler/call-jobs/{call_job_id}
Returns the aggregate state of a call job, including every scheduled request inside it. Poll the call job until the call job status is terminal.
Path parameters
Parameter | Type | Required | Notes |
|---|---|---|---|
| string (UUID) | Yes | The id returned by |
Query parameters
Parameter | Type | Required | Notes |
|---|---|---|---|
| boolean | No | Defaults to |
Example request
API_KEY="$API_KEY"
CALL_JOB_ID="f15e9b8c-1a2d-4f3e-9c4b-7e8f1a2b3c4d"
URL="https://stage-agent-api.evergrovelabs.com/scheduler/call-jobs/${CALL_JOB_ID}"
curl -G "$URL" \
-H "Authorization: Bearer $API_KEY" \
-H "Accept: application/json" \
--data-urlencode "include_call_attempts=true"Response (HTTP 200)
All attributes are returned as JSON.
Attribute | Type | Required | Description |
|---|---|---|---|
| UUID | Yes | Server-generated id for this call job. |
| string | Yes | Tenant the call job belongs to. |
| string | Yes | Client id that submitted the batch. |
| CallJobDerivedStatus | Yes | Derived status of the whole call job. See CallJobDerivedStatus enum. |
| object | Yes | Counts and percentages for request completion. See Completion summary table. |
| object[] | Yes | Array containing the current state of each request in the batch. See Scheduled request status table below. |
| datetime | Yes | When the call job was created. |
| datetime | Yes | Last update timestamp. |
Completion summary
Each item in completion_summary:
Attribute | Type | Required | Description |
|---|---|---|---|
| integer | Yes | Total number of scheduled requests in the call job. |
| integer | Yes | Number of requests whose entity batch is no longer |
| integer | Yes | Number of requests with a successful terminal status: |
| integer | Yes | Number of requests with a terminal |
| integer | Yes | Number of requests still in progress or otherwise not terminal. |
| number | Yes | Percentage of completed requests, rounded to 2 decimals. |
| number | Yes | Percentage of successfully resolved requests, rounded to 2 decimals. |
Scheduled request status
Each item in scheduled_requests:
Attribute | Type | Required | Description |
|---|---|---|---|
| UUID | Yes | Identifies the individual outbound call request. |
| string | No | Echo of the submitted row id. |
| UUID | Yes | Underlying call input id. |
| UUID | Yes | Underlying call entity id. |
| string | Yes | Tenant the request belongs to. |
| OutboundCallStatus | Yes | Current status. See OutboundCallStatus enum. |
| string | No | Reason the request stopped, if terminal. For example, a completed call or a retryable error. |
| datetime | No | Recommended callback time, or |
| object | No | Latest resolved outcome for this request. See Generated outcomes table. Clients do not need to inspect raw call attempts to understand the current outcome. |
| object[] | No | Public attempt history. Omitted unless |
| datetime | Yes | When the request was created. |
| datetime | Yes | Last update timestamp. |
Generated outcomes
Attribute | Type | Required | Description |
|---|---|---|---|
| boolean | No | Whether the call was placed. |
| string | No | Name of the person reached, if any. |
| enum | No |
|
| object | No | AppointmentSlot with |
| object | No | AppointmentSlot with |
| enum | No |
|
| string | No | Free-text reason for the outcome. |
| boolean | No | Whether a voicemail was left. |
Public call attempt
Each item in call_attempts (only present when include_call_attempts=true):
Attribute | Type | Required | Description |
|---|---|---|---|
| UUID | Yes | Unique id for this attempt. |
| integer | Yes | Sequential attempt number (1-based). |
| enum | Yes |
|
| enum | No |
|
| string | No | Human-readable error, or |
| object | No | Outcomes from this attempt. Same shape as the Generated outcomes table above. Internal attempt fields such as workflow ids, room names, transcript storage, recording ids, and internal evaluations are not exposed. |
| UUID | No | ID of the call analysis result produced for this attempt. Analysis runs only after the recording is received and the call has concluded, so this starts as |
| datetime | Yes | When the attempt was created. |
| datetime | Yes | Last update timestamp. |
Grouped call jobs
Evergrove Labs groups compatible scheduled requests into a single physical call when they share the same patient_name, outbound_phone_number, and provider_zip. This is the default behavior for scheduled outreach. One grouped call can represent multiple related requests, and results are tracked per request.
How requests are bundled
After submission, the scheduler groups scheduled requests by the combination of patient_name, outbound_phone_number, and provider_zip. All requests that share the same name, phone number, and ZIP code are placed into one entity batch. One physical call workflow is started for each entity batch, not for each individual request.
You do not need to change your submission. The platform groups requests automatically. If every request in your batch has a unique combination of name, phone number, and ZIP, each request gets its own physical call.
Per-request status and outcomes
Even though multiple requests may share one physical call, each scheduled request keeps its own workflow_status, stop_reason, generated_outcomes, and call_attempts. Poll GET /scheduler/call-jobs/{call_job_id} to see these fields for every request in the batch.
When a grouped call finishes, the platform splits the results back out per request. The generated_outcomes object for each request reflects the outcome that applies to that specific request, and call_attempts shows the public attempt history shared by the entity batch.
Reviewing grouped call history
To inspect a grouped call job:
Poll
GET /scheduler/call-jobs/{call_job_id}and check the call jobstatusandcompletion_summary.Iterate through
scheduled_requestsand readworkflow_statusandstop_reasonfor each request.Read
generated_outcomesfor the resolved outcome without inspecting raw call attempts.When debugging, pass
include_call_attempts=trueto see the attempt history, disposition, and error details for each request.
POST /scheduler/call-jobs/query
Bulk-reads scheduled request snapshots by call job, snapshot ID, or both. Use it to check the status of specific calls from a submitted batch instead of polling the whole call job. See Track specific calls after a batch API call for the end-to-end workflow.
Query request body
Field | Type | Required | Notes |
|---|---|---|---|
| UUID | No | Scopes the query to one call job. Required unless |
| UUID[] | No | Non-empty array of snapshot IDs. Required unless |
| boolean | No | Defaults to |
At least one of call_job_id or snapshot_ids is required. When both are supplied, they narrow the result together: a snapshot must belong to the call job and appear in the list. Unknown request fields are rejected, and duplicate snapshot_ids return a 422 with snapshot_ids must not contain duplicates.
Query example request
API_KEY="$API_KEY"
curl -X POST "https://stage-agent-api.evergrovelabs.com/scheduler/call-jobs/query" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"call_job_id": "f15e9b8c-1a2d-4f3e-9c4b-7e8f1a2b3c4d",
"snapshot_ids": [
"2c9f0d1e-3b4a-4c5d-9e6f-1a2b3c4d5e6f",
"7d8e9f0a-1b2c-4d3e-8f9a-0b1c2d3e4f5a"
]
}'Query response (HTTP 200)
All attributes are returned as JSON.
Attribute | Type | Required | Description |
|---|---|---|---|
| object[] | Yes | One status item per matching snapshot. See Query result table below. Empty when no snapshots match. |
| UUID[] | Yes | Requested snapshot IDs that returned nothing: nonexistent, owned by another tenant, or not part of the supplied call job. Empty when no |
Query result
Each item in scheduled_requests:
Attribute | Type | Required | Description |
|---|---|---|---|
| UUID | Yes | The snapshot this status describes. |
| boolean | Yes | Whether this request has finished. True when |
| UUID | Yes | Identifies the individual outbound call request. |
| string | No | Echo of the submitted row id. |
| UUID | Yes | Underlying call input id. |
| UUID | Yes | Underlying call entity id. |
| string | Yes | Tenant the request belongs to. |
| OutboundCallStatus | Yes | Current status. See OutboundCallStatus enum. |
| string | No | Reason the request stopped, if terminal. Omitted when |
| datetime | No | Recommended callback time, or |
| object | No | Latest resolved outcome for this request. See Generated outcomes table. |
| object[] | No | Public attempt history. Omitted unless |
| datetime | Yes | When the request was created. |
| datetime | Yes | Last update timestamp. |
Results follow the order of the supplied snapshot_ids. When querying by call_job_id alone, results are ordered by entity batch creation, member order, and snapshot creation. Snapshot IDs that exist but are not accessible to your tenant, and IDs that do not exist at all, are both returned in missing_snapshot_ids rather than as errors.
Status derivation
Call jobs, entity batches, and scheduled request snapshots work in coordination. The call job status and completion_summary are derived from the entity batches inside it, and each scheduled request in the response is built from a frozen snapshot.
How the hierarchy works
When you poll a call job, the response is built from three coordinated layers:
Call job — the top-level batch. Its
statusandcompletion_summaryare derived from the entity batches it contains.Entity batch — one per physical call workflow. Requests that share the same
patient_name,outbound_phone_number, andprovider_zipare grouped into one batch. The batch has its own workflow and status.Scheduled request snapshot — a point-in-time copy of each request. While the entity batch is still
IN_PROGRESS, each query refreshes the snapshot to the request's current state; once the batch finishes, the snapshot freezes and stays stable. Snapshots are used so the API returns consistent results even while the underlying entity batch is running.
Call job status
The status field on the call job is derived from the entity batches inside it. It tells you whether the whole batch is still running or finished.
Value | Meaning |
|---|---|
| At least one entity batch is still active. |
| All entity batches finished successfully. |
| All entity batches finished, but at least one request has a terminal error. |
Scheduling behavior
By default, when trigger_now is omitted or false, submitted jobs enter Evergrove's managed scheduling. The platform resolves the request timezone from provider_zip, applies the provider office's working-hours policy, and places calls during the next appropriate window.
Set trigger_now: true to skip the business-hours window and dispatch immediately. This is suitable for end-to-end integration tests in staging and manual retries or one-off calls where timing is already confirmed.
Managed scheduling is recommended for production traffic. trigger_now: true will place calls outside normal business hours if used then, so it should be used deliberately.
Polling guidance
Poll the call job until the call job status is terminal. Terminal call job statuses are COMPLETE and COMPLETE_WITH_ERRORS. Non-terminal status is IN_PROGRESS.
Once the call job status is terminal, the call job will not change further. You can still inspect individual scheduled_requests to see which requests succeeded and which encountered errors.
Each scheduled request also has its own workflow_status with terminal statuses COMPLETE, ERROR, CANCELLED, and DO_NOT_CALL_BACK, and non-terminal statuses ENQUEUED and ACTIVE.
ZIP rule
provider_zip must be exactly 5 digits. ZIP+4 is not accepted.
External reference rule
external_reference_id is optional. If provided on any row, it must be provided on every row. It is echoed back on scheduled request results so clients can map API results back to their submitted rows.
Shared enums
CallJobDerivedStatus
Value | Meaning |
|---|---|
| At least one entity batch is still active. |
| All entity batches finished successfully. |
| All entity batches finished, but at least one request has a terminal error. |
OutboundCallStatus
Value | Meaning |
|---|---|
| Waiting to start. |
| Call workflow is running. |
| Finished successfully. |
| Encountered an unrecoverable error. |
| Cancelled before completion. |
| Marked as do-not-call. |
Common headers
Every request must include:
Authorization: Bearer {API_KEY}
Accept: application/json
Content-Type: application/json