Build an in-app inbox
Section: DOC-CP-notifications-inbox#build-an-in-app-inbox.
Use this recipe for a notification center where a person can see a new coaching update, read it, open its conversation and clear it without losing the underlying message history.
Build the REST inbox first: load the first feed page, show the unseen badge, mark messages as the person reads them, and offer archive/restore. Add the live connection after those actions work. A socket outage then affects freshness without preventing the person from opening or managing their inbox.
Before starting, configure an in-app workflow and authenticate the user. Send an example notification so the completed inbox has an entry to display.
1. Show the user’s current notifications
Section: DOC-CP-notifications-inbox#get-inbox-feed.
Load page 1 after sign-in and render each entry with its text and supported actions. Keep both message and notification identifiers: later actions use different identifiers. Offer another page while hasMore is true; use the archive filter for a separate archive view.
Request example: Get inbox feed messages · Request fields.
{
"page": 1,
"pageSize": 20
}
Use the feed filters and pagination contract when adding the archive view:
Request example: Get inbox feed messages · Request fields.
{
"page": 1,
"pageSize": 20,
"filter": {"archived": true}
}
Keep the live and archive views distinct; see archive filtering.
Response:
Response example: Get inbox feed messages · Response fields.
{
"messages": [
{
"messageId": "69c0df5391079c0a4596f7d3",
"notificationId": "69c0df5291079c0a4596f79b",
"title": "Weekly Check-in",
"body": "Time to review your progress.",
"data": {
"title": "Weekly Check-in",
"body": "Time to review your progress.",
"deep_link": "socayo://screen?name=weekly"
},
"deepLink": "socayo://screen?name=weekly",
"status": "MESSAGE_STATUS_UNSEEN",
"createdAt": "2026-03-23T06:36:03.578Z"
}
],
"totalCount": 6,
"page": 1,
"pageSize": 20
}
Render missing optional values using the feed presence rules.
2. Add a badge without loading every message
Section: DOC-CP-notifications-inbox#get-unseen-count.
Use this count for the app’s inbox badge. It is a bounded indicator, not the number of rows you must download. Refresh it after read/archive changes and when a live update arrives.
Response:
Response example: Get unseen and unread inbox counts · Response fields.
{
"unseenCount": 3,
"unreadCount": 5
}
Use the bounded badge-count contract to choose the badge label.
3. Keep read and seen state in sync with the user
Section: DOC-CP-notifications-inbox#mark-messages.
Mark a message seen when your app shows it and read when the user reads it, according to your interface’s meaning. Send the returned message identifier; use the all-message operation only for an explicit action whose scope your interface explains.
Mark a single message:
Request example: Mark a single inbox message · Request fields.
{
"messageId": "msg_123",
"markAs": "MESSAGE_STATUS_READ"
}
Response:
Response example: Mark a single inbox message · Response fields.
{
"messages": [
{
"messageId": "msg_123",
"channel": "in_app",
"seen": true,
"read": true,
"content": "Time to review your progress.",
"status": "sent",
"createdAt": "2026-03-23T06:36:03.578Z",
"lastSeenDate": "2026-03-23T10:24:35.518Z"
}
]
}
Confirm the returned read state before treating the entry as read. An absent value means false; HTTP success or a seen flag alone is not that confirmation.
Mark all messages:
Request example: Mark all inbox messages · Request fields.
{
"markAs": "MESSAGE_STATUS_SEEN"
}
Response:
Response example: Mark all inbox messages · Response fields.
{
"updatedCount": 5
}
Choose the read/seen status that matches the user action.
4. Let the user clear and restore a notification
Section: DOC-CP-notifications-inbox#archive-messages.
Archiving hides a message from the default feed but keeps it — unlike
delete-inbox-message, it is reversible.
Use the notification identifier from the selected feed entry for archive/restore; identifier rules distinguish it from a message identifier.
Request example: Archive an inbox message · Request fields.
{
"notificationId": "69c0df5291079c0a4596f79b"
}
Response:
Response example: Archive an inbox message · Response fields.
{
"status": {
"acknowledged": true,
"status": "archived"
}
}
Restore it with the mirror call:
Request example: Unarchive an inbox message · Request fields.
{
"notificationId": "69c0df5291079c0a4596f79b"
}
Response: the same envelope, with status: "unarchived".
After an uncertain archive or restore, refresh the entry and apply only the user’s current choice. See archive outcomes.
Variant: clear all read notifications
Section: DOC-CP-notifications-inbox#archive-in-bulk.
Clear the whole inbox, or just the messages the user has already read — the second is what a "clear read" button should call.
Request example: Archive all inbox messages · Request fields.
{}
Response:
Response example: Archive all inbox messages · Response fields.
{
"archivedCount": 12
}
Request example: Archive all read inbox messages · Request fields.
{}
Response:
Response example: Archive all read inbox messages · Response fields.
{
"archivedCount": 7
}
To clear one group of notices, use the tag filter:
Request example: Archive all read inbox messages · Request fields.
{
"tags": ["coaching", "reminders"]
}
Variant: permanently remove an inbox entry
Section: DOC-CP-notifications-inbox#delete-inbox-message.
Use deletion only when the user chooses to remove this inbox entry permanently. Prefer archive for ordinary “clear” behavior because it can be undone. Refresh the feed after deletion; the action does not delete the conversation or undo any business action represented by the notice.
Request example: Delete an inbox message · Request fields.
{
"messageId": "msg_123"
}
Response:
Response example: Delete an inbox message · Response fields.
{
"status": {
"acknowledged": true,
"status": "deleted"
}
}
5. Connect a notification action to its business result
Section: DOC-CP-notifications-inbox#message-actions.
For an “open conversation” or other action, validate the destination and the current user’s access. If the action performs business work, complete that work first, then acknowledge the inbox action. Keep its operation reference so a lost inbox update cannot cause the work to run twice.
Render only the actions supplied by the selected notice; see the action payload contract:
Message excerpt: Inbox message fields.
{
"actions": [
{
"actionId": "log-workout",
"label": "Log it",
"url": "travila://workout/new",
"isPrimary": true,
"completed": false
}
]
}
When the user taps an action, perform its authorized business operation first. After that operation reports success, mark the inbox action complete so other devices can show the result. An inbox acknowledgment neither performs nor rolls back the business operation; refresh the inbox after a lost acknowledgment; do not repeat the business operation just to update the inbox.
Request example: Complete an inbox message action · Request fields.
{
"notificationId": "69c0df5291079c0a4596f79b",
"actionType": "ACTION_TYPE_PRIMARY"
}
Response:
Response example: Complete an inbox message action · Response fields.
{
"status": {
"acknowledged": true,
"status": "completed"
}
}
Offer reversal only for the inbox completion marker. Business compensation is a separate user action; see action states:
Request example: Revert an inbox message action · Request fields.
{
"notificationId": "69c0df5291079c0a4596f79b",
"actionType": "ACTION_TYPE_PRIMARY"
}
Response: the same envelope, with status: "reverted" and the action back in its
pending state.
6. Obtain a session for live updates
Section: DOC-CP-notifications-inbox#get-inbox-session.
Get a session token and WebSocket URL for real-time inbox updates.
Request example: Get an inbox session token · Request fields.
{}
Response:
Response example: Get an inbox session token · Response fields.
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"socketUrl": "wss://inbox.travila.ai",
"expiresIn": "1296000"
}
Use the returned connection details following the session contract.
7. Refresh the inbox as messages arrive
Section: DOC-CP-notifications-inbox#real-time-inbox-updates.
Connect with the supported client and refresh the feed and badge when an update arrives. The session and event reference defines connection fields and event names.
Resilience. If the socket fails to connect or drops, reconnect with exponential backoff (a handful of attempts), and while disconnected fall back to polling get-inbox-unseen-count every ~30 seconds so the badge stays roughly current. Use the returned expiresIn to schedule renewal and call get-inbox-session again after expiry. A socket/session failure need not prevent using the authenticated REST feed.
Finish the interaction and clear the inbox on sign-out
Section: DOC-CP-notifications-inbox#actions-and-sign-out.
Mark an inbox action complete only after its underlying business operation succeeds. Reading a message or opening a deep link is not proof the requested action ran. Mark-all operations can affect more than the visible page; label their scope clearly. Validate deep links against an allowlist, reauthenticate on cold start, and require explicit user intent before sending a prompt or executing an action. Clear private feed state and close the socket on sign-out.
Document ID: DOC-CP-notifications-inbox. Section identities and revisions.