SaffronSuite

Saffron SIP

Any PBX: the iPhone wake webhook

If your phone system can send one HTTPS POST when a call arrives, it can ring a sleeping iPhone. This page is the whole contract: FreeSWITCH, Kamailio, or anything else with webhook or scripting support.

The required call flow

  1. A call arrives for the extension. Hold it: don't deliver the INVITE yet.
  2. POST the webhook below.
  3. Wait about 1.5 seconds. Apple wakes the phone, the app rings it via CallKit and refreshes its SIP registration.
  4. Deliver the INVITE to the extension as normal, with your usual timeout and voicemail fallback.

The request

request
POST https://sipreg.saffronsuite.com/v1/incoming-call
Authorization: Bearer pk_your_key_from_the_app
Content-Type: application/json

{
  "call_id": "pbx-call-1700000000.12",
  "to": "sip:101@pbx.example.com",
  "from": "+61290001234",
  "display_name": "Jane Caller"
}

The pk_ key comes from the app: Settings → Incoming Calls. It's scoped to one account and can be rotated there; treat it like a password. Requests must be POST, HTTPS, JSON, and under 8 KB.

FieldRequiredNotes
call_idyesUnique per call, stable across retries (it's the idempotency key). The inbound leg's SIP Call-ID is ideal; any unique string works. Max 200 chars.
toyesThe full SIP address of the extension being called, as sip:user@host. This is how the relay finds the phone: it must equal what the app registered. A bare extension number is rejected. Max 512 chars.
fromyesThe caller's number or extension. Shown as the caller on the iPhone. Max 128 chars.
display_namenoThe caller's name, shown on the incoming-call screen. Max 120 chars.

The responses

CodeBodyMeaning
202acceptedPush sent. devices_woken says to how many phones.
202duplicate_pendingRetry of a call_id that's already in flight. Harmless.
200duplicate_acceptedRetry of a call_id that already woke the phone. Harmless.
401invalid_api_keyMissing or wrong pk_ key. Re-copy it from the app.
402subscription_requiredNo active subscription on the account. Subscribe in the app.
404no_active_devicesNo enrolled phone matches the 'to' address. Almost always an AOR mismatch.
409idempotency_conflictSame call_id, different payload. Keep the payload identical on retries.
422invalid_payloadA field failed validation; the issues array says which.
502apns_failedApple couldn't deliver the push to any device. Usually transient; retry.

Retrying with the same call_id and payload is always safe; the relay is idempotent per call.

Test it from a terminal

You don't need a PBX to test the wake path. From any machine, with the values your app is actually configured with:

terminal
curl -s https://sipreg.saffronsuite.com/v1/incoming-call \
  -H "Authorization: Bearer pk_your_key_from_the_app" \
  -H "Content-Type: application/json" \
  -d '{"call_id":"test-1","to":"sip:101@pbx.example.com","from":"100","display_name":"Test"}'

Lock the phone first, for the full effect. 202 and a ring means the wake path works; anything else is decoded on the troubleshooting page. Once the wake works, hooking it into your PBX is just running that POST at the right moment in your call flow (FreeSWITCH: mod_curl or a Lua hook; Kamailio: http_client before relaying the INVITE).