> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firetone.com.au/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile push

> Waking the FireTone app for an incoming call when the phone is in a pocket

A phone in a pocket has no SIP socket. Android kills a background connection
within minutes; iOS suspends it the moment the app leaves the screen. A
mobile that is not registered does not ring, however many times you call it.

The way out is a push. When a call arrives for an extension whose mobile app
has said how it can be woken, FireTone sends the push, waits for the app to
wake and register again, and only then rings the extension. The caller hears
ringback for the wait, a few seconds at most.

## What the app tells FireTone

The app has two ways of saying how to wake it, and both work.

* **In its SIP registration.** The Contact it registers with carries the
  standard parameters (RFC 8599): `pn-provider` (`fcm` for Android, `apns`
  or `apns.dev` for iPhone), `pn-prid` (its push token) and `pn-param` (the
  app's parameter: the Firebase app id, or the APNs topic). The extension's
  page shows these under the device, so you can see the app registered the
  way push needs before anything is pushed.
* **By posting its token.** `POST /push/register`, which is how an iPhone's
  PushKit token reaches FireTone at all — it is not in the SIP registration.
  The extension's page lists what has been posted under **Can be woken by
  push**.

### The posting contract

For whoever builds the app. The call is on the FireTone API, not on a
separate service:

```http theme={null}
POST https://api.<your host>/api/v1/push/register
Authorization: Basic <base64 of sipUser:sipPassword>
Content-Type: application/json

{
  "token":    "<the FCM token, or the PushKit token as hex>",
  "sipUser":  "911001@acme.firet.one",
  "platform": "android",
  "param":    "au.remotiq.firetone.client.voip"
}
```

| Field      |                                                                                                                       |
| ---------- | --------------------------------------------------------------------------------------------------------------------- |
| `token`    | the device's push token. Required. A token that moves to another extension follows it                                 |
| `sipUser`  | `number@domain`, the extension's SIP address. Required                                                                |
| `platform` | `android`, `ios`, or `ios.dev` for a development build, whose token belongs to Apple's sandbox. Defaults to `android` |
| `param`    | the app's parameter, when it is not the operator's default app for the platform. Optional                             |

<Warning>
  **It authenticates as the extension**, with HTTP Basic carrying the SIP
  username and password the app already holds. Unauthenticated, anyone could
  attach a token to a number and be told who calls it. Wrong credentials and
  no such extension get the same `401`, so a probe learns neither.
</Warning>

**There is no separate relay, and no `/wake`.** FireTone wakes devices
itself, at the moment a call arrives, from what is registered and what has
been posted. The app's only call is the one above; the base URL is the
FireTone API. A `200` means the token is stored.

## What you set up, once

Under **Settings → Mobile push**, add the FireTone app for each platform.
These are the operator's, not a tenant's: one app serves every organisation.

**Android (FCM).** In the Firebase console, the project the Android app
belongs to, and a service account with the *Firebase Cloud Messaging API*
role. Paste the service account's JSON key. The device parameter is what
the app puts in `pn-param`; leave it empty to make this the default Android
app.

**iPhone (APNs).** In the Apple Developer portal under Keys, a key with
*Apple Push Notifications* enabled, downloaded as a `.p8`. Enter the team
id, the key id, the topic (the app's bundle id with `.voip`, for example
`au.remotiq.firetone.client.voip`), and paste the key's text. One key serves
every app on the team and does not expire, which is why a key is preferred
to a VoIP Services certificate.

A build signed for development holds a sandbox token; the app reports
`apns.dev` for it and FireTone pushes it through Apple's sandbox host with
the same key. Pushing a sandbox token to the production host is refused
with `BadDeviceToken`, which the Test button explains in words.

## Testing

Each app has a **Test** button: paste a device's token from its extension's
page and one push is sent exactly as a call would send it. The app should
wake; nothing rings. The provider's answer is shown, with the reason when it
refused.

## What happens on a call

1. A call arrives for an extension. FireTone reads the switch's registrations
   for it and the tokens posted for it.
2. Every device that can be woken is pushed, once. Each push is recorded on
   the extension's page under "Recent pushes" with the provider's answer and
   how long it took.
3. FireTone waits for the app to re-register: up to eight seconds when the
   extension has no other device that could ring, three when a desk phone is
   also registered and should not be kept waiting. The wait ends the moment
   the app is back.
4. The extension is rung on every device registered by then. The call's
   journey carries a `push` line saying what was pushed and whether the
   device woke in time.

The app gives up 45 seconds after a push with no call, so all of this fits
comfortably inside its patience.

## Reading a failure

The extension's page shows each push's outcome. "refused" is the provider's
answer, with its reason: a dead token (the app was uninstalled, the token
rotated), the wrong APNs environment, a topic that does not match the app.
"failed" means the provider could not be asked, or no app is set up for the
device's provider and parameter. A push that was accepted but did not wake
the device in time reads "no re-register in time": the app took longer than
the wait, or is not allowed to run in the background on that phone.
