---
name: sarj
description: Integrate Sarj.ai outbound voice calls, scheduled retries, call results, and webhooks through the public API, Python SDK, or action MCP server.
---

# Sarj.ai voice calls

Read the [quickstart](https://platform-docs.sarj.ai/getting-started.md) for a first call, the [Python guide](https://platform-docs.sarj.ai/python-sdk.md) for tested client examples, and the [OpenAPI contract](https://platform-docs.sarj.ai/api-reference/openapi.json) for exact fields and errors.

## Select the interface

- REST base: `https://platform-api.sarj.ai/api/v1`; send `Authorization: Bearer $SARJ_API_KEY`.
- Python: `sarj-platform-sdk`, imported as `sarj_platform_sdk`. Check release availability before using scheduling methods; the guide names the required version.
- Call-action MCP: `https://platform-api.sarj.ai/api/v1/mcp`, authenticated through Sarj sign-in. Use it when the user asks to perform call operations.
- Documentation MCP: `https://platform-docs.sarj.ai/mcp`, public and read-only. Use it to find documentation; it cannot place calls.

For standalone speech, read [STT](https://platform-docs.sarj.ai/speech-to-text.md) or [TTS](https://platform-docs.sarj.ai/text-to-speech.md). Those services have separate base URLs and credentials; the platform SDK is the voice-call client.

## Place and inspect a call

Obtain an active scenario's UUID from the dashboard and an intended recipient in E.164 format. Do not invent a scenario ID or treat its display name as the identifier. `POST /calls` returns `202 Accepted`; store `data.id` and inspect it with `GET /calls/{call_id}`. A public `GET /health` proves reachability, not credential validity.

Call status and webhook `payload.type` are different contracts. Read the current enum/schema instead of assuming values such as `no_answer` are call statuses.

Reports are asynchronous. Only `completed` and `max_duration_reached` calls are eligible; other terminal states keep `report` null. Even eligible calls can fail report generation. Bound polling by elapsed time and handle a missing report. Use [webhooks](https://platform-docs.sarj.ai/webhooks.md) for push delivery. Prefer `permanent_recording_url`, with `recording_url` as the documented rollout fallback.

## Schedule and retry

Calculate `scheduled_at` at request time with a timezone-aware datetime at least 10 minutes and at most 30 days ahead. Cancel or reschedule only while the call is pending; once released for dialing those operations return `409 call_not_pending`.

Create a retry policy with `POST /schedule-configs`, then pass its returned ID as `schedule_config_id`. The timezone belongs beside `retry_window`, not inside it:

```json
{
  "max_retries": 2,
  "wait_between": "PT2H",
  "retry_window": {
    "start_edge": { "hour": 9, "minute": 0 },
    "end_edge": { "hour": 17, "minute": 0 }
  },
  "timezone": "Asia/Riyadh",
  "expire_after": "P3D"
}
```

Two retries permit up to three dials, after eligible unsuccessful outcomes. Retry-group expiry is one hour to seven days. Each attempt has its own call ID; `root_call_id` links retries, and `next_retry_at: null` means no next attempt is planned. Scheduling can return `scheduling_disabled` when unavailable on the deployment.

## Recover without duplicate calls

Branch on `error.type` and retain `meta.request_id`. SDK HTTP retries are disabled by default and are separate from scheduled dial retries. An immediate create-call request has no documented idempotency guarantee: after a timeout or connection loss, reconcile the outcome before resubmitting. Do not apply a blanket 5xx retry policy to call creation.

Follow the webhook guide's current acknowledgment deadline, authentication options, and deduplication rules. Never invent HMAC verification support or treat a failed delivery as proof that the call failed.
