Ring-a-Ding docs

Use Ring-a-Ding without guessing.

This page is only about using the product well: setup, how to frame calls, how the MCP and CLI flow works, what results come back, and how to recover when something fails. It is not a full app architecture reference.

The most important rule: have your agent think through the whole call before dialing. The more relevant context, personality, and prompt guidance the phone agent gets, the better it performs.

Start here

Pick the surface that matches how you actually work. OpenClaw is the shortest path. MCP is for general agent clients. The CLI is for direct local use.

OpenClaw quick-start

Best when OpenClaw is already your agent runtime.

  • Install the CLI and the ring-a-ding skill.
  • Add your Ring-a-Ding key and OPENAI_API_KEY.
  • Start a fresh OpenClaw session before the first call.

Generic MCP client setup

Best for Claude Desktop, Claude Code, Cursor, or any MCP client.

  • Add a ring-a-ding MCP server entry.
  • Set RAD_API_KEY, RAD_API_URL, and OPENAI_API_KEY.
  • Call make_call, then follow with wait_for_call.

CLI or scripts

Best for shell workflows, local automation, and manual debugging.

  • Run rad init to save keys locally.
  • Start calls with rad call.
  • Poll with rad status or block with rad wait.

Call quality

Most bad calls come from thin instructions. The agent should reason through the call before it invokes the tool.

  • Think through the whole call before dialing: Decide who should be called, what success looks like, what is off-limits, and what fallback behavior is acceptable if the first path fails.
  • Make the purpose do real work: Purpose is not a title. It should say who to call, what needs to happen, and the exact questions or outcomes that matter. The minimum is 20 characters; strong calls usually need much more.
  • Use context for facts, not vibes: Put names, dates, order numbers, budgets, addresses, and scheduling constraints in context so the phone agent can reference them naturally without exposing them unless relevant.
  • Set personality intentionally: Personality changes delivery style: warmth, pacing, formality, patience, firmness. It is the easiest way to stop a call from sounding generic.
  • Control the opening when first impressions matter: If the first sentence has to land well, write opening_line or openingLine yourself instead of relying on the generated greeting.
  • Describe every structured field you want back: When you use output_schema or outputSchema, add a description to each property. The more context the phone agent has, the better it performs, and field descriptions are part of that context.

Thin request

Call the dentist and see what they say.

Better request

{
  "to": "+15551234567",
  "purpose": "Call Sunrise Dental to schedule a cleaning for Taylor next week. Ask about Tuesday or Wednesday morning, confirm Blue Cross PPO pricing, and do not book anything unless the total cost is under $200.",
  "context": "Patient: Taylor Kim. Insurance: Blue Cross PPO. Preferred days: Tuesday or Wednesday morning. Budget: under $200. If they only have afternoon openings, ask for the earliest one.",
  "personality": "Warm, calm, and professional. Sound like a real patient coordinator, not a scripted bot. Stay patient if put on hold.",
  "caller_name": "Taylor",
  "opening_line": "Hi, my name is Taylor. I'm calling to schedule a teeth cleaning for next week.",
  "output_schema": {
    "type": "object",
    "properties": {
      "appointment_date": {
        "type": "string",
        "description": "Confirmed appointment date and time"
      },
      "total_cost": {
        "type": "number",
        "description": "Total quoted price in dollars, including fees"
      },
      "accepts_insurance": {
        "type": "boolean",
        "description": "Whether Blue Cross PPO is accepted for this visit"
      }
    }
  }
}

outputSchema example

If you want structured data back, describe every field clearly so the phone agent knows what to collect.

{
  "type": "object",
  "properties": {
    "appointment_date": {
      "type": "string",
      "description": "Confirmed appointment date and time"
    },
    "total_cost": {
      "type": "number",
      "description": "Total quoted price in dollars, including fees"
    },
    "accepts_insurance": {
      "type": "boolean",
      "description": "Whether Blue Cross PPO is accepted for this visit"
    }
  }
}

Setup

Every working call needs a Ring-a-Ding key and an OpenAI key. After that, wire up OpenClaw or the CLI.

Ring-a-Ding API key

Authenticates Ring-a-Ding and gives the platform permission to place calls for your account.

  • Issued from the hosted account and checkout flow.
  • Typically starts with rad_live_.
  • Rotate it from the account page if an agent loses access.

