Send your first text in ten minutes

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.

1Authenticate

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"

2Create a tenant

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", … }

3Buy a number

# 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"}'

4Send a message

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…" }

5Receive events

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.

6Rehearse failure

In test mode, magic numbers simulate the things you hope never happen:

SDKs: typed clients are generated straight from the OpenAPI spec — TypeScript (@handset/sdk) and Python (handset). Ask us for early access and we'll onboard you with keys and the packages.

Ready for every endpoint, parameter, and schema? Read the full API reference.