CallMyCall (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:
- 1. Environment variable:
CALLMYCALL_API_KEY (preferred) - OpenClaw user config:
~/.openclaw/openclaw.json under INLINECODE2 - 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
SKILL.md, examples, references, or memory/state files. - Do not write API keys into
recent_calls or any conversation-visible output. Do not tell the user “I won’t echo it back.” - Use interactive keys for the current task only.
- Do not write user config files automatically from this skill.
How This Skill Should Work
- 1. 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
sid in state as a recent call.
Required Auth
Send the API key in the Authorization header:
CODEBLOCK0
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:
- -
recent_calls: array of objects
-
id (call SID)
-
phone_number
-
task
-
started_at
-
status (optional, updated when you fetch)
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:
- - INLINECODE14
- INLINECODE15
- INLINECODE16 (background + goals)
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
from_number is not the same as INLINECODE18 - If
from_number is requested, run caller-ID preflight:
1.
GET /v1/verified-caller-ids
2. Confirm requested
from_number exists in
verified_caller_ids
3. If not verified: do
not place call yet; ask user whether to continue with default caller ID or verify first
- - Normalize
language; normalize voice fields (genderVoice, openaiVoice) only if provided - 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
- 1. Collect required fields using the layered gating flow.
- If
from_number is provided, run caller-ID preflight via GET /v1/verified-caller-ids. - If requested
from_number is not verified, ask user to choose:
- continue now with default caller ID, or
- verify number first (
POST /v1/verify-caller-id, then
GET /v1/verification-status/:verificationId).
- 4. 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
sid in recent_calls. - Reply with confirmation and the call ID.
Scheduled Requests (No Cron)
Because the API has no scheduling field:
- 1. 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 (
cron, launchd, task scheduler) and do not run autonomous background turns. - 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
- 1. Read
recent_calls from state. - For each call, fetch status via
GET /v1/calls/:callId if needed. - Display a numbered list.
Retry Until Answered (important)
When the user asks to call repeatedly until answered:
- 1. Place one call with
POST /v1/start-call. - Poll
GET /v1/calls/:callId until terminal status. - Treat response as either flat (
status, duration) or nested (call.status, call.duration). - If status is
busy, no-answer, failed, or canceled, wait requested interval and place next call. - Stop retry loop when:
- status is
in-progress, or
- status is
completed with
duration > 0.
- 6. 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:
- -
POST /v1/end-call with { callSid }.
Get Results
When the user asks for call results:
- 1. 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: INLINECODE57
- Examples: INLINECODE58
技能名称: openclaw-phone
详细描述:
CallMyCall (OpenClaw 技能)
该技能帮助您通过聊天操作 CallMyCall。它采用拉取模式(无 Webhook 回调):您发起通话,将返回的通话 ID 存储在状态中,之后按需获取状态和结果。
API 密钥解析(OpenClaw 最佳实践)
按以下顺序解析凭据:
- 1. 环境变量:CALLMYCALLAPIKEY(首选)
- OpenClaw 用户配置:~/.openclaw/openclaw.json 中的 skills.openclaw-phone.apiKey
- 如果仍然缺失,则仅针对当前任务请求一次性密钥。
- 仅当用户明确要求持久化时,提供手动保存密钥到 ~/.openclaw/openclaw.json 的说明。
持久化规则:
- - 切勿将 API 密钥存储在 SKILL.md、示例、参考或内存/状态文件中。
- 不要将 API 密钥写入 recent_calls 或任何对话可见的输出中。不要告诉用户“我不会回显它”。
- 仅针对当前任务使用交互式密钥。
- 不要通过此技能自动写入用户配置文件。
该技能的工作方式
- 1. 按照“API 密钥解析(OpenClaw 最佳实践)”中的顺序解析 API 密钥。
- 使用分层门控流程(如下)收集所需的通话信息。
- 提供简短的审查摘要并请求确认。
- 确认后,调用 POST /v1/start-call。
- 将返回的 sid 作为最近通话存储在状态中。
所需认证
在 Authorization 标头中发送 API 密钥:
Authorization: Bearer YOURAPIKEY
切勿将 API 密钥回显给用户或包含在日志/摘要中。
有状态通话列表(必需)
在状态中维护最近通话的列表(最近 10 条):
- id(通话 SID)
- phone_number
- task
- started_at
- status(可选,在获取时更新)
使用此列表让用户可以说“结束通话 1”或“显示通话 2 的结果”。
分层门控流程(从 Web 应用复制)
不要依赖单一的验证步骤。使用以下所有层级。
第 1 层:结构化收集契约
在满足所有必需字段之前,不要最终确定任务:
- - phonenumber
- language
- callbrief(背景 + 目标)
第 2 层:任务差距分析
当用户给出初始请求时,分析缺少什么。然后仅询问缺少的信息。如果用户部分回答,重复分析并继续询问剩余的差距。
第 3 层:提示级别强制执行
当存在缺失信息时,继续收集必需字段。在满足所有必需字段之前,不要继续通话。
第 4 层:最终确定前的运行时验证
在发送通话请求之前:
- - 确保电话号码存在且符合 E.164 格式
- 阻止紧急或付费号码
- 确保 fromnumber 与 phonenumber 不同
- 如果请求了 from_number,运行主叫号码预检:
1. GET /v1/verified-caller-ids
2. 确认请求的 from
number 存在于 verifiedcaller_ids 中
3. 如果未验证:
不要立即发起通话;询问用户是继续使用默认主叫号码还是先验证
- - 标准化 language;仅在提供时标准化语音字段(genderVoice、openaiVoice)
- 如果存在调度,解析并限制在有效时间内
第 5 层:人工审查关卡
提供简短的审查摘要:
- - 电话号码
- 通话简介(背景 + 目标)
- 语言(如果提供,还有语音)
- 任何调度
询问:“确认并发起通话?” 在没有明确确认的情况下不要继续。
工作流程
发起通话
- 1. 使用分层门控流程收集必需字段。
- 如果提供了 fromnumber,通过 GET /v1/verified-caller-ids 运行主叫号码预检。
- 如果请求的 fromnumber 未验证,询问用户选择:
- 立即使用默认主叫号码继续,或
- 先验证号码(POST /v1/verify-caller-id,然后 GET /v1/verification-status/:verificationId)。
- 4. 如果请求了调度/时间,按照下面的定时请求(无 Cron) 操作,而不是立即调用 API。
- 否则调用 POST /v1/start-call。
- 将返回的 sid 存储在 recent_calls 中。
- 回复确认信息和通话 ID。
定时请求(无 Cron)
由于 API 没有调度字段:
- 1. 立即收集所有必需字段。
- 仅在技能状态中保存一个紧凑的通话计划,用于会话内的后续操作。
- 不要创建或修改操作系统调度器(cron、launchd、任务计划程序),不要运行自主后台轮次。
- 提供以下安全选项之一:
- 立即发起通话,或
- 提供提醒摘要,并要求用户在目标时间返回运行 start-call。
如果用户要求稍后调度,解释此技能不会创建后台任务;它可以准备通话计划,并在用户在会话中确认时执行。
列出最近通话
- 1. 从状态中读取 recent_calls。
- 对于每个通话,如果需要,通过 GET /v1/calls/:callId 获取状态。
- 显示编号列表。
重试直到接通(重要)
当用户要求反复拨打电话直到接通时:
- 1. 使用 POST /v1/start-call 发起一次通话。
- 轮询 GET /v1/calls/:callId 直到终端状态。
- 将响应视为扁平结构(status、duration)或嵌套结构(call.status、call.duration)。
- 如果状态是 busy、no-answer、failed 或 canceled,等待请求的间隔并发起下一次通话。
- 当以下情况时停止重试循环:
- 状态是 in-progress,或
- 状态是 completed 且 duration > 0。
- 6. 向用户报告每次尝试(通话 ID + 状态)。
实现说明:每次运行保持一个基础 URL(首选 https://call-my-call-backend.fly.dev),并一致地用于启动和状态端点。
结束通话
如果用户说“结束通话”而没有指定哪一个,列出最近通话并询问是哪一个。
如果只有一个活跃通话,确认并结束它。
调用:
- - POST /v1/end-call,参数为 { callSid }。
获取结果
当用户询问通话结果时:
- 1. 通过 GET /v1/calls/:callId 获取状态。
- 如果可用,通过 GET /v1/calls/:callId/transcripts/stream 获取转录。
- 如果通话被录音,通过 GET /v1/calls/:callSid/recording 获取录音 URL。
返回:
- - 状态(completed、failed、canceled)
- 简短摘要(1 到 3 个要点)
- 转录摘录(前几行,仅在用户要求查看转录内容后)
- 录音 URL(如果存在,警告 URL 访问可能暴露敏感音频)
安全和用户体验
- - 如果用户输入模糊,提出澄清问题。
- 切勿暴露机密或将 API 密钥存储在转录中。
- 将转录和录音视为敏感信息;仅分享用户请求的最小摘录。
- 切勿从此技能创建持久调度器条目或自主后台执行。
- 如果请求失败,显示 HTTP 错误并建议后续步骤。
参考
- - 完整 API 参考:references/api.md
- 示例:examples/prompts.md