Skip to main content

UC-018 — Manage the Inbox

FieldValue
IDUC-018
GoalManage inbound conversations: triage, reading, archiving
ChannelAll (SMS, RCS, WhatsApp, Messenger)
ComplexityIntermediate
Estimated time15 minutes
APIs involvedGET /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
}
]
Filter for unread

Add &unreadOnly=true to the query to display only conversations with unread messages, useful for triage.

Behind the scenes — How the inbox works
  1. Aggregation: The inbox aggregates messages from all active channels (SMS, RCS, WhatsApp, Messenger) into a single unified view.
  2. Threading: Messages are grouped into conversations based on the contact's phone number. A channel switch from the same number creates a new conversation.
  3. Sorting: Conversations are sorted by last message timestamp (most recent first).
  4. No pagination: the endpoint returns the whole list as a JSON array. Narrow it with unreadOnly=true, channelIds, dateFrom (ISO date) and section, and sort it with orderBy (-lastMessageDate for 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
  1. Archiving: The conversation is moved to the archive and no longer appears in the active list. Messages remain accessible.
  2. Restoring: Use PATCH /conversations/{chatId}/unarchive to bring a conversation back to the active inbox.
  3. New message: If a contact with an archived conversation sends a new message, the conversation is automatically restored to the active inbox.
  4. 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.

One assignee at a time

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
  1. Exclusive ownership: Only one team member can own a conversation at a time. Re-assigning silently replaces the previous owner.
  2. Notifications: The new assignee receives a real-time notification about the assignment and any subsequent inbound messages on that conversation.
  3. Idempotent release: DELETE .../assignee returns 204 whether or not there was a previous assignee, so retry logic is safe.
  4. Cross-company guard: The userId in 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​

StepActionResult
1GET /inbox/conversationsConversation list with preview and unread count
2GET /inbox/conversations/{chatId}/messagesComplete message history of the thread
3PATCH /{chatId}/read + PATCH /{chatId}/archiveConversation read and archived
4POST /{chatId}/assignee + DELETE /{chatId}/assigneeConversation 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​

References​