Skip to content
Developer API · v1

Build on your RingReady data

A simple REST API to read your call & web-chat activity and manage your AI receptionists — designed for your own scripts and for AI agents like Claude acting on your behalf.

Bearer-token auth · JSON · stable /api/v1

Quickstart

1. Create an API key in Profile → API keys. Copy it — the full key is shown only once.

2. Call the API with the key as a bearer token. This first request confirms your key works:

import requests

BASE = "https://www.ring-ready.com"
API_KEY = "rr_live_xxxxxxxx"          # from Profile → API keys
headers = {"Authorization": f"Bearer {API_KEY}"}

r = requests.get(f"{BASE}/api/v1/whoami", headers=headers)
print(r.json())
# {'user_id': 142, 'email': 'you@example.com', 'auth_method': 'api_key',
#  'api_key_prefix': 'rr_live_8f3a', 'scopes': ['read', 'write']}

Authentication

Every request is authenticated with an API key sent as a bearer token:

Authorization: Bearer rr_live_xxxxxxxx

Create and revoke keys in Profile → API keys. Each key only ever sees your own data. The full key is shown once at creation — we store only a hash, so if you lose it, revoke it and create a new one.

Scopes

Keys carry coarse scopes. Read endpoints need read; updating an agent needs write.

ScopeGrants
readList and read conversations and agents.
writeUpdate agent metadata (PATCH /agents/{id}).

Conventions

  • Base URL: https://www.ring-ready.com — all paths under /api/v1.
  • Format: JSON request and response bodies.
  • Timestamps: UTC, ISO-8601 with a trailing Z.
  • Scoping: every response is limited to the key owner's data.

Errors

Errors return {"detail": "..."} with an HTTP status:

StatusMeaning
401Missing, invalid, or revoked key.
403Valid key, but missing the required scope.
404Resource not found or not yours.
422Invalid request body (e.g. a blank required field).

Rate limits

None are enforced during the experimental phase. Per-key limits will be added before general availability.

Conversations

Phone calls, website chats, and inbound texts live in one place, told apart by channel (phone, web_chat, or sms). Rows include callback_number when the receptionist captured one (or recovered it from caller ID).

GET/api/v1/conversationsread

List conversations, newest first.

