Skip to main content

The Orbit mobile app

Orbit has a native mobile app for iOS and Android. It keeps the Inbox, your voice history, and your voicemail in your pocket so your team can answer customers without being at a desk. This page covers what’s on each of the mobile app’s five tabs and how to get started. For the mobile SDK-bridging surfaces (React Native SDK, CallKit), see the SDKs reference.

Sign in

Sign in with the same work email you use for the web dashboard. If your account belongs to more than one workspace (org), a workspace picker appears; if your account has multi-factor verification enabled, continue your sign-in in a browser instead. The app keeps you signed in between restarts using the OS-level keychain / Keystore.

Tabs

The bottom tab bar is the same on iOS and Android. The order is: Softphone, Meet, Inbox, Calls, More.

Softphone

The Softphone is a working inbound and outbound caller. Connects, dials, mutes, and hangs up like a real phone. The dial box takes E.164 numbers; the softphone is live in real-time until you sign out.

Meet

Meet is the video-room surface — the same meeting rooms you’d create on the dashboard. Anyone you invite via link can join from a browser; the tab lists live and scheduled rooms.

Inbox

The Inbox tab shows the same unified inbox your team sees in the web dashboard — customer conversations, the team-chat channels you belong to, and direct messages, all sorted by activity. Each row shows a channel avatar, the conversation title (a contact’s name, a team channel’s name, or the DM counterparty’s resolved presence name), a text preview, an unread-count badge, and a compact relative timestamp. Unread conversations sort the same way they do on the dashboard’s own Inbox page, so what your desktop colleague sees and what you see match. Tap the Inbox tab’s Refresh button (or pull-to-refresh in the future) to re-poll.

Calls

The Calls tab shows your call history and voicemail as two segments of one screen — to keep the bottom tab count at five instead of adding a sixth.
  • Calls segment. Each row shows direction (inbound / outbound), the caller or destination, duration, and a compact relative timestamp. Missed inbound calls are flagged.
  • Voicemail segment. Your unread voicemail shows a dot marker. When a transcript is available, the first lines show inline. Tap a voicemail row to toggle read/unread. Tapping a voicemail does NOT open an audio player yet — that requires a native media library and is a separate roadmap item.
Both segments read from the same call_logs and voicemails tables the web Voice dashboard uses, and refresh on demand.

More

The More tab is a placeholder whose content lands in a later step (Settings will live there). Until then the app displays a quiet “This surface is coming soon.” surface in the chosen palette. Push-derived links are routed through the navigator only when they pass the app’s deep-link allowlist, and the payload must declare the workspace you’re currently signed into. Meanwhile, the app’s Push notification settings aren’t gated by a separate toggle — a tap on a push notification that points at a valid surface will open the matching tab. The same gate guards the Inbox, Calls, and Meet tabs before the navigator accepts a URL. A URL carried in a push payload or a browser link must satisfy all of these before the app navigates:
  • Match one of three URL shapes: the orbit:// custom scheme, https://orbit.devotel.io/... (the production web app), or https://api.orbit.devotel.io/... (API-hosted link surfaces).
  • Parse as a valid URL with no control characters, no userinfo (user@host), and no explicit port.
  • If it came from a push payload: the payload’s workspace identifier must equal the workspace you’re currently signed into, otherwise the link is rejected with a warning even when the URL itself is allowlisted.
On rejection the app logs a warning and stays on the current screen — no navigation happens. Pseudocode of the gate, in the order the checks run:
Anything URL-like your backend hands a push notification (deep_link, onboarding redirects, in-app “open in app” buttons) must stay inside those origins. Self-hosted dashboards or vanity domains are rejected unless they’re behind the allowlisted hosts.

Register a device for push

The app registers the OS push token against your workspace the moment sign-in completes, and re-registers whenever the OS rotates the token. If you build your own client against Orbit, reproduce this flow with two calls:
  1. Ask the OS for the push token (APNs on iOS, FCM on Android).
  2. POST it to the registration endpoint:
On success the response is 201 with a { data, meta } envelope; data.id is the token’s Orbit identifier, and data.token is the canonical token shape stored on the row (for platform: "web" that’s the base64url-encoded Web Push envelope — reuse it on rotation instead of re-encoding). Registration is idempotent per token: re-POSTing the same token refreshes its last-seen time and owner instead of creating a duplicate row. Pass a stable app_install_id so a reinstall retires the install’s previous token instead of leaving a zombie registration that pushes to nothing. Unregister on sign-out with DELETE /api/v1/push/device-tokens/:id. POST /api/v1/push/register is a backwards-compatible alias with the identical request and response — older SDK clients call it; for new integrations prefer /api/v1/push/device-tokens. To receive inbound-call (VoIP/CallKit) pushes on Android, register the same FCM token a second time against POST /api/v1/push/voip-devices with { "platform": "android", "voip_token": "<fcm-token>" }. The server dispatches to it at high priority with a call-only payload. iOS needs a real PushKit token from native code — don’t register an alert token under the VoIP topic, that registration is rejected.

Construct push payloads that open the right screen

When you send a push (POST /api/v1/push/send or a notification triggered from the dashboard), pass deep_link as a top-level field alongside title and body — a URL inside the allowed origins. Orbit attaches the sender-side workspace automatically; receiving devices only navigate when that workspace matches the one the user is signed into.
The receiving device validates the deep_link against the allowlist above before anything on screen moves — an invalid link still shows the notification, but tapping it does not navigate.