OpenAI API key

Powers the live voice model during the call. Ring-a-Ding does not replace your OpenAI account.

  • Starts with sk-.
  • Required for MCP and CLI calls.
  • Stored in OpenClaw config or local CLI setup.

OpenClaw install

npm install -g ring-a-ding-cli
openclaw skills install ring-a-ding
openclaw skills check

OpenClaw config

{
  "skills": {
    "entries": {
      "ring-a-ding": {
        "enabled": true,
        "apiKey": "rad_live_...",
        "env": {
          "OPENAI_API_KEY": "sk-..."
        }
      }
    }
  }
}

Generic MCP config

{
  "mcpServers": {
    "ring-a-ding": {
      "command": "npx",
      "args": [
        "-y",
        "ring-a-ding-mcp"
      ],
      "env": {
        "RAD_API_KEY": "rad_live_...",
        "RAD_API_URL": "https://api.ringading.ai",
        "OPENAI_API_KEY": "sk-..."
      }
    }
  }
}

CLI setup

rad init
rad init --api-key rad_live_... --openai-key sk-...

Smoke test

Start a fresh OpenClaw session after installation, then try a real phone task.

Use the ring-a-ding skill to call the best pizza place around me and order delivery.

MCP tools

The MCP server name is ring-a-ding. In most agent flows: make_call starts the call, wait_for_call finishes it, and get_call_result is the non-blocking fallback.

make_call

Starts the outbound call and returns a callId immediately. Use it when the task really requires a live phone conversation.

Best for: Beginning the call once your agent has already reasoned through the objective, supporting facts, tone, and desired structured output.

to, purpose, context?, personality?, caller_name?, output_schema?

After make_call, call wait_for_call. Do not treat callId alone as the result.

  • MCP parameter names use snake_case such as caller_name, opening_line, and output_schema.
  • Detailed purpose, context, personality, and opening_line materially improve call quality.
make_call({
  "to": "+15551234567",
  "purpose": "Call Sunrise Dental to ask about Tuesday morning cleaning availability next week.",
  "context": "Patient: Taylor Kim. Insurance: Blue Cross PPO.",
  "personality": "Warm and professional."
})

wait_for_call

Blocks until the call reaches a terminal state or the timeout is reached. This is the default follow-up after make_call.

Best for: Getting the final transcript, summary, extracted data, and metadata in the same workflow without writing your own polling loop.

call_id, timeout_seconds?

  • Default timeout is 660 seconds; max timeout is 1860 seconds.
  • Polls internally every 5 seconds.
wait_for_call({
  "call_id": "<call_id>",
  "timeout_seconds": 660
})

get_call_result

Returns the current call record immediately, including any transcript, summary, extracted data, and metadata available so far.

Best for: Non-blocking workflows where your agent wants to continue other work and check the call later.

call_id

  • If the status is initiated, ringing, or in-progress, the call is still running.
  • Use this for manual polling when blocking is not desirable.
get_call_result({
  "call_id": "<call_id>"
})

CLI

The CLI is the straightforward path when you want local control. Start the call, save callId, then poll or wait for the final result.

Install the CLI and OpenClaw skill

Public install path for OpenClaw users.

npm install -g ring-a-ding-cli
openclaw skills install ring-a-ding
openclaw skills check
  • Start a fresh OpenClaw session after the skill is installed or updated.
  • If OpenClaw uses a shell allowlist, permit rad.

Save keys locally with rad init

Use this for CLI-only workflows or local debugging outside OpenClaw.

rad init
rad init --api-key rad_live_... --openai-key sk-...
  • Config is stored in ~/.config/ring-a-ding/config.json.
  • The CLI validates the Ring-a-Ding key when possible and can detect an OpenClaw install.

Start a basic call

Shortest valid CLI example.

rad call "+15551234567" "Call Sunrise Dental to ask about Tuesday or Wednesday morning cleaning availability next week."

Start a structured call from stdin

Best when the request is long or needs outputSchema.

echo '{"to":"+15551234567","purpose":"Call Northside Pizza and ask whether they deliver downtown tonight.","context":"Customer: Taylor. Address: 200 Market St. Ask for delivery estimate if yes.","personality":"Warm and concise","outputSchema":{"type":"object","properties":{"delivers":{"type":"boolean","description":"Whether the restaurant delivers downtown tonight"},"delivery_window":{"type":"string","description":"Quoted delivery estimate"}}}}' | rad call --stdin
  • CLI stdin uses camelCase keys such as callerName and outputSchema.

