Passa al contenuto principale

UC-018 — Gestire l'Inbox

CampoValore
IDUC-018
ObiettivoGestire conversazioni in ingresso: triage, lettura, archiviazione
CanaleTutti (SMS, RCS, WhatsApp, Messenger)
ComplessitàIntermedia
Tempo stimato15 minuti
API coinvolteGET /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
}
]
Filtra per non lette

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
  1. Aggregazione: L'inbox aggrega messaggi provenienti da tutti i canali attivi (SMS, RCS, WhatsApp, Messenger) in un'unica vista unificata.
  2. 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.
  3. Ordinamento: Le conversazioni sono ordinate per timestamp dell'ultimo messaggio (più recente prima).
  4. Nessuna paginazione: l'endpoint restituisce l'intera lista come array JSON. Restringila con unreadOnly=true, channelIds, dateFrom (data ISO) e section, e ordinala con orderBy (-lastMessageDate per 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
  1. Archiviazione: La conversazione viene spostata nell'archivio e non compare più nella lista attiva. I messaggi restano accessibili.
  2. Ripristino: Usa PATCH /conversations/{chatId}/unarchive per riportare una conversazione nell'inbox attiva.
  3. Nuovo messaggio: Se un contatto con conversazione archiviata invia un nuovo messaggio, la conversazione viene automaticamente ripristinata nell'inbox attiva.
  4. 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.

Un solo assegnatario alla volta

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
  1. Proprietà esclusiva: Solo un membro del team può possedere una conversazione alla volta. Riassegnare sostituisce silenziosamente il proprietario precedente.
  2. Notifiche: Il nuovo assegnatario riceve una notifica real-time dell'assegnazione e di ogni messaggio in ingresso successivo su quella conversazione.
  3. Rilascio idempotente: DELETE .../assignee restituisce 204 sia che ci fosse un assegnatario precedente sia che non ce ne fosse, quindi la logica di retry è sicura.
  4. Protezione cross-company: Lo userId nella 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​

StepAzioneRisultato
1GET /inbox/conversationsLista conversazioni con anteprima e conteggio non lette
2GET /inbox/conversations/{chatId}/messagesStorico completo dei messaggi del thread
3PATCH /{chatId}/read + PATCH /{chatId}/archiveConversazione letta e archiviata
4POST /{chatId}/assignee + DELETE /{chatId}/assigneeConversazione 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​

Riferimenti​