UC-009 — Two-Way Conversation
| Field | Value |
|---|---|
| ID | UC-009 |
| Goal | Handle two-way conversations with customers |
| Channel | WhatsApp / RCS |
| Complexity | ⭐⭐⭐ Advanced |
| Estimated time | 20 minutes |
| APIs involved | POST /api/partner-gateway/v1/webhooks/delivery-status, GET /api/partner-gateway/v1/inbox/conversations, GET /api/partner-gateway/v1/inbox/conversations/{chatId}/messages, POST /api/partner-gateway/v1/inbox/conversations/reply, PATCH /api/partner-gateway/v1/inbox/conversations/{chatId}/read, PATCH /api/partner-gateway/v1/inbox/conversations/{chatId}/archive |
Real-world scenarios
- TechStore — Customer support chat: A customer writes on WhatsApp to ask about an order; the system receives the message, assigns it to an operator and replies with the shipping status.
- Studio Dentistico Bianchi — Booking confirmation by reply: After an appointment reminder is sent, the patient confirms or asks to move the visit by replying to the message.
- FashionOutlet — Post-sale feedback collection: A post-purchase survey asks the customer to reply with a score from 1 to 5; the system collects and catalogs the replies automatically.
Prerequisites
Before you begin, make sure you have:
- Active API Key → How to get one
- Sufficient credit → Check in the Qlara Dashboard
- Webhook configured + WhatsApp number or RCS agent
Interaction flow
Step-by-step guide
Step 1 — Configure the webhook for inbound messages
First, register a webhook to receive incoming messages. Despite its name, the delivery-status webhook receives every event of your account: DELIVERY, READ and INBOUND. See the Webhooks guide for the full configuration.
curl -X POST "https://api.qlara.ai/api/partner-gateway/v1/webhooks/delivery-status" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://api.techstore.it/webhooks/qlara"
}'
Response: 201 Created
{
"companyId": 189,
"callbackUrl": "https://api.techstore.it/webhooks/qlara"
}
Each account has a single callback URL, shared by SMS, RCS and WhatsApp. If a webhook is already configured, the call answers 409 Conflict and leaves the existing one in place: check it with GET /webhooks/delivery-status and change the URL with PUT on the same path. The URL must be HTTPS on port 443 or 8443 and publicly reachable, otherwise the call answers 400.
Step 2 — Receive an inbound message
When a customer sends a message, your endpoint receives a payload like this one. INBOUND events exist for RCS and WhatsApp only.
Inbound TEXT:
{
"eventType": "INBOUND",
"channel": "WHATSAPP",
"messageId": "msg-wa-in-001",
"source": "+393471234567",
"destination": "+393209998877",
"receivedDate": "2026-04-09T11:15:22+02:00",
"messageType": "TEXT",
"text": "Buongiorno, vorrei sapere lo stato del mio ordine ORD-2026-1584"
}
Inbound IMAGE:
{
"eventType": "INBOUND",
"channel": "WHATSAPP",
"messageId": "msg-wa-in-002",
"source": "+393471234567",
"destination": "+393209998877",
"receivedDate": "2026-04-09T11:16:05+02:00",
"messageType": "IMAGE",
"mediaKey": "b394ab72/efd4987c/39b4879f6fed01f0d622453be1488c93"
}
| Field | Description |
|---|---|
eventType | Always INBOUND for a message written by the end user |
channel | WHATSAPP or RCS |
messageId | Identifier of the message in the conversation |
source | The end user's phone number |
destination | Your WhatsApp number (WhatsApp) or your agent id (RCS) |
receivedDate | When the message was received. INBOUND events have no eventDate |
messageType | TEXT for text messages; media messages (e.g. IMAGE) carry a mediaKey |
text | Text of the message |
mediaKey | Key of the attachment, present for media messages |
Null fields are left out of the payload, so a text message has no mediaKey.
To download an attached media file, use GET /api/files/{mediaKey}?expireMinutes=180. The response contains a temporary pre-signed URL. See UC-026: Download Media from Inbound Messages.
Step 3 — List the conversations
Retrieve the conversations, keeping only those with unread messages:
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations?unreadOnly=true" \
-H "X-Api-Key: YOUR_API_KEY"
Response:
[
{
"id": 1042,
"hasUnreadMessages": true,
"userName": "Marco Bianchi",
"phoneNumber": "+393471234567",
"unreadCount": 2,
"lastMessageText": "Buongiorno, vorrei sapere lo stato del mio ordine ORD-2026-1584",
"lastMessageDate": "2026-04-09 09:15:22.041+0000",
"socialMedia": "WHATSAPP",
"companyProfileName": "TechStore Italia",
"companyPhoneNumber": "+393209998877",
"whatsappWindowExpire": "2026-04-10 09:15:22.041+0000"
}
]
Step 4 — Get the conversation messages
Retrieve the full thread of a conversation:
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/1042/messages" \
-H "X-Api-Key: YOUR_API_KEY"
Response:
{
"profile": {
"userName": "Marco Bianchi",
"profilePicture": null,
"additionalInfo": "+393471234567"
},
"messages": [
{
"id": 5001,
"text": "Buongiorno, vorrei sapere lo stato del mio ordine ORD-2026-1584",
"isRead": false,
"isUserReply": true,
"createdAt": "2026-04-09 09:15:22.041+0000",
"attachments": [],
"status": "DELIVERED"
}
]
}
isUserReply: true marks a message written by the contact, false one of your replies. A chatId that does not exist or does not belong to your account is answered 404.
Step 5 — Reply to the conversation
Send a reply within the existing conversation:
curl -X POST "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/reply" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"chatId": 1042,
"message": "Buongiorno Marco! Il suo ordine ORD-2026-1584 è in fase di spedizione. Riceverà il tracking entro oggi pomeriggio."
}'
Response: 202 Accepted
chatId is required. You can also send replyTo (the id of the message you are quoting) and attachments (a list of {"mediaId": …} from your media library). The reply goes out on the conversation's channel; track its delivery through the DELIVERY webhook event.
On WhatsApp, free-form replies are only possible within 24 hours of the customer's last message. Check the whatsappWindowExpire field of the conversation. Outside the window the reply can still be accepted (202) but is not delivered: the DELIVERY event carries statusCode: 12 (conversation closed). After the window expires, use a Meta-approved template. See the WhatsApp guide.
Step 6 — Mark as read and archive
Once you have handled the request, mark the conversation as read:
curl -X PATCH "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/1042/read" \
-H "X-Api-Key: YOUR_API_KEY"
Response: 204 No Content
When the conversation is over, archive it:
curl -X PATCH "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/1042/archive" \
-H "X-Api-Key: YOUR_API_KEY"
Response: 204 No Content
An archived conversation can be restored at any time with PATCH /conversations/{chatId}/unarchive. If the contact writes again, the conversation typically comes back to the active inbox on its own.
Conversation filter parameters
| Parameter | Type | Description |
|---|---|---|
channelIds | string | Comma-separated channel IDs (e.g. WHATSAPP-123,RCS-456) |
dateFrom | string | ISO date: only conversations updated after this date |
orderBy | string | Sort field with a +/- prefix (e.g. -lastMessageDate) |
section | string | Inbox section, by assignment state (e.g. only conversations assigned to you, or unassigned ones) |
unreadOnly | boolean | If true, returns only conversations with unread messages |
Message pagination
For long threads, use the idFrom (message id greater than or equal to) and idTo (message id less than or equal to) parameters:
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/inbox/conversations/1042/messages?idFrom=5000&idTo=5050" \
-H "X-Api-Key: YOUR_API_KEY"
Behind the scenes
When a customer sends a message, the carrier (Meta for WhatsApp, Google for RCS) delivers it to the Qlara platform, which:
- Creates or updates the conversation -- If the contact already has an open conversation on the same channel, the message is added to the existing thread. Otherwise a new one is created.
- Notifies you via webhook -- The
INBOUNDpayload is sent to your registered callback URL. Your server must answer with any2xxwithin 5 seconds. Failed deliveries are retried, so the same event can arrive more than once: deduplicate oneventType+messageId. - Manages the window -- On WhatsApp, the platform automatically moves the 24-hour window expiry forward each time the customer writes.
- Routes the reply -- When you send a reply with
POST /conversations/reply, the platform automatically sends it on the same channel as the original conversation.
Expected result
| Aspect | Detail |
|---|---|
| Action completed | Inbound message received, reply sent, conversation archived |
| Channel used | WhatsApp / RCS |
| Delivery confirmation | Via webhook (statusCode: 3) within 5-60 sec |
Common errors
| Problem | Probable cause | Solution |
|---|---|---|
HTTP 401 | Missing or invalid API Key | Check X-Api-Key header |
HTTP 409 on POST /webhooks/delivery-status | A webhook is already configured for the account | Check it with GET /webhooks/delivery-status; change the URL with PUT on the same path |
HTTP 400 on POST /webhooks/delivery-status | The callback URL is not a public HTTPS URL on port 443 or 8443 | Use a publicly reachable HTTPS URL, without credentials or fragment |
HTTP 400 on reply | Malformed body or missing chatId | Send chatId (required) and message |
HTTP 404 — Conversation not found | The chatId does not exist or does not belong to your account | Get a valid chatId from GET /inbox/conversations |
| Reply accepted but not delivered | WhatsApp 24h window has closed (DELIVERY event with statusCode: 12) | Check whatsappWindowExpire; use a Meta-approved template instead |
| No INBOUND webhook received | Webhook not configured, or endpoint unreachable or not answering 2xx within 5 seconds | Verify the registration with GET /webhooks/delivery-status and check your endpoint |
Next steps
- Webhooks Guide -- Full webhook configuration
- WhatsApp API -- Send WhatsApp messages and templates
- RCS API -- Send rich messages with cards and carousels
- UC-018: Manage the Inbox -- Triage, assign and archive conversations
- UC-013: WhatsApp Template Workflow -- When the 24h window expires, use templates