Docs

SIPCraft docs

Everything here works against the v0 API. Anything marked Planned doesn't exist yet.

Quickstart

Five calls to a working agent line

bash
export K=sk_...  B=https://sipcraft.dev
curl -s $B/v0/numbers/search?area_code=415 -H "Authorization: Bearer $K"
curl -s $B/v0/numbers -H "Authorization: Bearer $K" -d '{"e164":"+14155550123","confirm":true}'
curl -s -X PATCH $B/v0/numbers/+14155550123 -H "Authorization: Bearer $K" \
     -d '{"route_type":"websocket","route_target":"wss://agent.example.com/media"}'
curl -s $B/v0/webhooks -H "Authorization: Bearer $K" -d '{"url":"https://agent.example.com/hooks"}'
curl -s $B/v0/verification -H "Authorization: Bearer $K" -d '{"kind":"individual"}'   # Get Verified: a human opens verification_url

Get Verified is powered by Didit. /v0/kyc and the MCP tools start_kyc and get_kyc_status remain as aliases.

Agent sign-up

Agents can sign up themselves

  1. The agent signs up with its own email. MCP sign_up (no Authorization header) or POST /v0/signup with email, optional agent_name and use_case. The API key is returned once. A 6-digit code and a link go to that email; confirm with verify_email or POST /v0/email/verify.
  2. It makes free on-net calls. After the email is verified the account is on the free on-net plan: endpoints and SIPCraft-to-SIPCraft calls, with the fair-use limits below.
  3. Its human owner logs in and gets verified, which unlocks numbers. Call get_verified with owner_email (and notify_human: true to email them). It returns verify_url, message_for_human and instructions. Pass the message to your human. They set a password or use an emailed sign-in link at /owner/, then complete Get Verified (powered by Didit). The owner becomes the responsible person on the account. One verified owner can own several agents, and each new agent they accept is unlocked straight away.
MCP · tools/call
{"name":"sign_up","arguments":{"email":"agent@yourco.com","agent_name":"Front desk"}}
{"name":"verify_email","arguments":{"code":"123456"}}
{"name":"get_verified","arguments":{"owner_email":"you@yourco.com","notify_human":true}}
# place_call to a phone number before then → 403 with verify_url + message_for_human

Limits: 5 sign-ups per IP per hour and 20 per day, 3 per email per day, 5 code attempts. Disposable email domains are rejected. Until the email is verified the key can only read and verify. Events: account.signed_up, account.email_verified, kyc.owner_invited, kyc.link_sent, kyc.owner_linked, kyc.started, kyc.approved / kyc.declined.

On-net · free

SIPCraft-to-SIPCraft calls, no phone number

Every endpoint gets a SIP address like sip:front-desk@harbor.sipcraft.dev and a short extension. Calls between SIPCraft addresses never touch the phone network, so they're free and need no verification. Types: agent (routes to your wss:// stream, SIP URI or webhook), human_app (an invite link; the person signs in on the SIPCraft app or the NT Connect App, Planned), and sip_device (registrar credentials for a softphone).

bash
curl -s $B/v0/endpoints -H "Authorization: Bearer $K" -d '{"name":"front-desk","route_target":"wss://agent.example.com/media"}'
curl -s $B/v0/endpoints -H "Authorization: Bearer $K" -d '{"type":"human_app","name":"nick"}'   # returns invite_url to share
curl -s $B/v0/calls -H "Authorization: Bearer $K" -d '{"from":"front-desk","to":"nick"}'           # or to: "sip:…" · network: onnet

Fair use on the free plan: 1,000 on-net minutes a month, 3 simultaneous calls, 10 endpoints. The audio stream to your agent is included. Past a cap the API returns 429 with upgrade: "buy_number". Cross-account calls reach only endpoints that set accept_from: "anyone". A human endpoint rings only while its app is registered (presence online); otherwise the call ends no_answer. MCP: create_endpoint, list_endpoints, call_endpoint, or place_call with a sip: target.

Media streams

The WebSocket audio protocol

The framing is JSON, compatible with the Twilio Media Streams message shapes so existing agent code ports over. Audio is base64 G.711 µ-law at 8 kHz in 20 ms frames.

SIPCraft → agent
{"event":"start","start":{"call_id":"call_…","from":"+1…","to":"+1…",
  "media_format":{"encoding":"audio/x-mulaw","sample_rate":8000,"channels":1}}}
{"event":"media","media":{"payload":"<base64 160 bytes>","timestamp":"20"}}
{"event":"dtmf","dtmf":{"digit":"5"}}
{"event":"mark","mark":{"name":"greeting-done"}}
{"event":"stop"}
agent → SIPCraft
{"event":"media","media":{"payload":"<base64 µ-law>"}}
{"event":"mark","mark":{"name":"greeting-done"}}  # echoed when played
{"event":"clear"}                                # barge-in: flush queued audio
Webhooks

Verifying signatures

Every delivery carries SIPCraft-Signature: t=<unix>,v1=<hex>, where v1 = HMAC-SHA256(secret, "<t>.<raw body>"). Reject timestamps older than five minutes. Missed something? Replay from GET /v0/events?since=.

python
def verify(secret, header, body):
    t, v1 = (kv.split("=", 1)[1] for kv in header.split(","))
    mac = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, v1) and abs(time.time() - int(t)) < 300

Event types: number.purchased number.released call.ringing call.answered call.busy call.completed recording.ready endpoint.created kyc.approved (Get Verified) kyc.declined webhook.test

Billing

Prepaid credit

Usage is pay-as-you-go, drawn from prepaid credit. Every grant, pack purchase, call and monthly number fee is a row in the credit ledger, and your balance is their sum. Packs: $5, $10, $20, $50, $100. Payments coming soon

billing API
GET  /v0/billing            # balance, packs, payments_enabled, auto_topup, stop_outbound_at_zero
GET  /v0/billing/ledger     # grant | purchase | usage | number_fee entries
PUT  /v0/billing/settings   {"auto_topup":{"enabled":true,"threshold_usd":5,"pack_usd":20},"stop_outbound_at_zero":true}
POST /v0/billing/checkout   {"pack_usd":20}  # Stripe Checkout URL; 503 until payments are enabled
Errors & limits

Errors are RFC 9457 problem documents

Key scopes: numbers:read numbers:write calls:read calls:write calls:outbound webhooks:write account:read billing:write. Send Idempotency-Key on writes and a retry returns the original response.