Developers
Everything you need to integrate with Dialer Digital: a REST control-plane API for campaigns, leads, CDR, compliance evidence, supervision, caller-ID/DIDs, promises, callbacks and billing, plus a WebSocket live-floor stream for push-based realtime.
The integration surface at a glance
| Surface | Use it for | Docs |
|---|---|---|
REST API (/v1) | Campaigns + lifecycle, lead import, CDR queries, typed dispositions, promises, callbacks, agents, stats, API-key rotation. | REST API |
| API Reference (generated) | The full endpoint reference, generated at build time from core's OpenAPI spec — plus client guides for auth, errors, webhooks and limits. | API Reference |
| Postman collection | 27 folders over /v1, importable — vendored from the integration harness and published with the local credential stripped. | Postman |
WebSocket live floor (/v1/ws) | Push delivery of agent presence, call lifecycle, pacing stats, and promise events — no polling. | WebSocket events |
Outbound webhooks (/v1/webhooks) | Server-to-server push of call, promise, SMS and recording facts to your own endpoint — signed, retried, deduplicated by delivery id. Built in core, not in a published release yet — confirm with your operator before building against it. | Outbound webhooks |
| Contracts repo | The dialer.v1 Protobuf schemas used across the platform's internal planes. | Contracts |
| Roadmap | Surfaces that are designed but not implemented yet (protojson reports, SSO/MFA, …). | Roadmap |
Base URL and environments
https://api.usa.dialerdigital.com # production (US region ingress)
http://127.0.0.1:4000 # local stack (dd/integration-tests)The API listens on port 4000 (API_PORT). Prometheus metrics are served separately on port 9568 (GET /metrics, no auth) and are never exposed through the public API ingress.
All examples in these docs use the local-stack seed data: tenant local-dev, campaign local-demo, agent extension 1000, and the seeded bearer token (localdev-m2-demo-token in the integration-tests stack).
Conventions
The API follows a small set of rules consistently:
JSON in, JSON out
Request and response bodies are JSON. Request bodies above 8 MiB are rejected. A wrong Content-Type on a body-carrying request returns 415 unsupported_media_type.
The error envelope
Every error response has the same shape:
{
"error": {
"code": "conflict",
"message": "campaign cannot transition from completed to running"
}
}code is a stable, machine-readable string; message is human-readable and may change. Branch on code, never on message. The full set of codes:
error.code | Typical HTTP | Meaning |
|---|---|---|
bad_request | 400 | Malformed JSON, bad query params, wrong field types (validated fail-closed). |
invalid | 400 | The record violates a domain invariant (changeset-level validation). |
unauthorized | 401 | Missing, unknown, or revoked API key. |
forbidden | 403 | The tenant is suspended. |
not_found | 404 | Resource missing for this tenant — cross-tenant ids land here too. |
conflict | 409 | Uniqueness or state-machine violation (duplicate campaign name, invalid lifecycle transition, seat busy). |
unprocessable | 422 | A typed contract violation (e.g. the promise-to-pay contract, callback rules, reserved rewind filters). |
unsupported_media_type | 415 | Non-JSON body. |
internal | 500 | Server-side failure; retry with backoff and report the X-Request-ID. |
Request IDs
Every response carries an X-Request-ID header. Send your own and it is propagated (when sane); otherwise one is assigned. Include it in support tickets — it links directly to structured logs.
Tenancy
Authentication is a per-tenant API key: the key is the tenant. The API never accepts a tenant id from path, query, or body — there is no X-Tenant-ID header. Cross-tenant ids always read as 404 not_found; resource existence is never leaked. Details in Authentication.
Timestamps
RFC 3339 / ISO-8601 in UTC: 2026-06-11T14:30:00Z. Times of day (calling windows) are HH:MM:SS. Query parameters that take time ranges (from, to) expect RFC 3339.
Dispositions are lowercase strings
Engine dispositions are lowercase snake_case strings (answered_human, abandoned, busy, no_answer, canceled, rejected, failed). Mapping to the dialer.v1 Protobuf enum naming (DISPOSITION_*) is a contracts-alignment follow-up — see Roadmap.
Rate limits
There are no per-key request quotas on /v1 today, but the dial and SMS pipelines apply rate brakes and monthly spend caps (429 throttled, 429 sms_spend_cap_exceeded) — see Rate limits & caps. Be a good citizen: page with keyset cursors instead of hammering large limit values, and prefer the WebSocket stream over polling.
Versioning
The REST API is versioned in the path: /v1/.... Backward-compatible additions (new fields, new endpoints) happen within v1; breaking changes require /v2. The Protobuf package dialer.v1 evolves only backward-compatibly — see Contracts.
A 60-second tour
TOKEN=localdev-m2-demo-token # local stack seed; production keys look like dd_...
API=http://127.0.0.1:4000
# 1. Liveness (no auth)
curl -s $API/healthz
# -> {"status":"ok","service":"dialer-core","version":"0.2.0"}
# 2. Who am I? (validates the key)
curl -s $API/v1/me -H "Authorization: Bearer $TOKEN"
# -> {"tenant":{"id":"...","name":"local-dev","status":"active","seats":5,...}}
# 3. List campaigns (the seed ships "local-demo")
curl -s $API/v1/campaigns -H "Authorization: Bearer $TOKEN"
# 4. Pull today's call attempts (append-only CDR, keyset-paginated)
curl -s "$API/v1/call_attempts?from=2026-06-11T00:00:00Z&limit=100" \
-H "Authorization: Bearer $TOKEN"Continue with Authentication.