Skip to main content

Three token types

The push system uses three distinct token types. Understanding which is which will save you debugging time at 2am.
The updateToken is NOT stored in the devices table. It lives in live-activities because it’s scoped to a specific match + user + device combination. If you’re debugging why a LA isn’t updating, check live-activities, not devices.

Multi-device support

A user can be signed in on multiple iOS devices simultaneously. Each device registers independently. The devices table uses deviceId (identifierForVendor) as the sort key — not the push token. This means:
  • Each device gets its own record under the same userId
  • Token rotation (OS updates, reinstalls) updates the existing record in-place
  • No single-device enforcement — all devices receive pushes
identifierForVendor resets on app reinstall. When a user reinstalls, the old device record becomes orphaned. The 90-day TTL on lastSeenAt handles eventual cleanup, but there’s a window where both old and new records exist.

Devices table schema

Registration flow on app launch

Every app launch triggers a registration call. This keeps tokens fresh and metadata current.
1

Request permissions

On first launch, iOS shows the notification permission prompt. On subsequent launches, the app checks existing permission status.
2

Get device pushToken

If permissions are granted, iOS registers with APNs and returns the device push token. This token can change after OS updates.
3

POST /devices

The app sends pushToken, deviceId, osVersion, and appVersion. The backend upserts the device record and refreshes lastSeenAt and ttl.
4

Observe pushToStartToken

The app starts observing Activity<ScorecardAttributes>.pushToStartTokenUpdates. When a token arrives, it’s sent via POST /devices/push-to-start-token.

Unregistration on sign-out

When the user signs out:
  1. iOS calls DELETE /devices/:deviceId
  2. Backend removes the device record from the devices table
  3. Any active live-activities records for this device are NOT automatically cleaned up (they’ll expire via TTL or get caught by the Orchestrator’s recovery loop)
  4. iOS clears all local state

Failure modes

Key endpoints