UC-018 — Gestire l'Inbox
| Campo | Valore |
|---|---|
| ID | UC-018 |
| Obiettivo | Gestire conversazioni in ingresso: triage, lettura, archiviazione |
| Canale | Tutti (SMS, RCS, WhatsApp, Messenger) |
| Complessità | Intermedia |
| Tempo stimato | 15 minuti |
| API coinvolte | 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 |
Scenari reali
- Customer support triage: Il team support di ShopOnline usa l'API per alimentare la propria dashboard interna e assegnare le conversazioni agli operatori disponibili.
- Archiviare risolte: Dopo aver chiuso un ticket, l'operatore archivia la conversazione per mantenere l'inbox pulita e focalizzata sui casi aperti.
- Monitorare non lette: Il supervisore controlla quante conversazioni non lette ci sono per valutare il carico di lavoro del team.
- Distribuzione del carico: Il supervisore elenca i membri del team assegnabili e assegna ogni conversazione a un agente specifico via API; l'agente riceve notifica del nuovo lavoro e l'assegnazione compare nella sua vista personalizzata dell'inbox.
Flusso di gestione inbox
Il diagramma mostra il workflow tipico: lista, lettura, marcatura, archiviazione e, opzionalmente, assegnazione della conversazione a un membro del team.
Prerequisiti
- API Key attiva con permessi di gestione inbox
- Almeno un canale attivo con messaggi in ingresso configurato
- Webhook di ricezione configurato (opzionale, per notifiche real-time)
Step 1 — Recupera le conversazioni attive
Elenca le conversazioni nell'inbox, dalla più recente. Restringi la lista con unreadOnly, channelIds, dateFrom e section.
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations?orderBy=-lastMessageDate" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Lista conversazioni
La risposta è un array JSON, una voce per conversazione:
[
{
"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
}
]
Aggiungi &unreadOnly=true alla query per visualizzare solo le conversazioni con messaggi non letti, utile per il triage.
Dietro le quinte — Come funziona l'inbox
- Aggregazione: L'inbox aggrega messaggi provenienti da tutti i canali attivi (SMS, RCS, WhatsApp, Messenger) in un'unica vista unificata.
- Threading: I messaggi vengono raggruppati in conversazioni basate sul numero di telefono del contatto. Un cambio di canale dallo stesso numero crea una nuova conversazione.
- Ordinamento: Le conversazioni sono ordinate per timestamp dell'ultimo messaggio (più recente prima).
- Nessuna paginazione: l'endpoint restituisce l'intera lista come array JSON. Restringila con
unreadOnly=true,channelIds,dateFrom(data ISO) esection, e ordinala conorderBy(-lastMessageDateper le più recenti prima).
Step 2 — Leggi i messaggi di una conversazione
Recupera lo storico completo dei messaggi di una specifica conversazione. Per thread lunghi, pagina per id messaggio con idFrom e idTo.
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/messages" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Messaggi del thread
{
"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 indica un messaggio scritto dal contatto, false una tua risposta. I timestamp usano il formato yyyy-MM-dd HH:mm:ss.SSSZ.
Step 3 — Marca come letto e archivia
Dopo aver gestito la conversazione, marcala come letta e poi archiviala.
# Marca come letto
curl -X PATCH https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/read \
-H "X-Api-Key: YOUR_API_KEY"
Response — Marcato come letto
204 No Content — il contatore dei non letti della conversazione è ora 0.
# Archivia la conversazione risolta
curl -X PATCH https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/archive \
-H "X-Api-Key: YOUR_API_KEY"
Response — Conversazione archiviata
204 No Content — la conversazione non compare più in GET /inbox/conversations.
Dietro le quinte — Archiviazione e ripristino
- Archiviazione: La conversazione viene spostata nell'archivio e non compare più nella lista attiva. I messaggi restano accessibili.
- Ripristino: Usa
PATCH /conversations/{chatId}/unarchiveper riportare una conversazione nell'inbox attiva. - Nuovo messaggio: Se un contatto con conversazione archiviata invia un nuovo messaggio, la conversazione viene automaticamente ripristinata nell'inbox attiva.
- Retention: Le conversazioni archiviate vengono conservate per 12 mesi, poi spostate nello storico a lungo termine.
Step 4 — Assegna o rilascia una conversazione
Distribuisci il carico dell'inbox assegnando una conversazione a uno specifico membro del team, oppure rilasciala nella coda non assegnata.
Elenca i membri del team assegnabili
curl -X GET https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/assignable-users \
-H "X-Api-Key: YOUR_API_KEY"
Response — Membri del team
[
{
"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
}
]
Assegna la conversazione a un utente
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 — la conversazione è ora di proprietà dell'utente 4521. Una successiva GET /inbox/conversations/98765 mostrerà il nuovo assignedUserId.
Rilascia la conversazione
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 — la conversazione torna senza assegnatario. L'operazione è idempotente: chiamarla su una conversazione già non assegnata restituisce comunque 204.
Ogni conversazione può avere al massimo un assegnatario. Un nuovo POST .../assignee sostituisce la precedente assegnazione senza bisogno di un DELETE esplicito.
Dietro le quinte — Ciclo di vita dell'assegnazione
- Proprietà esclusiva: Solo un membro del team può possedere una conversazione alla volta. Riassegnare sostituisce silenziosamente il proprietario precedente.
- Notifiche: Il nuovo assegnatario riceve una notifica real-time dell'assegnazione e di ogni messaggio in ingresso successivo su quella conversazione.
- Rilascio idempotente:
DELETE .../assigneerestituisce 204 sia che ci fosse un assegnatario precedente sia che non ce ne fosse, quindi la logica di retry è sicura. - Protezione cross-company: Lo
userIdnella richiesta di assegnazione deve appartenere alla tua azienda. Tentativi di assegnare a un utente di un altro account restituiscono 404, non 403, per non rivelare l'esistenza di utenti di altri tenant.
Risultato atteso
| Step | Azione | Risultato |
|---|---|---|
| 1 | GET /inbox/conversations | Lista conversazioni con anteprima e conteggio non lette |
| 2 | GET /inbox/conversations/{chatId}/messages | Storico completo dei messaggi del thread |
| 3 | PATCH /{chatId}/read + PATCH /{chatId}/archive | Conversazione letta e archiviata |
| 4 | POST /{chatId}/assignee + DELETE /{chatId}/assignee | Conversazione assegnata a un collega e poi rilasciata |
Esempio completo end-to-end
Scenario ShopOnline: triage automatico delle conversazioni non lette.
# 1. Recupera conversazioni non lette
echo "=== Conversazioni non lette ==="
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. Leggi i messaggi della prima conversazione
FIRST_CHAT=$(echo "$CHATS" | jq -r '.[0].id')
echo "=== Messaggi di $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. Marca come letto (204, senza corpo)
curl -s -o /dev/null -w "marcata come letta: 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"
Varianti
Ripristinare una conversazione archiviata
Se una conversazione e stata archiviata per errore, ripristinala:
curl -X PATCH https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/61920/unarchive \
-H "X-Api-Key: YOUR_API_KEY"
Errori comuni
404 Not Found — Conversazione inesistente
{
"status": "fail",
"data": {
"conversation": "Conversation not found: chat_invalid_id"
}
}
Soluzione: Verifica che il chatId sia corretto. Usa GET /inbox/conversations per ottenere la lista degli ID validi.
404 Not Found — Utente non appartenente alla tua azienda
{
"status": "fail",
"data": {
"user": "User 9999 does not belong to your company"
}
}
Soluzione: Lo userId che hai tentato di assegnare non esiste nel tuo team. Chiama GET /inbox/conversations/assignable-users per elencare gli ID validi prima di riprovare.
409 Conflict — Conversazione già archiviata
{
"status": "fail",
"data": {
"conversation": "Conversation is already archived"
}
}
Soluzione: La conversazione e già nell'archivio. Se vuoi ripristinarla, usa l'endpoint unarchive.
Prossimi passi
- UC-009 — Conversazione Bidirezionale: Rispondi ai messaggi ricevuti nell'inbox
- UC-019 — Analizzare Storico Messaggi: Analizza lo storico per report e audit
- UC-008 — Tracking Delivery con Webhook: Configura webhook per notifiche real-time