Tote developers
All guides

Basics

Push notifications

Registering a device, the shape of every push, and the full list of events each app receives.

Tote sends pushes through Firebase Cloud Messaging (FCM). Each push carries a visible notification (title and body) and a data payload the app uses to decide where to navigate.

Registering the device#

After login, send the FCM token to profile/notification-token for the app's audience — customer, driver or merchant:

{ "token": "fcm-token…", "platform": "android", "device_id": "c3a1…" }
  • platform is android, ios or web.
  • device_id is a stable id for the install. Sending the same device_id again replaces its token, so call this whenever FCM rotates the token.
  • On logout, send the same device_id so the server forgets the device; see Logging out.

The data payload#

{ "type": "order", "event": "status_update", "user": "customer", "order_id": "1042", "status": "out_for_delivery" }
  • Every value is a string, including ids — FCM only allows strings. Parse ids before using them.
  • type says which kind of record the push is about; event says what happened.
  • user is the audience the push was built for: customer, driver or merchant.
  • The other keys depend on type:
type Extra keys
order order_id; status on status_update
return return_id, order_id, store_id
driver_store store_id, driver_id
friendship friendship_id, sender_id, receiver_id; status on friend_request_answered
store store_id
staff none

Route on type and event, and ignore pairs you do not know: new events are added over time.

Events by app#

Customer app#

type event When
order status_update The order moved to a new status
order order_paid Payment confirmed
order payment_failed A payment attempt failed
return return_opened The store opened a Counter Return the customer must confirm
return return_dropped_off, return_store_responded, return_approved, return_rejected, return_refunded, return_expired The Return moved on
friendship friend_request, friend_request_answered Friend requests

Merchant app (the store team)#

Store pushes go to the owner and every active staff member of the store.

type event When
order new_order An order was placed
order order_paid The order's payment was confirmed
order order_cancelled The order was cancelled
return return_opened, return_confirmed, return_approved, return_rejected A Return needs attention or was decided
driver_store join_request, invitation_accepted, invitation_rejected A driver asked to join, or answered an invitation
store store_forced_offline, store_back_online Tote took the store offline for a while, or brought it back
staff staff_deactivated Sent to a staff member whose account the owner just deactivated

Driver app#

type event When
order order_assigned A store assigned an order to the driver
order order_cancelled An order assigned to the driver was cancelled
driver_store store_invitation A store invited the driver
driver_store join_request_approved, join_request_rejected A store answered the driver's request

Language#

The title and body are written in the platform's default language, not the device's: the server does not store a language preference per account yet. If the app needs its own wording, build it from type, event and the ids.

The notification list#

Every push sent to an account is also kept on the server, so the app can show an inbox — including pushes that arrived while the device was offline or had no FCM token. Each audience has profile/notifications endpoints to list (newest first, paginated), mark as read and delete them: customer, driver, merchant.

{
  "id": "9d2c…",
  "data": { "title": "Order Status Updated", "body": "…", "type": "order", "event": "status_update", "user": "customer", "order_id": 1042, "status": "accepted" },
  "read_at": null,
  "created_at": "2026-10-01T18:30:00.000000Z"
}

data holds the push's title and body next to the same keys as the push payload, so tapping an inbox entry can open the same screen the push would. Unlike the push itself, ids here are numbers. read_at is null until the app marks the entry read.

The staff_deactivated push is the one exception: it is sent to the devices only, because the account is being switched off.