Device Token
Data Entity
Description
Stores FCM/APNs push notification tokens registered by user devices. Each record ties a physical device to a user account, enabling targeted push delivery. Tokens are platform-specific, rotate on app reinstall or OS token refresh, and must be deregistered on logout to prevent ghost deliveries.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key — surrogate UUID generated server-side on token registration | PKrequiredunique |
user_id |
uuid |
Foreign key to users table — owner of the device | required |
token |
string |
Raw FCM registration token (Android) or APNs device token (iOS) as returned by the platform SDK. Max 4096 chars to accommodate FCM v1 token lengths. | requiredunique |
platform |
enum |
Mobile OS platform that generated this token | required |
app_version |
string |
Semantic version of the Meander app at time of registration (e.g. '1.4.2'). Used to detect stale tokens from very old app versions. | - |
device_model |
string |
Device model string reported by Flutter (e.g. 'iPhone 15 Pro', 'Pixel 8'). Used for diagnostics and analytics only, never for routing logic. | - |
os_version |
string |
Operating system version string (e.g. 'iOS 17.4', 'Android 14'). Helps identify platform-specific FCM/APNs issues. | - |
is_active |
boolean |
Whether this token should receive push notifications. Set to false on logout, token rotation, or delivery failure (InvalidRegistration / NotRegistered errors from gateway). | required |
last_used_at |
datetime |
Timestamp of the most recent successful push delivery to this token. Used by cleanup jobs to identify and deactivate abandoned tokens. | - |
failure_count |
integer |
Consecutive delivery failure count. Auto-incremented by push-notification-gateway on each failed delivery attempt. Token deactivated when threshold exceeded. | required |
registered_at |
datetime |
Timestamp when the token was first registered. Set on INSERT, never updated. | required |
deregistered_at |
datetime |
Timestamp when the token was explicitly deregistered (logout, token refresh). NULL while active. | - |
Database Indexes
idx_device_tokens_token_unique
Columns: token
idx_device_tokens_user_id
Columns: user_id
idx_device_tokens_user_active
Columns: user_id, is_active
idx_device_tokens_platform_active
Columns: platform, is_active
idx_device_tokens_last_used_at
Columns: last_used_at
Validation Rules
token_not_empty
error
Validation failed
platform_enum_valid
error
Validation failed
user_id_exists
error
Validation failed
token_max_length
error
Validation failed
app_version_semver_format
warning
Validation failed
failure_count_non_negative
error
Validation failed
Business Rules
one_active_token_per_device
A single physical device token value must be globally unique. If a token arrives that already exists for a different user_id (device transferred or shared), the old record must be deactivated before the new registration is saved. The unique index on `token` enforces this at the DB level; device-token-registration-service handles the upsert logic.
deactivate_on_logout
When a user logs out from the mobile app, all device tokens for that user on that device must be immediately set to is_active=false and deregistered_at recorded. This prevents push notifications being delivered to a device after the user has signed out.
auto_deactivate_on_gateway_rejection
When push-notification-gateway receives an InvalidRegistration or NotRegistered error from FCM/APNs for a specific token, it must immediately set that token's is_active=false and increment failure_count. This prevents repeated failed deliveries and wasted API calls.
failure_threshold_deactivation
If failure_count reaches 5 for any token, the token is permanently deactivated (is_active=false) regardless of error type. This guards against transient gateway errors accumulating and marking healthy tokens as dead. Threshold configurable via environment variable.
notification_only_to_active_tokens
push-notification-service must query device_tokens WHERE user_id = ? AND is_active = true before dispatching any push. Sending to inactive tokens is prohibited at the service layer, not just the gateway layer.
token_rotation_upsert
Flutter's Firebase Messaging SDK may refresh the FCM token without user action. The app must send the new token to device-token-registration-service on each app foreground. The service performs an upsert: if the user already has an active token on this platform, update the token value rather than creating a duplicate record.
multi_device_support
A single user may have multiple active tokens (one per device they are logged in on). All active tokens for the user must receive push notifications — there is no 'primary device' concept. push-notification-service must fan out to all active tokens for the target user.