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:read | GET /api/v1/account |
numbers:read | GET /api/v1/numbers |
cdrs:read | GET /api/v1/cdrs |
messages:send | POST /api/v1/messages |
calls:originate | Outbound 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.