Query paramTypeNotes
channelstringphone, web_chat, or sms
agent_idintFilter to one receptionist
sincestringISO-8601; conversations at/after this time
before_idintPagination cursor (the previous page's next_before_id)
limitint1–200, default 50
r = requests.get(f"{BASE}/api/v1/conversations",
                 headers=headers,
                 params={"channel": "web_chat", "limit": 20})

data = r.json()
for c in data["conversations"]:
    print(c["started_at"], "·", c["label"], "·", c["summary"])

# next page:
if data["next_before_id"]:
    more = requests.get(f"{BASE}/api/v1/conversations", headers=headers,
                        params={"before_id": data["next_before_id"]}).json()

Response:

{
  "conversations": [
    {
      "id": 184, "channel": "web_chat", "agent_id": 7,
      "from": null, "to": null,
      "caller_name": "Sam", "label": "Demo Request",
      "summary": "Visitor asked for a demo and pricing.",
      "started_at": "2026-06-09T14:03:00Z",
      "ended_at": "2026-06-09T14:07:11Z",
      "duration_seconds": 251, "end_reason": "visitor_ended",
      "is_archived": false, "is_viewed": false,
      "web_chat": {"device_type": "desktop", "origin_url": "https://acme.com/pricing"}
    }
  ],
  "next_before_id": 184,
  "limit": 20
}
GET/api/v1/conversations/{id}read

A single conversation with its full transcript. Adds transcript (an array of {role, content, elapsed_s}), recording_url, caller_address, comments (the owner's notes), appointment_link_sent, and — for web chats — web_chat.visitor_ip.

convo = requests.get(f"{BASE}/api/v1/conversations/184", headers=headers).json()
for turn in convo["transcript"]:
    print(f"{turn['role']}: {turn['content']}")

Agents (receptionists)

GET/api/v1/agentsread

List your receptionists.

agents = requests.get(f"{BASE}/api/v1/agents", headers=headers).json()["agents"]
# [{"id": 7, "phone_number": "+15551234567", "agent_name": "Ava",
#   "business_name": "Acme Plumbing", "subscription_type": "growth",
#   "created_at": "2026-01-12T09:30:00Z"}]
GET/api/v1/agents/{id}read

Read one receptionist's full configuration. goal_source tells you which goal is active: "prompt" (free-text agent_goal) or "list" (the structured collect-list, shown read-only as agent_goal_list). The entitlements block reports, per plan-gated feature, whether it's configured, included in the plan, and therefore actually active — config saved on a lower plan stays dormant until an upgrade. CAPI secrets are write-only: reads return has_access_token / has_api_secret flags, never the values.

{
  "id": 7, "phone_number": "+15551234567", "subscription_type": "growth",
  "plan": {"subscription_type": "growth", "effective_tier": "growth"},
  "agent_name": "Ava", "voice": "friendly",
  "greeting_message": "Thanks for calling Acme — how can I help?",
  "goal_source": "prompt",
  "agent_goal": "Qualify leads and book consultations.",
  "agent_goal_list": null,
  "supported_languages": ["en", "es"],
  "business_name": "Acme Plumbing", "business_website": "https://acme.com",
  "business_services": "Emergency plumbing, water heaters, drain cleaning.",
  "business_address": "12 Main St, Denver, CO",
  "business_service_areas": "Denver metro",
  "business_hours": {"mon": [{"open": "08:00", "close": "17:00"}]},
  "timezone": "America/Denver",
  "faq": {"Do you offer emergency service?": "Yes, 24/7.",
          "Do you give free estimates?": "Yes, on every job."},
  "appointment_booking": {"sms_enabled": true, "sms_url": "https://book.acme.com", "sms_purpose": "booking"},
  "notifications": {"email": null, "calls_disabled": false, "sms_enabled": true,
                    "sms_number_masked": "***4567", "sms_number_verified": true},
  "recording_enabled": true,
  "capi": {"meta":   {"enabled": false, "pixel": null, "has_access_token": false, "events": []},
           "google": {"enabled": true, "pixel": {"measurement_id": "G-XXXX"},
                      "has_api_secret": true, "events": [{"event_name": "conv", "always": true}]},
           "tiktok": {"enabled": false, "pixel": null, "has_access_token": false, "events": []}},
  "features": {"web_chat_enabled": true, "live_context_enabled": false,
               "webhook_enabled": false, "webhook_url": null},
  "entitlements": {
    "call_recording": {"configured": true, "plan_includes": true, "active": true, "required_tier": "growth"},
    "capi_google":    {"configured": true, "plan_includes": false, "active": false, "required_tier": "scale"},
    "webhooks":       {"configured": false, "plan_includes": true, "active": false, "required_tier": "professional"}
  },
  "is_suspended": false, "created_at": "2026-01-12T09:30:00Z"
}
GET/api/v1/voicesread

The voice catalog. Each entry's key is what you send in PATCH {"voice": ...}; agent_name is the receptionist name that voice speaks with (the name always derives from the voice — it can't be set directly).

PATCH/api/v1/agents/{id}write

Partial update — only the fields you send change. Writable fields:

FieldMax lengthNotes
agent_goal10,000 charsRequired — can't be blanked
greeting_message500 charsOptional
business_name100 charsRequired — can't be blanked
business_services30,000 charsRequired — can't be blanked
business_website400 charsOptional
business_address512 charsOptional
business_service_areas1,024 charsOptional
business_hoursPer-day ranges object; null clears
timezoneIANA name, e.g. America/New_York
faq50 entries · question 200 · answer 500A {question: answer} map
goal_sourceprompt or list; list mode needs a dashboard-built collect-list
voiceA key from GET /voices; renames the receptionist (response returns the new agent_name)
agent_notification_email255 charsWhere summaries email; empty = account email. Triggers a security notice to the owner
webhook_enabled / webhook_urlURL 500 charsEnabling requires a URL. Triggers a security notice
recording_enabledBoolean. Active on Growth+; callers hear a consent notice
capi{meta|google|tiktok: {enabled, pixel, events}}. Omitted channels untouched; a pixel sent without its token keeps the stored token. Active on Scale

Configured ≠ active. Tier-gated config (recording, webhooks, CAPI) saves on any plan; the runtime only activates it when the plan includes the feature. Any PATCH touching one echoes an activation block — e.g. {"capi_google": {"configured": true, "plan_includes": false, "active": false, "required_tier": "scale"}} — so your integration can tell the customer an upgrade is needed rather than assuming it went live.

Goal modes. If goal_source is "list", the free-text goal is inactive and PATCH agent_goal returns 409. goal_source is writable: send {"goal_source": "prompt", "agent_goal": "..."} to switch a list-mode agent to prompt mode and set the goal in one request. Switching to "list" requires a collect-list already built in the dashboard (its content isn't editable via the API).

Still dashboard-only: the structured goal list, phone number & forwarding, the SMS notification number (verify-first), SMS booking, web chat, live context, plan & billing.

r = requests.patch(f"{BASE}/api/v1/agents/7",
                   headers=headers,
                   json={
                       "agent_goal": "Qualify the caller, then book a consultation.",
                       "greeting_message": "Thanks for calling Acme Plumbing — how can I help?",
                       "faq": {
                           "Do you offer free estimates?": "Yes — free, no-obligation estimates on every job.",
                           "What are your hours?": "We answer 24/7, including weekends and holidays.",
                           "Do you handle emergencies?": "Yes — we dispatch same-day for emergencies.",
                       },
                   })

print(r.json())
# {'id': 7, 'updated': ['agent_goal', 'greeting_message', 'faq']}

Full example: improve a receptionist from its conversations

The pattern this API is built for — read what callers actually asked, have an LLM find the gaps, and feed the answers back into the agent's FAQ:

import requests, json
from anthropic import Anthropic

BASE = "https://www.ring-ready.com"
RR = {"Authorization": "Bearer rr_live_xxxxxxxx"}
claude = Anthropic()
AGENT_ID = 7

# 1. Pull the last 50 conversations for this agent
convos = requests.get(f"{BASE}/api/v1/conversations", headers=RR,
                      params={"agent_id": AGENT_ID, "limit": 50}).json()["conversations"]

# 2. Fetch each transcript
transcripts = []
for c in convos:
    full = requests.get(f"{BASE}/api/v1/conversations/{c['id']}", headers=RR).json()
    transcripts.append(full["transcript"])

# 3. Ask Claude for recurring questions the agent answered poorly,
#    with a drafted FAQ answer for each
prompt = (
    f"Here are {len(transcripts)} receptionist transcripts. "
    "Find the 5 most common questions callers asked that the agent did NOT answer well. "
    'Return JSON: [{"question": ..., "answer": ...}].\n\n'
    + json.dumps(transcripts)[:50000]
)
reply = claude.messages.create(
    model="claude-sonnet-4-6", max_tokens=1500,
    messages=[{"role": "user", "content": prompt}],
)
additions = json.loads(reply.content[0].text)

# 4. Merge into the agent's FAQ and write it back
agent = requests.get(f"{BASE}/api/v1/agents/{AGENT_ID}", headers=RR).json()
faq = agent.get("faq") or {}
for item in additions:
    faq[item["question"]] = item["answer"]

requests.patch(f"{BASE}/api/v1/agents/{AGENT_ID}", headers=RR, json={"faq": faq})
print(f"Added {len(additions)} answers to the FAQ.")

Run it on a schedule and your receptionist keeps getting better at the questions it actually gets.

Prefer to just talk to Claude? The same loop with no script to deploy — paste one prompt into Claude Code or Codex and let it read your calls and propose the changes, across every client. See the conversational walkthrough →

For AI agents & tools

Coding agents (Claude Code, Codex, Gemini CLI) — start here: point your agent at ring-ready.com/agent.md. It's a purpose-built operator's guide — the workflow, the editable-vs-locked fields, the secure-key & skill best practices, and a link to the spec below. See it in action →

Machine-readable spec: the whole API is described in OpenAPI at /api/v1/openapi.json — import it as tools in the Anthropic or OpenAI SDKs, a ChatGPT custom GPT (Actions), LangChain or n8n, or generate an MCP server from it.

Site overview for LLMs: llms.txt.

Hand an agent a key and the spec, and it can read conversations and tune receptionists on its own. Because every key is scoped to one account, an agent only ever touches that customer's data.

Ready to build?

Create a key and make your first call in two minutes.