Sales and results
The question every business asks after a few weeks: is this making me money? Mark a conversation as a sale when a customer pays, and the Results page adds it up alongside the conversations and messages that led there.
Mark a conversation as a sale
In the inbox, open the conversation and press Mark as sold. Enter the amount, choose the currency and, if it helps, a note - “3 bags, paid by transfer”. Any member of the workspace can, and any member can remove one marked by mistake.
POST /v1/conversations/{conversationId}/sales
{
"amount": "25000.50",
"currency": "NGN",
"note": "3 bags, paid by transfer"
}| Field | Type | Notes |
|---|---|---|
amount | string | In the main unit - naira, not kobo. Commas are fine. At most two decimal places. |
currency | string | NGN, GHS, KES, ZAR, EGP, USD, GBP or EUR. NGN when left out. |
note | string | Optional, up to 500 characters. |
GET on the same path lists a conversation's sales, newest first, and DELETE /v1/sales/{saleId} removes one.
Sales after a follow-up
If a workflow's message reached the customer in the 7 days before the sale, the sale is credited to that follow-up, and the response names the workflow:
"followUp": { "workflowId": "…", "workflowName": "Follow up quiet customers" }Timing, not proof
A customer who got a reminder on Monday and paid on Wednesday may have paid anyway. The credit says the follow-up was part of the conversation, not that it made the sale. It is still the best signal there is of whether your workflows are worth keeping.
Results
The Results page, or GET /v1/stats?from=…&to=…, counts over a range you choose. Both are instants with an offset; from is included and to is not, and the range can be a year at most. The dashboard sends whole days in your own time zone.
| Field | Type | Notes |
|---|---|---|
conversationsStarted | number | Conversations opened in the range. |
messagesReceived | number | Messages from customers. |
sentByTeam | number | Messages people sent from the inbox. Sends that failed are left out. |
sentByWorkflows | number | Workflow messages WhatsApp accepted. |
sentByCampaigns | number | Campaign messages WhatsApp accepted. |
sales | object | count, and amounts per currency - different currencies are never added together. |
salesAfterFollowUp | object | The same, for sales credited to a follow-up. |
One workflow
curl "https://api.engentra.io/v1/workflows/{id}/stats?from=2026-09-01T00:00:00%2B01:00&to=2026-10-01T00:00:00%2B01:00" -H "X-API-Key: $ENGAGE_API_KEY"The same range rules, counted over the runs that started in the range - so a run that began on the last day and sends next week still belongs to this range. The workflow's page in the dashboard shows these under Results.
| Field | Type | Notes |
|---|---|---|
started | number | Runs that started. |
completed, stopped, failed, inProgress | number | How those runs ended, or that they have not yet. Stopped includes stop-early rules. |
messagesSent | number | Messages those runs sent that WhatsApp accepted. |
replied | number | Runs whose customer wrote back within 7 days of the run starting - by timing, like sales. |
sales | object | Sales credited to this workflow: count, and amounts per currency. |
Which door produced the sale
curl "https://api.engentra.io/v1/stats/doors?from=2026-09-01T00:00:00%2B01:00&to=2026-10-01T00:00:00%2B01:00" -H "X-API-Key: $ENGAGE_API_KEY"A business cannot message first on Instagram. That rule does not bend, so the only thing left to work on is where the door is: an ig.me link on the packaging, one in an email, a QR on a poster, an ad that clicks straight to Direct. Meta tells us which one was used — once, on the message that opens the thread — and this is what that becomes.
response
[
{
"ref": "packaging-qr",
"adId": null,
"adTitle": null,
"source": "SHORTLINK",
"conversations": 31,
"customers": 29,
"sales": { "count": 4, "amounts": { "NGN": "72000.00" } }
},
{
"ref": null,
"adId": "239847",
"adTitle": "Ankara sale - October",
"source": "ADS",
"conversations": 9,
"customers": 9,
"sales": { "count": 3, "amounts": { "NGN": "54000.00" } }
}
]Conversations are counted from when the thread started and sales from when the money arrived. A door can show sales in a month it started nothing — it did its work earlier and somebody paid later. Counting the sale back to the month of the click would quietly rewrite last month's numbers every time a customer returned.
Threads that came through no door are not a row. An “(other)” line would be the largest number on a page whose only purpose is comparing links, and there is nothing to choose about traffic that arrived from nowhere in particular.
Naming a door
An ig.me link opens a Direct thread with your account, and a ref on it is the name you will see above. Build one under Settings › Channels, which fills in your username for you:
https://ig.me/m/adafabrics?ref=packaging-qrMeta drops a ref it does not like
Letters, numbers and - . : _ + / = only. A ref with a space or an accent in it is discarded at Meta's end — the link still works, the thread still opens, and every conversation it starts is untraceable, with nothing anywhere saying why. The builder in the dashboard checks this before you print it on anything.
An ad needs no setup. A click-to-Direct ad carries its own ad_id, and its title where Meta sends one, so it appears here on its own.
Each door also raises a conversation.referred event on the customer when the thread opens, carrying ref, source, adId and adTitle. A workflow can trigger on it — “somebody came from the October ad” — like any other event.
Two more Instagram events exist for the same reason: conversation.story_reply when somebody answers one of your stories, and conversation.story_mention when they put you in one of theirs. Both are also written into the message text so the inbox and the AI can read them, but a workflow should trigger on the event rather than matching a prefix inside free text.
Next
Workflows →
The follow-ups that get the credit.