Broadcast Analytics
curl --request GET \
--url https://api.flowiq.live/broadcast-analytics \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.flowiq.live/broadcast-analytics"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.flowiq.live/broadcast-analytics', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.flowiq.live/broadcast-analytics",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.flowiq.live/broadcast-analytics"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.flowiq.live/broadcast-analytics")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.flowiq.live/broadcast-analytics")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"broadcastInfo": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "March Promo",
"templateName": "march_promo_v2",
"createdAt": "2026-03-15T10:00:00Z",
"totalRecipients": 1500
},
"deliveryStats": {
"sent": 1500,
"delivered": 1420,
"read": 890,
"failed": 80,
"deliveryRate": 94.67,
"readRate": 62.68
},
"conversionFunnel": {
"sent": 1500,
"delivered": 1420,
"read": 890,
"buttonClicked": 63,
"optedOut": 4
},
"buttonAnalytics": {
"quickReplyButton": "Shop Now",
"quickReplyClicks": 29,
"quickReplyConversionRate": 3.26
},
"allButtons": [
{
"buttonText": "Shop Now",
"clicks": 29,
"conversionRate": 3.26
},
{
"buttonText": "View More",
"clicks": 23,
"conversionRate": 2.58
},
{
"buttonText": "Not Interested",
"clicks": 11,
"conversionRate": 1.24
}
],
"totalButtonClicks": 63,
"optOutAnalytics": {
"totalOptOuts": 4,
"optOutRate": 0.45
},
"revenue": {
"totalRevenue": 19302,
"currency": "ZAR",
"orderCount": 11,
"avgOrderValue": 1754.73,
"utmCampaign": "31marstockup",
"utmCampaigns": [
{
"campaign": "31marstockup",
"orderCount": 6,
"revenue": 10880,
"avgOrderValue": 1813.33
},
{
"campaign": "31marstockup_vm",
"orderCount": 5,
"revenue": 8422,
"avgOrderValue": 1684.4
}
],
"orders": [
{
"orderName": "#28556",
"totalPrice": 1650,
"currency": "ZAR",
"orderDate": "2026-04-03T07:35:39+02:00",
"financialStatus": "paid",
"utmCampaign": "31marstockup_vm"
}
]
}
}
}Endpoints
Broadcast Analytics
Retrieve broadcast analytics for your organization. Supports two modes:
- Individual broadcast — pass
broadcast_idto get detailed analytics for a single broadcast, including per-button click tracking, opt-out detection, conversion funnel, and revenue attribution. - Overall analytics — pass
start_dateandend_dateto get aggregated analytics across all broadcasts in a date range.
Requirements
- Valid API key with
fiq_prefix - For individual mode: a valid broadcast UUID
- For overall mode: both
start_dateandend_datequery parameters
GET
/
broadcast-analytics
Broadcast Analytics
curl --request GET \
--url https://api.flowiq.live/broadcast-analytics \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.flowiq.live/broadcast-analytics"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.flowiq.live/broadcast-analytics', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.flowiq.live/broadcast-analytics",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.flowiq.live/broadcast-analytics"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.flowiq.live/broadcast-analytics")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.flowiq.live/broadcast-analytics")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"broadcastInfo": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "March Promo",
"templateName": "march_promo_v2",
"createdAt": "2026-03-15T10:00:00Z",
"totalRecipients": 1500
},
"deliveryStats": {
"sent": 1500,
"delivered": 1420,
"read": 890,
"failed": 80,
"deliveryRate": 94.67,
"readRate": 62.68
},
"conversionFunnel": {
"sent": 1500,
"delivered": 1420,
"read": 890,
"buttonClicked": 63,
"optedOut": 4
},
"buttonAnalytics": {
"quickReplyButton": "Shop Now",
"quickReplyClicks": 29,
"quickReplyConversionRate": 3.26
},
"allButtons": [
{
"buttonText": "Shop Now",
"clicks": 29,
"conversionRate": 3.26
},
{
"buttonText": "View More",
"clicks": 23,
"conversionRate": 2.58
},
{
"buttonText": "Not Interested",
"clicks": 11,
"conversionRate": 1.24
}
],
"totalButtonClicks": 63,
"optOutAnalytics": {
"totalOptOuts": 4,
"optOutRate": 0.45
},
"revenue": {
"totalRevenue": 19302,
"currency": "ZAR",
"orderCount": 11,
"avgOrderValue": 1754.73,
"utmCampaign": "31marstockup",
"utmCampaigns": [
{
"campaign": "31marstockup",
"orderCount": 6,
"revenue": 10880,
"avgOrderValue": 1813.33
},
{
"campaign": "31marstockup_vm",
"orderCount": 5,
"revenue": 8422,
"avgOrderValue": 1684.4
}
],
"orders": [
{
"orderName": "#28556",
"totalPrice": 1650,
"currency": "ZAR",
"orderDate": "2026-04-03T07:35:39+02:00",
"financialStatus": "paid",
"utmCampaign": "31marstockup_vm"
}
]
}
}
}Retrieve broadcast analytics for your organization. Supports individual broadcast deep-dives (with per-button tracking, opt-out detection, and revenue attribution) or overall date-range aggregations.
Two Modes
The endpoint supports two query modes based on which parameters you provide:| Mode | Parameters | Description |
|---|---|---|
| Individual | broadcast_id | Detailed analytics for a single broadcast |
| Overall | start_date + end_date | Aggregated analytics across all broadcasts in a date range |
Individual Broadcast Analytics
Pass abroadcast_id to get detailed metrics for a single broadcast, including delivery stats, per-button click tracking, opt-out detection, and Shopify revenue attribution.
curl "https://api.flowiq.live/broadcast-analytics?broadcast_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer fiq_YOUR_API_KEY"
Response Fields
| Field | Description |
|---|---|
broadcastInfo | Broadcast metadata (name, template, date, total recipients) |
deliveryStats | Sent, delivered, read, failed counts with rates |
conversionFunnel | Funnel from sent → delivered → read → button clicked → opted out |
buttonAnalytics | First button stats (backward compatible) |
allButtons | Per-button breakdown with text, clicks, and conversion rate |
totalButtonClicks | Sum of clicks across all buttons |
optOutAnalytics | Total opt-outs and opt-out rate |
revenue | Shopify revenue attributed to this broadcast via UTM tracking |
Per-Button Tracking
All quick reply buttons on the template are tracked individually. The response includes anallButtons array with each button’s text, click count, and conversion rate:
{
"allButtons": [
{ "buttonText": "Shop Now", "clicks": 29, "conversionRate": 3.26 },
{ "buttonText": "View More", "clicks": 23, "conversionRate": 2.58 },
{ "buttonText": "Not Interested", "clicks": 11, "conversionRate": 1.24 }
],
"totalButtonClicks": 63
}
Opt-Out Detection
Opt-outs are detected from customer responses within 48 hours of the broadcast using multilingual keyword matching:| Language | Keywords |
|---|---|
| English | stop, unsubscribe, opt out, optout |
| Portuguese | parar, pare, cancelar |
| Spanish | detener, basta |
| French | arreter, desabonner |
| Italian | fermare, ferma, annullare, annulla, cancellare |
| General | quit, end, cancel |
Revenue Attribution
Revenue is automatically attributed by tracing the broadcast’s shortcode through to Shopify orders:- Broadcast button parameter contains a shortcode
- Shortcode resolves to a redirect URL with a
utm_campaignparameter - Shopify orders with
utm_source=whatsappand a matchingutm_campaignare summed
{
"revenue": {
"totalRevenue": 19302.00,
"currency": "ZAR",
"orderCount": 11,
"avgOrderValue": 1754.73,
"utmCampaign": "31marstockup",
"utmCampaigns": [
{ "campaign": "31marstockup", "orderCount": 6, "revenue": 10880.00, "avgOrderValue": 1813.33 },
{ "campaign": "31marstockup_vm", "orderCount": 5, "revenue": 8422.00, "avgOrderValue": 1684.40 }
],
"orders": [
{ "orderName": "#28556", "totalPrice": 1650.00, "currency": "ZAR", "utmCampaign": "31marstockup_vm", ... }
]
}
}
utmCampaigns array breaks down revenue per UTM variant, so you can see exactly how much came from the main link vs the “View More” button (_vm suffix). Each order also includes its own utmCampaign field.
Revenue attribution also matches UTM campaign suffix variants:
_viewmore, _view_more, and _vm (case-insensitive). This captures orders from “View More” button clicks on the same campaign.Overall Broadcast Analytics
Passstart_date and end_date to get aggregated analytics across all broadcasts in the date range.
curl "https://api.flowiq.live/broadcast-analytics?start_date=2026-03-01&end_date=2026-03-31" \
-H "Authorization: Bearer fiq_YOUR_API_KEY"
Response Fields
| Field | Description |
|---|---|
overallStats | Aggregated totals: sent, delivered, read, failed, rates, campaign count |
campaignPerformance | Per-campaign breakdown with delivery stats |
timeSeriesData | Daily breakdown of sent/delivered/read/failed |
templateAnalytics | Per-template aggregated stats and usage counts |
errorAnalysis | Ranked error codes with counts and percentages |
POST Method
The same analytics are also available via POST, with parameters in the request body instead of query string:curl -X POST "https://api.flowiq.live/broadcast-analytics" \
-H "Authorization: Bearer fiq_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "broadcast_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }'
curl -X POST "https://api.flowiq.live/broadcast-analytics" \
-H "Authorization: Bearer fiq_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "start_date": "2026-03-01", "end_date": "2026-03-31" }'
Integration Example
async function getBroadcastAnalytics(apiKey, broadcastId) {
const response = await fetch(
`https://api.flowiq.live/broadcast-analytics?broadcast_id=${broadcastId}`,
{
headers: {
Authorization: `Bearer ${apiKey}`,
},
}
);
const data = await response.json();
if (!response.ok) throw new Error(data.message || data.error);
return data;
}
const result = await getBroadcastAnalytics(
"fiq_YOUR_API_KEY",
"a1b2c3d4-e5f6-7890-abcd-ef1234567890"
);
console.log(`Delivered: ${result.data.deliveryStats.delivered}`);
console.log(`Revenue: ${result.data.revenue.totalRevenue}`);
console.log(`Buttons: ${result.data.allButtons.length} tracked`);
Revenue Attribution
Revenue attribution requires your broadcast template to use a FlowIQ shortcode link (via URL redirects) with a
utm_campaign parameter, and your Shopify store to be connected. Orders are matched where utm_source=whatsapp in the landing site URL.Authorizations
Bearer token for authentication. Format: Bearer fiq_YOUR_API_KEY
Query Parameters
UUID of a specific broadcast to get detailed analytics for. If provided, start_date and end_date are ignored.
Start date for overall analytics (inclusive). Required when broadcast_id is not provided.
End date for overall analytics (inclusive). Required when broadcast_id is not provided.

