|
| 1 | +{ |
| 2 | + "schema_version": 1, |
| 3 | + "id": "io.pilot.agentphone", |
| 4 | + "display_name": "AgentPhone", |
| 5 | + "tagline": "A real phone number for your agent — voice calls, SMS/iMessage, and conversations over REST", |
| 6 | + "description_md": "# AgentPhone — a real phone number for your AI agent\n\nAgentPhone gives your agent its **own real US/Canada phone number**: place and receive **voice calls**, send and receive **SMS \u0026 iMessage**, and hold threaded **conversations** with real people — all over plain REST. This is the managed Pilot front door: you bring **nothing** (no signup, no API key). Pilot holds one AgentPhone master key behind the broker and gives **each Pilot user a $5 budget**; calls and texts debit against it, and once it's spent the paid endpoints return `402 Payment Required` (reads stay free).\n\n## What you can do\n\n- **Call people.** `agentphone.place_call` with a `systemPrompt` runs an autonomous voice call — the phone rings in ~1–2s and the AI holds the conversation. Book a reservation, chase a shipment, return a missed call, or call another agent.\n- **Text people.** `agentphone.send_message` delivers over **iMessage** when both sides support it (unlocking threaded replies, tapback reactions, send effects, typing indicators, group chats) and transparently falls back to **SMS/MMS** otherwise — same call either way.\n- **Answer \u0026 follow up.** Poll `agentphone.list_number_messages` / `agentphone.list_conversation_messages` for inbound texts and `agentphone.get_call` for call transcripts — **no websockets required**.\n- **Manage your setup.** Buy/release numbers, create and tune agents (voice, model tier, system prompt, ambience), keep an address book of contacts, and attach numbers to agents.\n\n## How it works (no signup step)\n\nBecause this is the **managed** app, the AgentPhone account already exists behind the Pilot broker — you skip the `/v0/agent/sign-up` + `/v0/agent/verify` flow entirely. Just call the `/v1` methods below; the broker authenticates you as your Pilot identity, injects the master key, meters your spend, and forwards to `https://api.agentphone.ai`.\n\n**Async, poll-based (no streaming):**\n1. `agentphone.place_call` → returns a call `id` immediately; the call runs in the background.\n2. Poll `agentphone.get_call` every few seconds until `status` is `completed` or `failed`, then read `transcripts[]` (or `agentphone.get_transcript`).\n3. For inbound SMS, poll `agentphone.list_number_messages` with the `after` cursor and filter `direction == \"inbound\"`.\n\n## Critical gotchas (read once)\n\n- **You cannot call 911**, N11 numbers, or crisis lines — they're blocked. If your human has an emergency, tell them to dial directly.\n- **Released numbers are gone forever** — no refund for the unused month. Confirm before `agentphone.release_number`.\n- **Always use E.164**: `+14155551234` ✓ — never `(415) 555-1234` or `415-555-1234`. Assume `+1` for a bare US number and confirm if it matters.\n- **Inbound calls need hosted mode OR a webhook.** Create agents with `voiceMode: \"hosted\"` explicitly (the backend defaults to `webhook`, which fails inbound if no webhook is set).\n- **iMessage-only features** (reactions, send effects, typing, backgrounds, contact cards) are silently ignored on SMS — check the response `channel`.\n- **Don't spam.** Unsolicited bulk calls/texts are illegal and get the account suspended.\n\n## Cost \u0026 the $5 budget\n\nReads are free. Spending operations debit your per-user $5 Pilot budget: buying a number (**$3.00/mo**), placing a call (**per-minute**), and sending a text (**~$0.01–0.02**). When a call would overdraw, the broker returns `402` before anything is charged, and every response carries your remaining balance in the `X-Pilot-Credits-Remaining` header (micro-dollars).\n\nEvery method's parameters, kind, and latency class are discoverable at runtime via `agentphone.help`.\n", |
| 7 | + "vendor": { |
| 8 | + "name": "AgentPhone", |
| 9 | + "url": "https://agentphone.ai", |
| 10 | + "contact": "founders@agentphone.to", |
| 11 | + "publisher_pubkey": "ed25519:mvVzYABubZwOTzWWQA/TDbRLYkKzmD/x6k/w0nz+zHc=" |
| 12 | + }, |
| 13 | + "homepage": "https://agentphone.ai", |
| 14 | + "source_url": "https://github.com/AgentPhone-AI/skills", |
| 15 | + "license": "Apache-2.0", |
| 16 | + "categories": [ |
| 17 | + "communication", |
| 18 | + "phone", |
| 19 | + "voice", |
| 20 | + "messaging" |
| 21 | + ], |
| 22 | + "keywords": [ |
| 23 | + "phone", |
| 24 | + "sms", |
| 25 | + "imessage", |
| 26 | + "voice", |
| 27 | + "calls", |
| 28 | + "telephony", |
| 29 | + "conversations", |
| 30 | + "agent" |
| 31 | + ], |
| 32 | + "size": { |
| 33 | + "bundle_bytes": 5130424, |
| 34 | + "installed_bytes": 9363452 |
| 35 | + }, |
| 36 | + "compat": { |
| 37 | + "min_pilot_version": "1.10.0", |
| 38 | + "runtimes": [ |
| 39 | + "go" |
| 40 | + ] |
| 41 | + }, |
| 42 | + "methods": [ |
| 43 | + { |
| 44 | + "name": "agentphone.usage", |
| 45 | + "summary": "Account status: plan, phone-number hold limit (used/limit/remaining), and message/call/webhook stats. Call this first to orient in a session. Read-only; no charge." |
| 46 | + }, |
| 47 | + { |
| 48 | + "name": "agentphone.usage_daily", |
| 49 | + "summary": "Daily usage breakdown for the last N days (max 365). Read-only." |
| 50 | + }, |
| 51 | + { |
| 52 | + "name": "agentphone.usage_monthly", |
| 53 | + "summary": "Monthly usage aggregation. Read-only." |
| 54 | + }, |
| 55 | + { |
| 56 | + "name": "agentphone.usage_by_number", |
| 57 | + "summary": "Usage broken down per phone number. Read-only." |
| 58 | + }, |
| 59 | + { |
| 60 | + "name": "agentphone.usage_by_agent", |
| 61 | + "summary": "Usage broken down per agent over a period. Read-only." |
| 62 | + }, |
| 63 | + { |
| 64 | + "name": "agentphone.list_voices", |
| 65 | + "summary": "List available text-to-speech voices (voice_id, voice_name, provider, gender, accent, preview_audio_url) across ElevenLabs, Cartesia, OpenAI, and platform voices. gender/accent/preview may be null — do not crash on missing fields. Use voice_id when creating/updating an agent. Read-only." |
| 66 | + }, |
| 67 | + { |
| 68 | + "name": "agentphone.list_agents", |
| 69 | + "summary": "List your agents (phone personas: name, voiceMode, model tier, system prompt, attached numbers). You get one starter agent on account setup — ALWAYS list before creating another. Read-only." |
| 70 | + }, |
| 71 | + { |
| 72 | + "name": "agentphone.create_agent", |
| 73 | + "summary": "Create an agent (phone persona). For AI-driven use pass voiceMode:\"hosted\" explicitly (the backend defaults to \"webhook\", which needs a configured webhook or inbound calls fail). systemPrompt is required for hosted. Pick a voice from agentphone.list_voices. Free (no telephony spend)." |
| 74 | + }, |
| 75 | + { |
| 76 | + "name": "agentphone.get_agent", |
| 77 | + "summary": "Get one agent's full config and its attached numbers. Read-only." |
| 78 | + }, |
| 79 | + { |
| 80 | + "name": "agentphone.update_agent", |
| 81 | + "summary": "Update an agent — only the fields you send change. Any create field is updatable (systemPrompt, voice, modelTier, …). Free." |
| 82 | + }, |
| 83 | + { |
| 84 | + "name": "agentphone.delete_agent", |
| 85 | + "summary": "Delete an agent. Irreversible — clears the agent's references on its numbers/conversations/calls (those are NOT deleted). Confirm with your human first. Free." |
| 86 | + }, |
| 87 | + { |
| 88 | + "name": "agentphone.attach_number", |
| 89 | + "summary": "Attach an existing number to an agent so the agent can call/text from it. Free." |
| 90 | + }, |
| 91 | + { |
| 92 | + "name": "agentphone.detach_number", |
| 93 | + "summary": "Detach a number from an agent (the number is kept, just unassigned). Free." |
| 94 | + }, |
| 95 | + { |
| 96 | + "name": "agentphone.list_agent_conversations", |
| 97 | + "summary": "List an agent's conversation threads, newest activity first (data[], hasMore, total). Read-only." |
| 98 | + }, |
| 99 | + { |
| 100 | + "name": "agentphone.list_agent_calls", |
| 101 | + "summary": "List an agent's calls (data[], hasMore, total). Read-only." |
| 102 | + }, |
| 103 | + { |
| 104 | + "name": "agentphone.list_numbers", |
| 105 | + "summary": "List your active phone numbers (id, phoneNumber, country, status, agentId). Read-only." |
| 106 | + }, |
| 107 | + { |
| 108 | + "name": "agentphone.buy_number", |
| 109 | + "summary": "Provision a new US/CA phone number. COSTS $3.00 from your $5 Pilot budget (402 if it would overdraw). The provisioned number is saved to this host's ~/.pilot/.agentphone so you can recall it later with agentphone.mynumber — no need to store it yourself. Optionally attach to an agent and request an area code." |
| 110 | + }, |
| 111 | + { |
| 112 | + "name": "agentphone.get_number", |
| 113 | + "summary": "Get one phone number by id (any status, including released). Read-only." |
| 114 | + }, |
| 115 | + { |
| 116 | + "name": "agentphone.release_number", |
| 117 | + "summary": "Release (delete) a number. IRREVERSIBLE — the number returns to the carrier pool; no refund for the unused month. Confirm with your human first. Free to call." |
| 118 | + }, |
| 119 | + { |
| 120 | + "name": "agentphone.list_number_messages", |
| 121 | + "summary": "List messages on a number (inbound + outbound), newest first, cursor-paginated. THE non-websocket way to detect SMS replies: poll with the `after` cursor and filter direction==\"inbound\". Read-only." |
| 122 | + }, |
| 123 | + { |
| 124 | + "name": "agentphone.list_number_calls", |
| 125 | + "summary": "List calls on a number. Read-only." |
| 126 | + }, |
| 127 | + { |
| 128 | + "name": "agentphone.get_contact_card", |
| 129 | + "summary": "Get the iMessage contact card shown on a number (firstName, lastName, displayName, hasAvatar). iMessage only. Read-only." |
| 130 | + }, |
| 131 | + { |
| 132 | + "name": "agentphone.set_contact_card", |
| 133 | + "summary": "Create/replace the iMessage contact card on a number (name + avatar shown to recipients). iMessage only. Free." |
| 134 | + }, |
| 135 | + { |
| 136 | + "name": "agentphone.delete_contact_card", |
| 137 | + "summary": "Remove the iMessage contact card from a number. Free." |
| 138 | + }, |
| 139 | + { |
| 140 | + "name": "agentphone.send_message", |
| 141 | + "summary": "Send an SMS/iMessage. COSTS MONEY (~$0.01–0.02, debited from your $5 budget → 402 if over). Auto-delivers over iMessage when both sides support it, else SMS/MMS — the response `channel` (sms|mms|imessage) tells you how it went. E.164 for `to_number` (or a group id grp_… for an iMessage group). iMessage-only extras (send_style, reply_to_message_id) are silently ignored on SMS." |
| 142 | + }, |
| 143 | + { |
| 144 | + "name": "agentphone.react", |
| 145 | + "summary": "Send a tapback reaction to a message. iMessage ONLY — returns 400 on SMS. Free." |
| 146 | + }, |
| 147 | + { |
| 148 | + "name": "agentphone.list_calls", |
| 149 | + "summary": "List calls for the account (filter by status/direction). Read-only." |
| 150 | + }, |
| 151 | + { |
| 152 | + "name": "agentphone.place_call", |
| 153 | + "summary": "Place an OUTBOUND voice call. COSTS MONEY (per-minute, ~$0.05+, debited from your $5 budget → 402 if over). With `systemPrompt` the AI runs the call autonomously (recommended); without it, each turn is POSTed to the agent's webhook. Returns a call id IMMEDIATELY (async) — the phone rings in a second or two. Then POLL agentphone.get_call every few seconds until status is completed/failed to read the transcript. Cannot call 911 / N11 / crisis lines (blocked)." |
| 154 | + }, |
| 155 | + { |
| 156 | + "name": "agentphone.get_call", |
| 157 | + "summary": "Get a call and its embedded transcripts[]. THE poll target for a call outcome (no websockets): call every few seconds until status is completed or failed (in-progress means partial/empty transcript). Also carries durationSeconds, startedAt, endedAt. Read-only." |
| 158 | + }, |
| 159 | + { |
| 160 | + "name": "agentphone.end_call", |
| 161 | + "summary": "Terminate an in-progress call. status/endedAt settle shortly after via the provider — keep polling agentphone.get_call until terminal. Free." |
| 162 | + }, |
| 163 | + { |
| 164 | + "name": "agentphone.get_transcript", |
| 165 | + "summary": "Get the full ordered transcript of a call as plain JSON (user utterance + agent response per turn). This is the REST/polling alternative to the SSE live-transcript stream — the adapter never uses the stream. Read-only." |
| 166 | + }, |
| 167 | + { |
| 168 | + "name": "agentphone.list_conversations", |
| 169 | + "summary": "List conversation threads (one per external contact or iMessage group), sorted by lastMessageAt desc (data[], hasMore, total). Read-only." |
| 170 | + }, |
| 171 | + { |
| 172 | + "name": "agentphone.get_conversation", |
| 173 | + "summary": "Get one conversation with its recent messages (participant, isGroup, group roster, messageCount, metadata). Read-only." |
| 174 | + }, |
| 175 | + { |
| 176 | + "name": "agentphone.update_conversation", |
| 177 | + "summary": "Update a conversation's `metadata` (attach custom AI context/state to the thread). Free." |
| 178 | + }, |
| 179 | + { |
| 180 | + "name": "agentphone.list_conversation_messages", |
| 181 | + "summary": "List a conversation's messages, cursor-paginated (data[], hasMore). Poll with `after` to catch new inbound replies in a thread. Read-only." |
| 182 | + }, |
| 183 | + { |
| 184 | + "name": "agentphone.typing", |
| 185 | + "summary": "Show a typing indicator before you reply. iMessage only, best-effort, auto-expires (no stop call). Free." |
| 186 | + }, |
| 187 | + { |
| 188 | + "name": "agentphone.set_background", |
| 189 | + "summary": "Set a chat background image for a conversation. iMessage only. Free." |
| 190 | + }, |
| 191 | + { |
| 192 | + "name": "agentphone.clear_background", |
| 193 | + "summary": "Clear a conversation's chat background. iMessage only. Free." |
| 194 | + }, |
| 195 | + { |
| 196 | + "name": "agentphone.list_contacts", |
| 197 | + "summary": "List saved contacts (data[], hasMore, total); `search` filters by name/phone. Read-only." |
| 198 | + }, |
| 199 | + { |
| 200 | + "name": "agentphone.create_contact", |
| 201 | + "summary": "Save a contact so you can look them up by name later. phoneNumber is normalized to E.164; returns 409 if the phone already exists. Free." |
| 202 | + }, |
| 203 | + { |
| 204 | + "name": "agentphone.get_contact", |
| 205 | + "summary": "Get one contact by id. Read-only." |
| 206 | + }, |
| 207 | + { |
| 208 | + "name": "agentphone.update_contact", |
| 209 | + "summary": "Update a contact — only the fields you send change (phone is re-normalized; 409 on conflict). Free." |
| 210 | + }, |
| 211 | + { |
| 212 | + "name": "agentphone.delete_contact", |
| 213 | + "summary": "Delete a contact. Confirm with your human first. Free." |
| 214 | + }, |
| 215 | + { |
| 216 | + "name": "agentphone.get_webhook", |
| 217 | + "summary": "Get the account-level webhook config. Read-only. (Polling is the default event model for this adapter; webhooks are optional.)" |
| 218 | + }, |
| 219 | + { |
| 220 | + "name": "agentphone.set_webhook", |
| 221 | + "summary": "Set the account-level webhook URL (returns a signing `secret`; a new one each call). NOTE: on the shared Pilot AgentPhone account this is a GLOBAL setting — prefer per-agent webhooks or polling. Free." |
| 222 | + }, |
| 223 | + { |
| 224 | + "name": "agentphone.delete_webhook", |
| 225 | + "summary": "Remove the account-level webhook. Global on the shared account — use with care. Free." |
| 226 | + }, |
| 227 | + { |
| 228 | + "name": "agentphone.list_webhook_deliveries", |
| 229 | + "summary": "List recent webhook delivery attempts (items[], total). Read-only." |
| 230 | + }, |
| 231 | + { |
| 232 | + "name": "agentphone.webhook_delivery_stats", |
| 233 | + "summary": "Aggregated webhook delivery stats over the last N hours (success/failed/pending, byEventType, byHour). Read-only." |
| 234 | + }, |
| 235 | + { |
| 236 | + "name": "agentphone.test_webhook", |
| 237 | + "summary": "Send a synthetic test event to verify your endpoint is reachable and verifying signatures. Free." |
| 238 | + }, |
| 239 | + { |
| 240 | + "name": "agentphone.get_agent_webhook", |
| 241 | + "summary": "Get an agent-specific webhook (overrides the account default for that agent). Read-only." |
| 242 | + }, |
| 243 | + { |
| 244 | + "name": "agentphone.set_agent_webhook", |
| 245 | + "summary": "Set an agent-specific webhook URL (overrides the account default for THIS agent only — safer than the account-level webhook on a shared account). Free." |
| 246 | + }, |
| 247 | + { |
| 248 | + "name": "agentphone.delete_agent_webhook", |
| 249 | + "summary": "Delete an agent-specific webhook (the agent falls back to the account default). Free." |
| 250 | + }, |
| 251 | + { |
| 252 | + "name": "agentphone.mynumber", |
| 253 | + "summary": "Recall the phone number(s) THIS daemon provisioned (local, no backend call, free). Reads ~/.pilot/.agentphone, populated automatically by agentphone.buy_number. Returns {entries:[{id,phoneNumber,status,agentId,...}]} — empty if this host hasn't provisioned one yet. Use it to find 'my number' without listing the shared account." |
| 254 | + }, |
| 255 | + { |
| 256 | + "name": "agentphone.help", |
| 257 | + "summary": "Discovery: every method with params, kind, and latency class." |
| 258 | + } |
| 259 | + ], |
| 260 | + "changelog": [ |
| 261 | + { |
| 262 | + "version": "0.3.0", |
| 263 | + "notes": [ |
| 264 | + "Released v0.3.0" |
| 265 | + ] |
| 266 | + } |
| 267 | + ], |
| 268 | + "links": [ |
| 269 | + { |
| 270 | + "label": "Source", |
| 271 | + "url": "https://github.com/AgentPhone-AI/skills" |
| 272 | + }, |
| 273 | + { |
| 274 | + "label": "Website", |
| 275 | + "url": "https://agentphone.ai" |
| 276 | + } |
| 277 | + ] |
| 278 | +} |
0 commit comments