Skip to content

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

SurfaceUse it forDocs
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 collection27 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 repoThe dialer.v1 Protobuf schemas used across the platform's internal planes.Contracts
RoadmapSurfaces 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:

json
{
  "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.codeTypical HTTPMeaning
bad_request400Malformed JSON, bad query params, wrong field types (validated fail-closed).
invalid400The record violates a domain invariant (changeset-level validation).
unauthorized401Missing, unknown, or revoked API key.
forbidden403The tenant is suspended.
not_found404Resource missing for this tenant — cross-tenant ids land here too.
conflict409Uniqueness or state-machine violation (duplicate campaign name, invalid lifecycle transition, seat busy).
unprocessable422A typed contract violation (e.g. the promise-to-pay contract, callback rules, reserved rewind filters).
unsupported_media_type415Non-JSON body.
internal500Server-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

bash
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.

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.