Poll a call without blocking

Fetches the latest call record.

rad status <call_id>

Block until the call finishes

Use only when blocking the current shell session is acceptable.

rad wait <call_id> --timeout 300
  • Exit code 2 means the wait timed out before the call reached a terminal state.

End or cancel a call

Hangs up an active call or cancels one that has not connected yet.

rad end <call_id>

Print the bundled skill file

Useful when debugging or manually copying the OpenClaw skill.

rad skill

Call inputs

A small number of fields drive most of the behavior. MCP uses snake_case. CLI flags use kebab-case. CLI stdin uses camelCase.

Destination

to, <to>

Phone number to dial.

Use E.164 format with country code, for example +15551234567.

Required

Purpose

purpose

What the call should accomplish.

Include who is being called, what outcome you need, and any must-ask questions. Minimum 20 characters; detailed purposes consistently produce better calls.

Required

Context

context, --context

Supporting facts the phone agent may need during the call.

Put names, dates, budgets, addresses, account numbers, and constraints here. Keep tone instructions in personality instead.

Personality

personality, --personality

How the agent should sound and behave.

Use it to control warmth, pacing, formality, patience, or firmness. This changes delivery style, not the task itself.

Caller name

caller_name, callerName, --caller-name

The name used when the agent introduces itself.

Set it explicitly when identity matters. Otherwise a random caller name is chosen.

Opening line

opening_line, openingLine, --opening-line

Exact first sentence when someone answers.

Use this when the first impression matters or when the wording should be tightly controlled.

Output schema

output_schema, outputSchema, --output-schema

JSON Schema for structured data extraction after the call ends.

Add a description to every property so the agent knows what information to collect during the call.

Voice

voice, --voice

Which OpenAI voice is used during the call.

Start with marin. Try ash or cedar for warmer calls and echo for calmer ones.

Default: marin

Voicemail behavior

voicemail_action, voicemailAction, --voicemail-action

What to do if voicemail or an automated system answers.

leave_message is the default. Use hang_up when voicemail should be silent. Shared numbers cannot receive callbacks.

Default: leave_message

Max duration

max_duration_minutes, maxDurationMinutes, --max-duration

Maximum call length in minutes.

Set a lower cap for quick tasks and a higher cap for detailed multi-step conversations.

Default: 10 minutes, with a hard max of 30

Voices

Start with marin. Available voices: alloy, ash, ballad, cedar, coral, echo, marin, sage, shimmer, verse.

Results

A good workflow is simple: start the call, keep callId, then read the final record instead of assuming success.

  1. Start the call and store callId immediately.
  2. Use wait_for_call or rad wait when blocking is fine. Use get_call_result or rad status when it is not.
  3. Read the final record before treating the task as done.

Example final record

{
  "callId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "summary": "Sunrise Dental offered Tuesday at 10:00 AM next week for $95 with Blue Cross PPO and can hold the slot until tomorrow.",
  "extractedData": {
    "appointment_date": "Tuesday at 10:00 AM",
    "total_cost": 95,
    "accepts_insurance": true
  },
  "transcript": [
    {
      "role": "assistant",
      "content": "Hi, my name is Taylor. I'm calling to schedule a teeth cleaning for next week.",
      "timestamp": "2026-04-14T10:30:05Z"
    },
    {
      "role": "user",
      "content": "We have Tuesday at 10 available. With Blue Cross PPO the visit is usually around $95.",
      "timestamp": "2026-04-14T10:30:18Z"
    }
  ],
  "metadata": {
    "voiceUsed": "marin",
    "callerNameUsed": "Taylor",
    "openaiModel": "gpt-realtime-1.5",
    "totalTokensUsed": null,
    "callEndedBy": "assistant",
    "numberUsed": "+15555551234",
    "transcriptCaptured": true
  },
  "costCents": 84,
  "startedAt": "2026-04-14T10:30:00Z",
  "endedAt": "2026-04-14T10:33:04Z",
  "errorMessage": null
}

callId and status

callId is the stable identifier for later polling. status tells you whether the call is still running or has reached a terminal outcome.

