Developer API

DialRingo API v1

A REST API for your account data: profile, phone numbers, and call history, plus outbound SMS. Authenticate with an API key from your dashboard and integrate in minutes.

Overview

v1 covers three reads - your account profile, your phone numbers, and your call history - and one write: sending an outbound SMS from a number you own. All endpoints live under https://app.dialringo.com/api/v1, return JSON, and are scoped to the account that owns the API key.

Talking to a coding agent instead?

The same API key drops straight into Claude Code, Claude Desktop, Codex or OpenClaw through the DialRingo MCP server, one line of config and no build step. It covers these endpoints plus one thing this REST surface cannot express on its own: place a real call and poll it to a real outcome.

Create an API key

In the dashboard, open Developer in the sidebar (app.dialringo.com/dashboard/developer - owners and admins only). Name the key, pick its scopes, and select Create key. The full secret - it starts with dr_live_ - is shown exactly once, at creation. Copy it immediately; afterwards only the key's prefix is visible, and we store only a SHA-256 hash of the secret. Keys can be revoked from the same page at any time.

Authentication

Send the key as a bearer token on every request:

curl https://app.dialringo.com/api/v1/account \
  -H "Authorization: Bearer dr_live_..."

Requests without a key get 401 {"error": "missing_api_key"}; unknown or revoked keys get 401 {"error": "invalid_api_key"}.

Scopes

Each key carries the scopes you chose at creation (the default is account:read + numbers:read). Calling an endpoint without its scope returns 403 {"error": "insufficient_scope", "required": "<scope>"}. POST /api/v1/messages requires messages:send, which is not part of the default set: sending costs money, so a key can only send if you tick Send messages when you create it. Keys issued before this change were granted messages:send automatically, so existing integrations keep working with no action from you.

calls:originate works the same way: it lets a key ask your SMART Voice assistant to place an outbound call, which costs money per call, so you have to tick Place calls at creation. No existing key was granted it, and it is only usable on accounts with SMART Voice enabled, which is in early access. Talk to us if you want it switched on.

Scope Grants
account:readGET /api/v1/account
numbers:readGET /api/v1/numbers
cdrs:readGET /api/v1/cdrs
messages:sendPOST /api/v1/messages
calls:originateOutbound calls placed by a SMART Voice assistant (early access)

GET/api/v1/account

Your account profile. Requires account:read.

curl https://app.dialringo.com/api/v1/account \
  -H "Authorization: Bearer dr_live_..."
{
  "data": {
    "id": "0b0f0f5e-8f5f-4a2e-9df3-1c7a4a3f2b10",
    "name": "Riverside Dental",
    "customer_ref": "DRC-000042",
    "billing_status": "subscribed"
  }
}

customer_ref is your human-readable customer number (DRC-…). billing_status reflects the billing state, for example subscribed, no_payment_method, or past_due.

GET/api/v1/numbers

All phone numbers on the account, newest first. Requires numbers:read.

curl https://app.dialringo.com/api/v1/numbers \
  -H "Authorization: Bearer dr_live_..."
{
  "data": [
    {
      "id": "7c9a1d34-2b8e-4f10-a5cd-90e3b2f4a611",
      "phone_number": "+15025550114",
      "status": "active",
      "type": "local",
      "sms_enabled": true,
      "friendly_name": "Main line",
      "created_at": "2026-06-18T14:03:22.51427+00:00"
    }
  ]
}

status is active, porting, or released. type is local or toll_free.

GET/api/v1/cdrs

Call history across all of the account's numbers, newest first. Requires cdrs:read.

