# StablePhone API AI phone calls and phone numbers via micropayments. USDC on Base. No API keys. ## Make a call POST /api/call ($0.54) Body (JSON): { "phone_number": "+14155551234", "task": "Call this person and ask if they're available for a meeting tomorrow at 2pm." } Required fields: - phone_number: E.164 format (e.g. +14155551234) - task: Instructions for the AI agent (what to say, how to behave) Optional fields: - from: Outbound caller ID — must be an active StablePhone number (see POST /api/number) - first_sentence: Specific opening line - voice: Voice preset (see below) or any Bland.ai voice ID - max_duration: Max call length in minutes (1-30, default 5) - wait_for_greeting: Wait for recipient to speak first (default false) - record: Record call audio (default true) - model: "base" (default, best for most cases) or "turbo" (lowest latency) - transfer_phone_number: Number to transfer to if caller requests a human - voicemail_action: "hangup" (default), "leave_message", or "ignore" (bypasses detection — best for leaving voicemails naturally) - voicemail_message: Message to leave on voicemail (required when voicemail_action is "leave_message") - metadata: Custom key-value data to attach to the call Response: { "success": true, "call_id": "abc-123-def", "message": "Call initiated" } ## Check call status GET /api/call/{call_id} (SIWX, free) — only the wallet that initiated the call can poll. Response includes: status, completed, to, from, call_length, answered_by, summary, transcript, transcripts (array), recording_url, price, error_message, created_at. ## Buy a phone number POST /api/number ($20.00) Body (JSON): { "area_code": "415", "country_code": "US" } Optional fields: - area_code: 3-digit US/CA area code (random if omitted) - country_code: "US" (default) or "CA" Response: { "success": true, "phone_number": "+14155551234", "expires_at": "2025-07-14T..." } Numbers expire after 30 days. Top up to extend. ## Top up a phone number POST /api/number/topup ($15.00) Body (JSON): { "phone_number": "+14155551234" } Extends the number by 30 days from the current expiry (or from today if already expired). Top-ups stack — call it multiple times to prepay months ahead. Anyone can top up any number. Response: { "success": true, "phone_number": "+14155551234", "expires_at": "2025-08-14T..." } ## List your numbers GET /api/numbers (SIWX, free) — returns numbers for the authenticated wallet. No query params. Response: { "success": true, "numbers": [{ "phone_number": "+14155551234", "expires_at": "...", "area_code": "415", "country_code": "US" }] } ## Typical flow 1. POST /api/call → get 402 Payment Required 2. Pay with x402-compatible client (USDC on Base) 3. Receive call_id 4. Poll GET /api/call/{call_id} until completed=true 5. Read transcript and summary ## Phone number flow 1. POST /api/number → buy a number ($20.00, active 30 days) 2. Use "from" field in POST /api/call to call from your number 3. POST /api/number/topup → extend by 30 days ($15.00) 4. GET /api/numbers (SIWX) → list your active numbers ## iMessage/FaceTime lookup POST /api/lookup ($0.05) Body (JSON): { "phone_number": "+14155551234" } Required fields: - phone_number: E.164 format (e.g. +14155551234) Response (202): { "token": "" } The lookup is async (30-90 seconds typical). Use the token to poll for results: GET /api/lookup/status?token= (SIWX, free) — only the wallet that paid can poll. Poll responses: - { "status": "pending", "message": "..." } — still processing, poll again in a few seconds - { "status": "complete", "phone_number": "+1...", "imessage": "available"|"unavailable"|"unknown", "facetime": "available"|"unavailable"|"unknown"|"pending", "carrier": {"carrier": "...", "number_type": "..."}, "country": {"name": "...", "iso2": "...", "flag": "..."}, "checked_at": "2026-02-20" } - { "status": "error", "error": "..." } — upstream error Notes: - "unknown" means the number exists but iMessage/FaceTime status can't be determined (likely landline/VoIP) - Token expires after 60 minutes. If expired, submit a new lookup (re-pay) - Same number submitted within ~1 hour reuses the cached result (no extra charge) ## Lookup flow 1. POST /api/lookup with phone_number → get 402 Payment Required 2. Pay with x402-compatible client (USDC on Base) 3. Receive { token } (202) 4. Poll GET /api/lookup/status?token= every 3-5 seconds 5. When status is "complete", read imessage/facetime fields ## Voices - nat (default): American female - josh: Articulate American male - maya: Young American female, soft - june: American female - paige: Calm, soft-tone female - derek: Soft and engaging male - florian: German male ## Authentication - **Paid endpoints** (call, number, number/topup, lookup): x402 payment (USDC on Base). - **Free endpoints** (call status, numbers, lookup status): SIWX required. Use fetch_with_auth / authed_call. Only the owning wallet can access their data. ## Discovery - GET /.well-known/x402 — machine-readable resource discovery - GET /llms.txt — this file