Build an in-app inbox
Section ID: Guide/jbi4ogh3pk#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 ID: Guide/jbi4ogh3pk#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.
See Get inbox feed messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
page: number 1.pageSize: number 20.
Use the feed filters and pagination contract when adding the archive view:
Request example: Get inbox feed messages · Request fields.
See Get inbox feed messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
page: number 1.pageSize: number 20.filter.archived: boolean true.
Keep the live and archive views distinct; see archive filtering.
Response:
Response example: Get inbox feed messages · Response fields.
See Get inbox feed messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
messages[0].messageId: text 69c0df5391079c0a4596f7d3.messages[0].notificationId: text 69c0df5291079c0a4596f79b.messages[0].title: text Weekly Check-in.messages[0].body: text Time to review your progress..messages[0].data.title: text Weekly Check-in.messages[0].data.body: text Time to review your progress..messages[0].data.deep_link: text socayo://screen?name=weekly.messages[0].deepLink: text socayo://screen?name=weekly.messages[0].status: text MESSAGE_STATUS_UNSEEN.messages[0].createdAt: text 2026-03-23T06:36:03.578Z.totalCount: number 6.page: number 1.pageSize: number 20.
Render missing optional values using the feed presence rules.
2. Add a badge without loading every message
Section ID: Guide/jbi4ogh3pk#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.
See Get unseen and unread inbox counts for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
unseenCount: number 3.unreadCount: number 5.
Use the bounded badge-count contract to choose the badge label.
3. Keep read and seen state in sync with the user
Section ID: Guide/jbi4ogh3pk#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.
See Mark a single inbox message for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
messageId: text msg_123.markAs: text MESSAGE_STATUS_READ.
Response:
Response example: Mark a single inbox message · Response fields.
See Mark a single inbox message for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
messages[0].messageId: text msg_123.messages[0].channel: text in_app.messages[0].seen: boolean true.messages[0].read: boolean true.messages[0].content: text Time to review your progress..messages[0].status: text sent.messages[0].createdAt: text 2026-03-23T06:36:03.578Z.messages[0].lastSeenDate: text 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.
See Mark all inbox messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
markAs: text MESSAGE_STATUS_SEEN.
Response:
Response example: Mark all inbox messages · Response fields.
See Mark all inbox messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
updatedCount: number 5.
Choose the read/seen status that matches the user action.
4. Let the user clear and restore a notification
Section ID: Guide/jbi4ogh3pk#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.
See Archive an inbox message for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
notificationId: text 69c0df5291079c0a4596f79b.
Response:
Response example: Archive an inbox message · Response fields.
See Archive an inbox message for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
status.acknowledged: boolean true.status.status: text archived.
Restore it with the mirror call:
Request example: Unarchive an inbox message · Request fields.
See Unarchive an inbox message for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
notificationId: text 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 ID: Guide/jbi4ogh3pk#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.
See Archive all inbox messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
(root): empty object.
Response:
Response example: Archive all inbox messages · Response fields.
See Archive all inbox messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
archivedCount: number 12.
Request example: Archive all read inbox messages · Request fields.
See Archive all read inbox messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
(root): empty object.
Response:
Response example: Archive all read inbox messages · Response fields.
See Archive all read inbox messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
archivedCount: number 7.
To clear one group of notices, use the tag filter:
Request example: Archive all read inbox messages · Request fields.
See Archive all read inbox messages for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
tags[0]: text coaching.tags[1]: text reminders.
Variant: permanently remove an inbox entry
Section ID: Guide/jbi4ogh3pk#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.
See Delete an inbox message for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
messageId: text msg_123.
Response:
Response example: Delete an inbox message · Response fields.
See Delete an inbox message for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
status.acknowledged: boolean true.status.status: text deleted.
5. Connect a notification action to its business result
Section ID: Guide/jbi4ogh3pk#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.
See InboxMessage for the full field contract. This excerpt illustrates the relevant choices; replace identifiers with your own authorized values.
actions[0].actionId: text log-workout.actions[0].label: text Log it.actions[0].url: text travila://workout/new.actions[0].isPrimary: boolean true.actions[0].completed: boolean 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.
See Complete an inbox message action for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
notificationId: text 69c0df5291079c0a4596f79b.actionType: text ACTION_TYPE_PRIMARY.
Response:
Response example: Complete an inbox message action · Response fields.
See Complete an inbox message action for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
status.acknowledged: boolean true.status.status: text 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.
See Revert an inbox message action for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
notificationId: text 69c0df5291079c0a4596f79b.actionType: text 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 ID: Guide/jbi4ogh3pk#get-inbox-session
Get a session token and WebSocket URL for real-time inbox updates.
Request example: Get an inbox session token · Request fields.
See Get an inbox session token for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
(root): empty object.
Response:
Response example: Get an inbox session token · Response fields.
See Get an inbox session token for the request and response contract. The following values illustrate the choices for this example; replace identifiers with your own authorized values.
token: text eyJhbGciOiJIUzI1NiIs....socketUrl: text wss://inbox.travila.ai.expiresIn: text 1296000.
Use the returned connection details following the session contract.
7. Refresh the inbox as messages arrive
Section ID: Guide/jbi4ogh3pk#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 ID: Guide/jbi4ogh3pk#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: Guide/jbi4ogh3pk