UC-018 — Manage the Inbox
| Field | Value |
|---|---|
| ID | UC-018 |
| Goal | Manage inbound conversations: triage, reading, archiving |
| Channel | All (SMS, RCS, WhatsApp, Messenger) |
| Complexity | Intermediate |
| Estimated time | 15 minutes |
| APIs involved | GET /api/partner-gateway/v1/inbox/conversations, GET /api/partner-gateway/v1/inbox/conversations/{chatId}, GET /api/partner-gateway/v1/inbox/conversations/{chatId}/messages, PATCH /api/partner-gateway/v1/inbox/conversations/{chatId}/archive, PATCH /api/partner-gateway/v1/inbox/conversations/{chatId}/unarchive, PATCH /api/partner-gateway/v1/inbox/conversations/{chatId}/read, POST /api/partner-gateway/v1/inbox/conversations/{chatId}/assignee, DELETE /api/partner-gateway/v1/inbox/conversations/{chatId}/assignee, GET /api/partner-gateway/v1/inbox/conversations/assignable-users |
Real-world scenarios
- Customer support triage: The ShopOnline support team uses the API to feed their internal dashboard and assign conversations to available operators.
- Archive resolved conversations: After closing a ticket, the operator archives the conversation to keep the inbox clean and focused on open cases.
- Monitor unread messages: The supervisor checks how many unread conversations there are to assess team workload.
- Workload distribution: The supervisor lists assignable team members and assigns each conversation to a specific agent via API; the agent is notified of the new work and the assignment shows up in their personalised inbox view.
Inbox management flow
The diagram shows the typical workflow: listing, reading, marking, archiving, and optionally assigning the conversation to a teammate.
Prerequisites
- Active API Key with inbox management permissions
- At least one active channel with inbound messages configured
- Receiving webhook configured (optional, for real-time notifications)
Step 1 — Retrieve active conversations
List the conversations in the inbox, newest first. Narrow the list with unreadOnly, channelIds, dateFrom and section.
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations?orderBy=-lastMessageDate" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Conversation list
The response is a JSON array, one entry per conversation:
[
{
"id": 61920,
"companyId": 189,
"hasUnreadMessages": true,
"lastCheckedAt": "2026-04-09 12:10:00.000+0000",
"userName": "Marco Rossi",
"phoneNumber": "+393471234567",
"profilePicture": null,
"unreadCount": 3,
"lastMessageText": "Buongiorno, vorrei informazioni sulla spedizione del mio ordine",
"lastMessageDate": "2026-04-09 12:30:00.000+0000",
"socialMedia": "whatsapp",
"companyProfileName": "ShopOnline",
"companyProfilePicture": null,
"companyProfileId": "17841473473831061",
"companyPhoneNumber": "+390212345678",
"companyPhoneNumberType": null,
"whatsappWindowExpire": "2026-04-10 12:30:00.000+0000",
"assignedUserId": null
},
{
"id": 61918,
"companyId": 189,
"hasUnreadMessages": true,
"lastCheckedAt": "2026-04-09 11:00:00.000+0000",
"userName": "giulia.bianchi",
"phoneNumber": null,
"profilePicture": "https://storage.example.com/profiles/giulia.jpg",
"unreadCount": 1,
"lastMessageText": "Ciao, avete ancora la borsa in vetrina?",
"lastMessageDate": "2026-04-09 11:45:00.000+0000",
"socialMedia": "instagram",
"companyProfileName": "shoponline_official",
"companyProfilePicture": null,
"companyProfileId": "17841473473831061",
"companyPhoneNumber": null,
"companyPhoneNumberType": null,
"whatsappWindowExpire": null,
"assignedUserId": 4521
}
]
Add &unreadOnly=true to the query to display only conversations with unread messages, useful for triage.
Behind the scenes — How the inbox works
- Aggregation: The inbox aggregates messages from all active channels (SMS, RCS, WhatsApp, Messenger) into a single unified view.
- Threading: Messages are grouped into conversations based on the contact's phone number. A channel switch from the same number creates a new conversation.
- Sorting: Conversations are sorted by last message timestamp (most recent first).
- No pagination: the endpoint returns the whole list as a JSON array. Narrow it with
unreadOnly=true,channelIds,dateFrom(ISO date) andsection, and sort it withorderBy(-lastMessageDatefor newest first).
Step 2 — Read messages in a conversation
Retrieve the complete message history of a specific conversation. For long threads, paginate by message id with idFrom and idTo.
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/messages" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Thread messages
{
"profile": {
"userName": "Marco Rossi",
"profilePicture": null,
"additionalInfo": "+393471234567"
},
"messages": [
{
"id": 27514,
"text": "Buongiorno, vorrei informazioni sulla spedizione del mio ordine",
"template": null,
"isUnsupported": false,
"isRead": false,
"replyMessage": null,
"reaction": null,
"createdAt": "2026-04-09 12:25:00.000+0000",
"attachments": [],
"isUserReply": true,
"status": "RECEIVED",
"isEdited": false
},
{
"id": 27515,
"text": "Il numero ordine e #ORD-20260405",
"template": null,
"isUnsupported": false,
"isRead": false,
"replyMessage": null,
"reaction": null,
"createdAt": "2026-04-09 12:26:00.000+0000",
"attachments": [],
"isUserReply": true,
"status": "RECEIVED",
"isEdited": false
},
{
"id": 27516,
"text": "Potete aiutarmi?",
"template": null,
"isUnsupported": false,
"isRead": false,
"replyMessage": null,
"reaction": null,
"createdAt": "2026-04-09 12:30:00.000+0000",
"attachments": [],
"isUserReply": true,
"status": "RECEIVED",
"isEdited": false
}
]
}
isUserReply: true marks a message written by the contact, false one of your replies. Timestamps use the yyyy-MM-dd HH:mm:ss.SSSZ format.
Step 3 — Mark as read and archive
After handling the conversation, mark it as read and then archive it.
# Mark as read
curl -X PATCH https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/read \
-H "X-Api-Key: YOUR_API_KEY"
Response — Marked as read
204 No Content — the unread counter of the conversation is now 0.
# Archive the resolved conversation
curl -X PATCH https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/archive \
-H "X-Api-Key: YOUR_API_KEY"
Response — Conversation archived
204 No Content — the conversation no longer appears in GET /inbox/conversations.
Behind the scenes — Archiving and restoring
- Archiving: The conversation is moved to the archive and no longer appears in the active list. Messages remain accessible.
- Restoring: Use
PATCH /conversations/{chatId}/unarchiveto bring a conversation back to the active inbox. - New message: If a contact with an archived conversation sends a new message, the conversation is automatically restored to the active inbox.
- Retention: Archived conversations are kept for 12 months, then moved to long-term storage.
Step 4 — Assign or release a conversation
Distribute the inbox workload by assigning a conversation to a specific team member, or release it back to the unassigned queue.
List assignable team members
curl -X GET https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/assignable-users \
-H "X-Api-Key: YOUR_API_KEY"
Response — Team members
[
{
"id": 4521,
"fullName": "Anna Bianchi",
"profilePicture": "https://cdn.example.com/users/4521/avatar.png"
},
{
"id": 4522,
"fullName": "Luca Verdi",
"profilePicture": "https://cdn.example.com/users/4522/avatar.png"
},
{
"id": 4523,
"fullName": "Marco Rossi",
"profilePicture": null
}
]
Assign the conversation to a user
curl -X POST https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/98765/assignee \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": 4521}'
Response
204 No Content — the conversation is now owned by user 4521. A subsequent GET /inbox/conversations/98765 will reflect the new assignedUserId.
Release the conversation
curl -X DELETE https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/98765/assignee \
-H "X-Api-Key: YOUR_API_KEY"
Response
204 No Content — the conversation is back to unassigned. The operation is idempotent: calling it on an already-unassigned conversation still returns 204.
Each conversation can have at most one assignee. A new POST .../assignee replaces the previous assignment without requiring an explicit DELETE.
Behind the scenes — Assignment lifecycle
- Exclusive ownership: Only one team member can own a conversation at a time. Re-assigning silently replaces the previous owner.
- Notifications: The new assignee receives a real-time notification about the assignment and any subsequent inbound messages on that conversation.
- Idempotent release:
DELETE .../assigneereturns 204 whether or not there was a previous assignee, so retry logic is safe. - Cross-company guard: The
userIdin the assign request must belong to your company. Attempts to assign to a user from another account return 404, not 403, to avoid leaking user existence across tenants.
Expected result
| Step | Action | Result |
|---|---|---|
| 1 | GET /inbox/conversations | Conversation list with preview and unread count |
| 2 | GET /inbox/conversations/{chatId}/messages | Complete message history of the thread |
| 3 | PATCH /{chatId}/read + PATCH /{chatId}/archive | Conversation read and archived |
| 4 | POST /{chatId}/assignee + DELETE /{chatId}/assignee | Conversation assigned to a teammate and later released |
Complete end-to-end example
Scenario ShopOnline: automatic triage of unread conversations.
# 1. Retrieve unread conversations
echo "=== Unread Conversations ==="
CHATS=$(curl -s -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations?unreadOnly=true&orderBy=-lastMessageDate" \
-H "X-Api-Key: YOUR_API_KEY")
echo "$CHATS" | jq '.[] | {id, channel: .socialMedia, contact: .userName, unread: .unreadCount}'
# 2. Read messages of the first conversation
FIRST_CHAT=$(echo "$CHATS" | jq -r '.[0].id')
echo "=== Messages for $FIRST_CHAT ==="
curl -s -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/${FIRST_CHAT}/messages" \
-H "X-Api-Key: YOUR_API_KEY" | jq '.messages[] | {isUserReply, text, createdAt}'
# 3. Mark as read (204, no body)
curl -s -o /dev/null -w "marked as read: HTTP %{http_code}\n" -X PATCH "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/${FIRST_CHAT}/read" \
-H "X-Api-Key: YOUR_API_KEY"
Variants
Restore an archived conversation
If a conversation was archived by mistake, restore it:
curl -X PATCH https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/unarchive \
-H "X-Api-Key: YOUR_API_KEY"
Common errors
404 Not Found — Conversation not found
{
"status": "fail",
"data": {
"conversation": "Conversation not found: chat_invalid_id"
}
}
Solution: Verify that the chatId is correct. Use GET /inbox/conversations to get the list of valid IDs.
404 Not Found — User not in your company
{
"status": "fail",
"data": {
"user": "User 9999 does not belong to your company"
}
}
Solution: The userId you tried to assign does not exist in your team. Call GET /inbox/conversations/assignable-users to list the valid IDs before retrying.
409 Conflict — Conversation already archived
{
"status": "fail",
"data": {
"conversation": "Conversation is already archived"
}
}
Solution: The conversation is already in the archive. If you want to restore it, use the unarchive endpoint.
Next steps
- UC-009 — Two-Way Conversation: Reply to messages received in the inbox
- UC-019 — Analyze Message History: Analyze history for reports and audits
- UC-008 — Delivery Tracking with Webhooks: Configure webhooks for real-time notifications