Skills openclaw-phone
Use CallMyCall API to start, end, and check AI phone calls, and return results in chat. Use when the user asks to call someone, plan a future call, end a call, or fetch call results.
git clone https://github.com/openclaw/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/benjaminwaye/openclaw-phone" ~/.claude/skills/openclaw-skills-openclaw-phone && rm -rf "$T"
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/benjaminwaye/openclaw-phone" ~/.openclaw/skills/openclaw-skills-openclaw-phone && rm -rf "$T"
skills/benjaminwaye/openclaw-phone/SKILL.mdCallMyCall (OpenClaw Skill)
This skill helps you operate CallMyCall from chat. It is pull based (no webhook callbacks): you start a call, store the returned call ID in state, and later fetch status and results on demand.
API Key Resolution (OpenClaw best practice)
Resolve credentials in this order:
- Environment variable:
(preferred)CALLMYCALL_API_KEY - OpenClaw user config:
under~/.openclaw/openclaw.jsonskills.openclaw-phone.apiKey - If still missing, ask for a one-time key for the current task only.
- Only if the user explicitly asks for persistence, provide manual instructions for saving the key to
.~/.openclaw/openclaw.json
Persistence rules:
- Never store API keys in
, examples, references, or memory/state files.SKILL.md - Do not write API keys into
or any conversation-visible output. Do not tell the user “I won’t echo it back.”recent_calls - Use interactive keys for the current task only.
- Do not write user config files automatically from this skill.
How This Skill Should Work
- Resolve API key using the order in "API Key Resolution (OpenClaw best practice)".
- Collect required call info using a layered gating flow (below).
- Present a short review summary and ask for confirmation.
- On confirmation, call
.POST /v1/start-call - Store the returned
in state as a recent call.sid
Required Auth
Send the API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Never echo the API key back to the user or include it in logs/summaries.
Stateful Calls List (required)
Maintain a list (last 10) of recent calls in state:
: array of objectsrecent_calls
(call SID)idphone_numbertaskstarted_at
(optional, updated when you fetch)status
Use this list to let the user say "end call 1" or "show results for call 2".
Layered Gating Flow (copy from web app)
Do not rely on a single validation step. Use all layers below.
Layer 1: Structured collection contract
Do not finalize a task until all required fields exist:
phone_numberlanguage
(background + goals)call_brief
Layer 2: Task gap analysis
When the user gives the initial request, analyze what is missing. Then ask only for missing info. If the user answers partially, repeat analysis and keep asking for the remaining gaps.
Layer 3: Prompt level enforcement
While missing info exists, continue gathering required fields. Do not proceed to the call until all required fields are present.
Layer 4: Runtime validation before finalizing
Before sending the call request:
- Ensure phone exists and is E.164
- Block emergency or premium numbers
- Ensure
is not the same asfrom_numberphone_number - If
is requested, run caller-ID preflight:from_numberGET /v1/verified-caller-ids- Confirm requested
exists infrom_numberverified_caller_ids - If not verified: do not place call yet; ask user whether to continue with default caller ID or verify first
- Normalize
; normalize voice fields (language
,genderVoice
) only if providedopenaiVoice - If scheduling is present, parse and clamp to a valid time
Layer 5: Human review gate
Present a short review summary:
- Phone number
- Call brief (background + goals)
- Language (and voice if provided)
- Any schedule
Ask: "Confirm and place the call?" Do not proceed without explicit confirmation.
Workflows
Start a Call
- Collect required fields using the layered gating flow.
- If
is provided, run caller-ID preflight viafrom_number
.GET /v1/verified-caller-ids - If requested
is not verified, ask user to choose:from_number- continue now with default caller ID, or
- verify number first (
, thenPOST /v1/verify-caller-id
).GET /v1/verification-status/:verificationId
- If a schedule/time is requested, follow Scheduled Requests (No Cron) below instead of calling the API immediately.
- Otherwise call
.POST /v1/start-call - Store the returned
insid
.recent_calls - Reply with confirmation and the call ID.
Scheduled Requests (No Cron)
Because the API has no scheduling field:
- Collect all required fields now.
- Save a compact call plan in skill state only for in-session follow-up.
- Do not create or modify OS schedulers (
, launchd, task scheduler) and do not run autonomous background turns.cron - Offer one of these safe options:
- place the call now, or
- provide a reminder summary and ask the user to return at the target time to run
.start-call
If the user asks to schedule for later, explain that this skill does not create background jobs; it can prepare the call plan and execute when the user confirms in-session.
List Recent Calls
- Read
from state.recent_calls - For each call, fetch status via
if needed.GET /v1/calls/:callId - Display a numbered list.
Retry Until Answered (important)
When the user asks to call repeatedly until answered:
- Place one call with
.POST /v1/start-call - Poll
until terminal status.GET /v1/calls/:callId - Treat response as either flat (
,status
) or nested (duration
,call.status
).call.duration - If status is
,busy
,no-answer
, orfailed
, wait requested interval and place next call.canceled - Stop retry loop when:
- status is
, orin-progress - status is
withcompleted
.duration > 0
- status is
- Report each attempt (call ID + status) back to user.
Implementation note: keep one base URL per run (
https://call-my-call-backend.fly.dev preferred) and use it consistently for both start + status endpoints.
End a Call
If the user says "end the call" without specifying which, list recent calls and ask which one.
If there is only one active call, confirm and end it.
Call:
withPOST /v1/end-call
.{ callSid }
Get Results
When the user asks for call results:
- Fetch status via
.GET /v1/calls/:callId - If available, fetch transcript via
.GET /v1/calls/:callId/transcripts/stream - If the call was recorded, fetch recording URL via
.GET /v1/calls/:callSid/recording
Return:
- Status (completed, failed, canceled)
- Short summary (1 to 3 bullets)
- Transcript excerpt (first few lines, only after user asks to view transcript content)
- Recording URL (if present, warn that URL access may expose sensitive audio)
Safety and UX
- If user input is ambiguous, ask a clarification question.
- Never expose secrets or store API keys in transcript.
- Treat transcripts and recordings as sensitive; share only minimal excerpts requested by the user.
- Never create persistent scheduler entries or autonomous background execution from this skill.
- If a request fails, show the HTTP error and suggest next steps.
References
- Full API reference:
references/api.md - Examples:
examples/prompts.md