{
"id": "ws_a1b2c3",
"name": "Tony's Test",
"plan": "pro",
"created_at": "2026-04-12T17:33:08Z",
"defaults": {
"timezone": "America/Edmonton",
"quiet_hours": { "start": "19:00", "end": "10:00" },
"quiet_days": ["sun"],
"max_attempts": 3,
"retry_triggers": {
"voicemail": { "enabled": true, "wait_minutes": 240 },
"no_answer": { "enabled": true, "wait_minutes": 60 },
"busy": { "enabled": true, "wait_minutes": 10 },
"technical_error": { "enabled": true, "wait_minutes": 30 },
"conversation_silent": { "enabled": true, "wait_minutes": 1440 }
}
},
"usage": {
"period_start": "2026-05-01T00:00:00Z",
"calls_this_period": 1247,
"cap": 10000,
"credits_balance_usd": 0
}
} The redialer.io API.
Read your callbacks, manage your agents, pull the activity feed, and integrate redialer.io with your own tooling. REST, versioned, JSON in / JSON out, bearer-token authenticated.
Three lines to your first call.
Once the API ships, the shape will be exactly this:
curl https://api.redialer.io/v1/callbacks?state=scheduled \ -H "Authorization: Bearer rdl_live_a1b2c3d4..." \ -H "Accept: application/json"
One key per workspace.
- Generate in Settings. Go to /app/settings · API keys and click Generate key. The key is shown once at creation. Copy it then.
- Bearer token in the header. Send every request with Authorization: Bearer rdl_live_…. Keys starting with rdl_test_ hit sandbox; rdl_live_ hit the production workspace they were generated in.
- Scoped per workspace. Each API key authorizes exactly one workspace. (Multi-workspace support is on the roadmap — for now, one workspace per account.)
- Rotate anytime. Settings exposes a Revoke action per key and a Generate key button to mint a new one. Revocation is immediate.
- Rotate a leaked webhook URL. Per-agent webhook URLs can be rotated from /app/setup/{agent_id}. The old URL keeps accepting events for a 24-hour grace window — or shorter, once we verify Retell is on the new URL — so in-flight calls finish cleanly. Dashboard-only on purpose; rotation isn't exposed via API to reduce the leaked-key blast radius.
Pagination, errors, rate limits.
Pagination
List endpoints return at most 200 items per page. Pass limit to request fewer. The response carries an opaque next_cursor string when more pages exist; pass it back as the cursor query parameter to continue.
{
"items": [...],
"next_cursor": "cb_a4f9c20a"
} Error envelope
Every non-2xx response carries a structured error body. HTTP status indicates the broad class; the code field is the machine-readable specific reason.
{
"error": {
"type": "invalid_request",
"code": "callback_not_found",
"message": "No callback exists with id cb_zzz.",
"details": null
}
} Common error codes:
- 401 unauthorized · missing or invalid bearer token
- 403 plan_forbidden · your plan doesn't include this endpoint (e.g. API access is Starter+)
- 404 not_found · resource doesn't exist or isn't in this workspace
- 422 invalid_state · resource is in a state that can't accept this operation (e.g. cancelling a callback that already dialed, or redialing a terminal row)
- 422 invalid_request · bad parameter, wrong shape, or business rule violation
- 429 rate_limited · per-key burst or sustained limit exceeded; check Retry-After header
- 500 internal_error · we screwed up; safe to retry with backoff
Rate limits
Each API key has a per-minute schedule rate cap (token bucket) on write endpoints. Read endpoints share a higher cap. Limits are per-workspace, not per-key. Plan ladder:
| Plan | Schedule (RPM) | Burst |
|---|---|---|
| Free | 5 | 10 |
| Starter | 30 | 60 |
| Pro | 150 | 300 |
| Scale | 600 | 1200 |
Free is not exposed to the API at all (Starter+ only).
Idempotency
Write endpoints accept an optional Idempotency-Key header (max 128 chars). Retries with the same key return the original result within a 24h window. Recommended on every POST.
Versioning
Version lives in the URL: api.redialer.io/v1/…. Breaking changes get a new subdomain path: api.redialer.io/v2/…. Additive fields are not breaking and may appear on v1 responses anytime.
Workspace.
Your workspace is one connected Retell account. The Workspace object exposes plan, current month usage, and the workspace defaults that flow into every agent.
Request body (any subset)
- name · string · workspace display name
- defaults.timezone · IANA zone
- defaults.quiet_hours · { start, end } as 24h HH:MM
- defaults.quiet_days · array of mon...sun
- defaults.max_attempts · integer, bounded by your plan
- defaults.retry_triggers.<outcome> · { enabled, wait_minutes }
Returns the usage sub-object of /v1/workspace without the rest of the workspace payload. Cheap to poll.
Agents.
Each agent is one Retell agent wired up to redialer.io. The Agent object carries the policy that controls retries, quiet hours, and the GUARD ZERO marker for that agent. It also carries an agentKind — voice for phone dial-backs, or sms_chat for outbound SMS (Retell create-sms-chat).
Query params: limit, cursor.
{
"agent_id": "agent_30095cadfc4e3c87a310167c24",
"name": "Test Callback",
"provider": "retell",
"workspace_id": "ws_a1b2c3",
"enabled": true,
"verification": {
"api_key": "ok",
"webhook": "ok",
"callback_boolean": "ok"
},
"policy": {
"inherit_from_workspace": true,
"timezone": null,
"quiet_hours": null,
"quiet_days": null,
"max_attempts": null,
"retry_triggers": null
},
"created_at": "2026-04-22T10:14:09Z"
} Set policy.inherit_from_workspace to false to enable overrides, then set any of timezone, quiet_hours, quiet_days, max_attempts, retry_triggers. Fields left null inherit from the workspace.
Returns 403 plan_forbidden on Starter and below.
Callbacks.
A Callback is a single scheduled outbound attempt — a phone dial-back for voice agents, or an outbound SMS for SMS agents. Callbacks belong to Chains: a chain is the sequence of attempts for one caller's request, attempt 1 through N.
Query parameters
- agent_id · scope to a single agent
- state · one of scheduled, claimed, calling, placed, failed, cancelled
- outcome · chain-wide; one of connected, voicemail, no_answer, busy, technical_error, conversation_silent
- from · ISO 8601; created_at ≥ from
- to · ISO 8601; created_at < to
- limit · integer 1-100, default 50
- cursor · opaque, from prior next_cursor
{
"items": [
{
"id": "cb_a4f9c20a",
"workspace_id": "ws_a1b2c3",
"agent_id": "agent_30095cadfc4e3c87a310167c24",
"to_number": null,
"state": "scheduled",
"run_at": "2026-05-24T17:00:00Z",
"created_at": "2026-05-23T14:02:11Z",
"chain": {
"id": "chain_xyz9",
"attempt": 2,
"max_attempts": 3,
"is_final_attempt": false
},
"last_outcome": "voicemail",
"attempt_outcomes": ["voicemail"]
}
],
"next_cursor": "cb_b71eaa"
} Phone numbers are redacted on this endpoint — to_number is always null in the list so a leaked callbacks:read key can't bulk-harvest numbers. Fetch the number for a single callback via /v1/callbacks/{id}/payload, which requires the callbacks:read_pii scope and is rate-limited + audited.
Returns the payload that was (or will be) sent to the provider on the outbound redial. PII fields (to_number, from_number, dynamic_variables, metadata) are decrypted via the tenant DEK; when the callback is terminal those fields are zeroed at markTerminal time and the response sets pii_redacted: true with those fields null. Cross-tenant rows return 404 (not 403) to avoid leaking existence.
Requires the callbacks:read_pii scope — callbacks:read alone is rejected with 403. Each call writes an audit-log entry (action: callback.payload_accessed) capturing the api_key id, and per-key requests are capped at 60 / minute; bursts beyond that return 429 with a Retry-After header.
Returns the full callback row minus the encrypted PII columns (numbers, dynamic vars, metadata). Use /v1/callbacks/{id}/payload for those — that endpoint unwraps the tenant DEK. Cross-tenant ids return 404, not 403, to avoid leaking existence.
Returns a chain summary (root id, chain_attempt sum, length, latest_state, max_attempts) plus an items array — every row that shares the same chain root, ordered by scheduled_at. The chain root is the first attempt; chain_root_id is null on that row and equals the root id on every subsequent attempt.
Returns 422 invalid_state if the callback is already in a terminal state (succeeded, failed_terminal, cancelled, expired) or already calling. The action is recorded in the audit log with the API key's creator as the actor.
Request body (required)
- reason · free-text, 3–500 chars; stored on the callback row and in the audit log
Returns 422 invalid_state if the callback has already dialed or is otherwise non-cancellable.
Activity feed.
The same denormalized stream the operator UI consumes. Every callback transition, every operator action, every inbound webhook we processed, in one chronological feed.
Query parameters
- type · one of callback, operator_action, webhook_inbound, webhook_outbound
- agent_id, from, to, limit, cursor
Audit log.
Every operator action that mutated state: REDIAL NOW, cancellations, and policy edits. Append-only.
Query parameters
- action · e.g. callback.redial_now, callback.cancel, agent.policy_update, callback.payload_accessed
- actor · user id, API key id, or system
- resource_type, resource_id · scope to one resource
- from, to, limit, cursor
Integrations.
An integration is one connected provider account (Retell today; Vapi and Bland on the way). Each carries an encrypted API key, a generated per-tenant webhook slug, and an enabled flag. Read-only on the public API — connecting accounts, rotating credentials, and rotating webhook URLs are dashboard-only actions so a leaked API key can't redirect a workspace's pipe.
Stats.
Daily totals series with a live in-flight count. Kept for dashboard back-compat. New integrations should use /v1/workspace/usage for the current-period counter and /v1/activity for granular history.
Outbound webhooks to your URL.
When you configure a forwarding URL on a workspace or agent, we POST every Retell payload there as we process it, plus a few of our own fields alongside (attempt number, chain context). Same structure Retell sent; your fields untouched.
Delivery
- HTTP POST to your configured URL. We retry 3 times with exponential backoff on 5xx and network errors.
- Signed. Header X-Redialer-Signature: t=<ts>,v1=<hmac> where v1 is HMAC-SHA256 of <ts>.<raw body> with your forwarding secret. Reject if t is older than 300s.
- Timeout 10s per attempt.
Event types
- call.inbound · forwarded as-is from Retell, with our chain context added under redialer_io
- call.analyzed · post-call analysis, forwarded as-is from Retell, with outcome classification added
- callback.scheduled · redialer-emitted; a callback was created in our system
- callback.placed · redialer-emitted; the outbound dial connected
- callback.failed · redialer-emitted; the chain closed without a connect
Something missing from this spec?
We are designing this surface before building it. If there's an endpoint or a field you'd need on day one, tell us before we ship v1. The shape locks at GA.