Handset gives your platform a complete business phone system — compliant
two-way SMS, inbound voice, voicemail, and real numbers — through one
REST API at https://api.handset.dev/v1.
Every request carries your API key as a bearer token. Keys come in pairs:
test keys run against a simulated carrier (free numbers,
instant compliance), live keys hit the real network. Same
code, different key.
# Sanity check: list your tenants curl https://api.handset.dev/v1/tenants \ -H "Authorization: Bearer $HANDSET_API_KEY"
A tenant is one of your customers — the dental practice or plumbing company inside your product. Numbers, conversations, and business hours all belong to tenants, so your one integration serves every customer.
curl https://api.handset.dev/v1/tenants \ -H "Authorization: Bearer $HANDSET_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Bayview Dental", "timezone": "America/Los_Angeles"}' { "id": "tnt_01kzv90afex…", "name": "Bayview Dental", … }
# Search available inventory… curl "https://api.handset.dev/v1/phone_numbers/available?area_code=415" \ -H "Authorization: Bearer $HANDSET_API_KEY" # …and purchase one for the tenant. It arrives SMS- and voice-ready. curl https://api.handset.dev/v1/phone_numbers \ -H "Authorization: Bearer $HANDSET_API_KEY" \ -H "Content-Type: application/json" \ -d '{"tenant_id": "tnt_…", "phone_number": "+14155550134"}'
Threading, opt-out enforcement, and 10DLC checks happen inside this one
call. Pass an Idempotency-Key and retries are safe.
curl https://api.handset.dev/v1/messages \ -H "Authorization: Bearer $HANDSET_API_KEY" \ -H "Idempotency-Key: 3f1b2c…" \ -H "Content-Type: application/json" \ -d '{"from": "num_…", "to": "+14805550199", "body": "Your technician is 15 minutes out."}' { "id": "msg_01kzv…", "status": "queued", "conversation_id": "cnv_01kzv…" }
Register a webhook endpoint and everything that happens on the line —
message.received, message.delivered,
call.completed, voicemail.created — arrives as a
signed event, retried until you return a 2xx.
curl https://api.handset.dev/v1/webhook_endpoints \ -H "Authorization: Bearer $HANDSET_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://yourapp.com/handset/events"}' # The response contains the signing secret — shown exactly once.
Verify every delivery: recompute HMAC-SHA256 over
{timestamp}.{body} with your endpoint secret and compare it to
the v1 value in the Handset-Signature header in
constant time. Reject anything older than five minutes.
In test mode, magic numbers simulate the things you hope never happen:
+15005550001 — delivery fails with carrier_rejected00000 — address registration is rejectedReady for every endpoint, parameter, and schema? Read the full API reference.