Query parameter Meaning
limit Number of records to return. Default 50, minimum 1, maximum 200.
curl "https://app.dialringo.com/api/v1/cdrs?limit=2" \
  -H "Authorization: Bearer dr_live_..."
{
  "data": [
    {
      "id": "e2f6c3a1-5d47-49b2-8c15-3fb9d0a7e422",
      "direction": "inbound",
      "from_number": "+15025550114",
      "to_number": "+15025550190",
      "disposition": "voicemail",
      "duration_seconds": 42,
      "started_at": "2026-07-11T18:22:05.113+00:00"
    },
    {
      "id": "a91b7d02-64c8-4e3f-b0aa-57c21e8f9d33",
      "direction": "outbound",
      "from_number": "+15025550190",
      "to_number": "+15025550171",
      "disposition": null,
      "duration_seconds": 187,
      "started_at": "2026-07-11T17:40:11.902+00:00"
    }
  ]
}

direction is inbound or outbound. disposition is the post-call tag set by the agent (for example voicemail, no_answer, follow_up) or null when none was recorded.

POST/api/v1/messages

Send an outbound SMS from one of the account's numbers. Requires messages:send; a key without it gets 403 {"error": "insufficient_scope", "required": "messages:send"}. Keys created before the scope existed were granted it automatically. The from number must be active, owned by the account, and SMS-enabled. Text only; MMS is not part of v1.

Body field Type Meaning
to string, required Recipient in E.164, for example +15025550123.
from string, required One of your numbers in E.164. Must be active and have sms_enabled: true in GET /api/v1/numbers.
body string, required Message text. Non-empty, 1600 characters maximum.
curl -X POST https://app.dialringo.com/api/v1/messages \
  -H "Authorization: Bearer dr_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+15025550123",
    "from": "+15025550114",
    "body": "Your appointment is confirmed for Thursday at 2:15pm. Reply STOP to opt out."
  }'
{
  "data": {
    "id": "4d6b8e17-3c02-45a9-9f6e-8b1d20c7a934",
    "status": "sent"
  }
}

id is the message record on your account. A 200 means the message was accepted by the carrier; delivery receipts are not exposed in v1. If the carrier rejects the send, the message is recorded as failed and the call returns 502.

Errors specific to this endpoint:

Status Body Meaning
400{"error": "invalid_to"}to is missing or not E.164.
400{"error": "invalid_from"}from is missing or not E.164.
400{"error": "empty_body"}body is missing or blank.
400{"error": "body_too_long", "max": 1600}body is over 1600 characters.
402{"error": "payment_required", "reason": "..."}Paid features are off for the account. reason is trial_expired_no_card or no_active_subscription. Add a payment method to resume sending.
403{"error": "insufficient_scope", "required": "messages:send"}The key was created without the messages:send scope. Create a new key with that scope ticked.
404{"error": "number_not_found_on_account"}No active number matching from on this account.
409{"error": "sms_not_enabled_on_number"}The from number is not SMS-enabled.
409{"error": "remote_opted_out"}The recipient replied STOP on this account. Opt-outs are honored and cannot be overridden by API.
502{"error": "send_failed", "code": "...", "message": "..."}The carrier rejected the send. The message is stored as failed with the same code.
503{"error": "insert_failed"}The message could not be recorded; nothing was sent. Retry.

Errors

Errors are JSON with an error field, and some carry extra fields alongside it. These apply across the API:

Status Body Meaning
401{"error": "missing_api_key"}No Authorization: Bearer header.
401{"error": "invalid_api_key"}Key unknown, malformed, or revoked.
403{"error": "insufficient_scope", "required": "..."}Key lacks the endpoint's scope.
404{"error": "not_found"}Resource does not exist.
503{"error": "db_unavailable"}Transient backend issue; retry with backoff.

Good citizenship

  • Treat keys like passwords: server-side only, never in client code or repos.
  • Create one key per integration so you can revoke them independently.
  • The dashboard shows each key's last-used time, so a leaked or stale key is easy to spot and revoke.
  • Any live key can send SMS, so a leaked key can spend money on your account. Revoke first, investigate second.
  • Honor STOP. We block sends to anyone who has opted out, but keep your own suppression list in sync so you are not relying on a 409 to catch it.

Questions or a use case v1 doesn't cover? Email support@dialringo.com.