summary

Short human-readable outcome. Use it for quick inspection, but fall back to transcript and extractedData when precision matters.

transcript

Ordered conversation turns with role and timestamp. This is the source of truth when you need to inspect exactly what happened.

extractedData

Structured JSON populated when outputSchema was provided and the conversation covered the requested data.

metadata

Includes voiceUsed, callerNameUsed, openaiModel, numberUsed, callEndedBy, and transcriptCaptured.

timing, cost, and errorMessage

Use startedAt, endedAt, durationSeconds, and costCents for observability. errorMessage explains terminal failures when one is available.

Status meanings

  • initiated (non-terminal): The call record exists and the outbound call flow has started.
  • ringing (non-terminal): The destination is ringing but the conversation has not started.
  • in-progress (non-terminal): The call connected and the live voice session is running.
  • completed (terminal): The conversation finished normally.
  • failed (terminal): The platform or provider failed before the task could complete.
  • no-answer (terminal): Nobody picked up the call.
  • busy (terminal): The destination line reported busy.
  • canceled (terminal): The call was canceled before normal completion.

Limits

These are the defaults and constraints that matter when you plan calls or build workflows around them.

  • Bring your own OpenAI key: Ring-a-Ding handles telephony, but the live voice model runs on your OpenAI account.
  • Current duration limits: Calls can run from 1 to 30 minutes. The default max duration is 10 minutes.
  • Current wait behavior: wait_for_call and rad wait default to 660 seconds, cap at 1860 seconds, and poll every 5 seconds.
  • Shared outbound numbers: Calls go out from the managed number pool. Treat those numbers as outbound-only and do not ask for callbacks.
  • Current usage caps: The shared constants declare 5 concurrent calls and 50 calls per day as the current operating limits.
  • Default voice: If you do not choose a voice, the platform defaults to marin.

Troubleshooting

Most failures come from missing keys, stale sessions, bad numbers, or prompts that do not tell the agent enough.

rad is not found after install

openclaw skills check or which rad cannot find the binary after npm install -g ring-a-ding-cli.

  • Open a fresh terminal and run which rad again.
  • Confirm the global npm bin path is on PATH.
  • Reinstall with npm install -g ring-a-ding-cli if the binary is still missing.

OpenClaw is not using the skill

The CLI is installed, but OpenClaw does not pick Ring-a-Ding for phone tasks.

  • Run openclaw skills check and confirm ring-a-ding is enabled.
  • Restart OpenClaw after installing the skill or changing its config.
  • If OpenClaw uses a shell allowlist, permit rad.

RAD_API_KEY or OPENAI_API_KEY is missing

Calls fail before the conversation can start.

  • Confirm the Ring-a-Ding key is present in OpenClaw config or local CLI config.
  • Confirm OPENAI_API_KEY is present for the skill or saved locally through rad init.
  • If the key was rotated, update every place where the old value is still cached.

The destination number is rejected

The API or CLI reports INVALID_INPUT for the phone number.

  • Use E.164 format only, for example +19195551234.
  • Do not include spaces, parentheses, or local-only formatting.
  • If the number is generated dynamically, log the final string before sending it.

The call prompt is too short or too vague

The request is rejected or the call sounds generic and misses obvious questions.

  • Make the purpose explicit about who to call and what outcome you need.
  • Move supporting facts into context instead of keeping the request vague.
  • Add personality, opening line, and output schema when the task needs more control.

The call stays initiated, ringing, or in-progress

The call appears to be running longer than expected.

  • Poll again after 30 to 60 seconds with rad status or get_call_result.
  • If the call is clearly stale, end it with rad end.
  • Check whether the destination actually answered or whether the line is still ringing.

The call ends as failed, busy, or no-answer

The task was not completed even though the call reached a terminal state.

  • Read errorMessage, summary, and transcript if they are available.
  • Confirm the number, timing, and business-hours assumptions before retrying.
  • Adjust the prompt or target rather than blindly running the same call again.

Transcript, summary, or extracted data is missing

The call finished but some post-call data is null or incomplete.

  • Fetch the call record again after a short delay in case post-call processing is still finishing.
  • Check whether the call actually connected. no-answer and early failures do not produce the same outputs.
  • If you expected structured data, confirm that outputSchema was supplied on the original request.