UC-029 — Complete Campaign Report
| Field | Value |
|---|---|
| ID | UC-029 |
| Goal | Create a campaign, monitor its statistics and download the complete CSV report |
| Channel | SMS (applicable to all channels) |
| Complexity | ⭐⭐⭐ Advanced |
| Estimated time | 25 minutes |
| APIs involved | POST /api/partner-gateway/v1/campaigns, PUT /api/partner-gateway/v1/campaigns/{id}/confirm, GET /api/partner-gateway/v1/campaigns/{id}, POST /api/partner-gateway/v1/exports/delivery-reports, GET /api/partner-gateway/v1/exports, GET /api/partner-gateway/v1/exports/{exportId} |
Real-world scenarios
- FashionOutlet — Black Friday ROI analysis: After the Black Friday campaign, the marketing team wants a detailed report with delivery rates by operator, total costs and comparison with the previous year.
- TelcoMobile — Weekly management report: Every Monday, the communications manager generates an aggregate report of the week's campaigns for the executive team.
- BancaAdriatica — Enterprise client delivery audit: The compliance team needs to document the delivery rate of transactional notifications for a quarterly internal audit.
This UC combines the flows from UC-006 — Bulk SMS Campaign and UC-011 — Export Delivery Reports into a complete end-to-end workflow.
Prerequisites
Before you begin, make sure you have:
- Active API Key → How to get one
- Sufficient credit → Check in the Qlara Dashboard
- Contact list + campaign created → See UC-006
The campaign API has no simulation mode, but nothing is sent and no credit is used until you confirm the campaign (Step 2).
Complete flow
The diagram illustrates the complete workflow: from campaign creation to CSV report download for analysis.
Step 1 — Create the campaign
Create a new SMS campaign targeted to a contact list (destinationType: 0). With readyToSend: true the configuration is final and the campaign can be confirmed:
curl -X POST "https://api.qlara.ai/api/partner-gateway/v1/campaigns" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Black Friday 2026 - SMS Promo",
"sendingMode": "SMS",
"destinationType": 0,
"contactListIds": [3415],
"smsSender": "FashionOut",
"smsBody": "Ciao {firstName}! Black Friday FashionOutlet: -50% su tutto solo oggi! Usa il codice BF2026 su https://fashionoutlet.it/bf Rispondi STOP per opt-out.",
"scheduledDate": "2026-11-27 09:00:00.000+0100",
"readyToSend": true
}'
Response — Campaign created
{
"id": 4512,
"name": "Black Friday 2026 - SMS Promo",
"status": "READY_TO_SEND",
"sendingMode": "SMS",
"smsSender": "FashionOut",
"destinationType": 0,
"contactListIds": [3415],
"scheduledDate": "2026-11-27 08:00:00.000+0000",
"readyToSend": true
}
The campaign id field is needed for all subsequent steps: confirmation, monitoring and report linking.
Step 2 — Confirm the campaign send
Nothing is sent until the campaign is confirmed. Confirming prices the campaign and reserves the amount:
curl -X PUT "https://api.qlara.ai/api/partner-gateway/v1/campaigns/4512/confirm" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Campaign confirmed
202 Accepted, with no body: the campaign is queued and is sent at its scheduledDate.
Behind the scenes — What happens after confirmation
- Validation: The system verifies contact list, sender and credit.
- Scheduling: If the
scheduledDateis in the future, the campaign stays in queue. - Throttling: Sending occurs in batches (15,000 SMS in ~30-45 minutes).
- Credit: Charged at actual send time, not at confirmation.
Step 3 — Monitor campaign statistics
During and after sending, check the campaign's delivery counters:
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/campaigns/4512" \
-H "X-Api-Key: YOUR_API_KEY"
Response — Completed campaign statistics
{
"id": 4512,
"name": "Black Friday 2026 - SMS Promo",
"status": "ENDED",
"sendingMode": "SMS",
"startDate": "2026-11-27 08:00:02.000+0000",
"endDate": "2026-11-27 08:38:22.000+0000",
"totalDestinations": 15420,
"totalPrice": 53970000000,
"walletKind": "EURO",
"totalSent": 15420,
"totalSuccess": 14650,
"totalFailed": 458,
"smsSent": 15420,
"smsPending": 312,
"smsSuccess": 14650,
"smsFailed": 458
}
14,650 delivered messages out of 15,420 is a 95.01% delivery rate.
The counters are updated during sending. You can query the endpoint every 30-60 seconds to monitor progress. The status becomes ENDED when the sending is over; delivery reports still on their way are counted in smsPending until they arrive. GET /campaigns/stats returns similar counters for all your campaigns together, aggregated by time bucket (hour, day, week, month) and sending mode: use it for trends, not for a single campaign.
Step 4 — Export the delivery report
Request the CSV export with the detail of every single message. Pass the campaignId alone: it is an alternative to a date range, and the export covers every channel of the campaign:
curl -X POST "https://api.qlara.ai/api/partner-gateway/v1/exports/delivery-reports" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"campaignId": 4512,
"exportFormat": "CSV"
}'
Response — Export queued
202 Accepted, with no body: the export job is queued. The response does not carry the export ID: you find the export in the list of Step 5.
Step 5 — Download the CSV
Poll the export list with GET /exports until the export of your campaign (detail.campaignId) shows isAvailableForDownload: true:
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/exports?page=0&limit=10" \
-H "X-Api-Key: YOUR_API_KEY"
{
"data": [
{
"id": 57,
"type": "DELIVERY_REPORT",
"detail": {
"type": "DELIVERY_REPORT",
"campaignId": 4512,
"exportFormat": "CSV"
},
"status": "COMPLETED",
"createdAt": "2026-11-28 09:00:00.000+0000",
"expiresAt": "2026-12-05 09:00:00.000+0000",
"isAvailableForDownload": true
}
],
"page": 0,
"limit": 10,
"totalCount": 1,
"totalPages": 1
}
Then get the download URL of that export with GET /exports/{exportId}:
curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/exports/57" \
-H "X-Api-Key: YOUR_API_KEY"
{
"url": "https://storage.example.com/exports/57.csv?token=f3a9c1d27b&expires=1796461200"
}
curl -o "report_blackfriday_2026.csv" \
"https://storage.example.com/exports/57.csv?token=f3a9c1d27b&expires=1796461200"
Behind the scenes — CSV structure
The CSV contains one row per message (one per part for a concatenated SMS) with columns such as DateTime, Originator, Destination, Sent type (SMS, RCS or WHATSAPP), Delivery result, SMS cost, Customer ID, Part number and Total parts. The async export is necessary for large datasets.
Expected result
| Step | Action | Result |
|---|---|---|
| 1 | POST /campaigns | Campaign created, status: "READY_TO_SEND" |
| 2 | PUT /campaigns/{id}/confirm | 202 Accepted, delivery scheduled |
| 3 | GET /campaigns/{id} | status: "ENDED", totalSuccess: 14650 out of 15,420 (95.01%) |
| 4 | POST /exports/delivery-reports | 202 Accepted, export queued |
| 5 | GET /exports + GET /exports/{exportId} | Download url of the CSV with 15,420 message details |
Complete end-to-end example
#!/bin/bash
API_KEY="YOUR_API_KEY"
BASE_URL="https://api.qlara.ai/api/partner-gateway/v1"
# 1. Create and confirm campaign
CMP_ID=$(curl -s -X POST "${BASE_URL}/campaigns" \
-H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{"name":"Black Friday 2026","sendingMode":"SMS","destinationType":0,"contactListIds":[3415],"smsSender":"FashionOut","smsBody":"Ciao {firstName}! -50% su tutto! Codice BF2026","readyToSend":true}' | jq -r '.id')
curl -s -X PUT "${BASE_URL}/campaigns/${CMP_ID}/confirm" -H "X-Api-Key: ${API_KEY}" > /dev/null
# 2. Monitor until completion
while true; do
CMP_STATUS=$(curl -s -X GET "${BASE_URL}/campaigns/${CMP_ID}" \
-H "X-Api-Key: ${API_KEY}" | jq -r '.status')
echo "Status: ${CMP_STATUS}"
[[ "$CMP_STATUS" == "ENDED" || "$CMP_STATUS" == "ARCHIVED" ]] && break
sleep 60
done
# 3. Export, wait for the file and download it
curl -s -X POST "${BASE_URL}/exports/delivery-reports" \
-H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{"campaignId":'"${CMP_ID}"',"exportFormat":"CSV"}'
EXP_ID=""
while [[ -z "$EXP_ID" ]]; do
sleep 10
EXP_ID=$(curl -s -X GET "${BASE_URL}/exports?page=0&limit=10" -H "X-Api-Key: ${API_KEY}" \
| jq -r --argjson c "$CMP_ID" '[.data[] | select(.detail.campaignId == $c and .isAvailableForDownload)][0].id // empty')
done
DL_URL=$(curl -s -X GET "${BASE_URL}/exports/${EXP_ID}" -H "X-Api-Key: ${API_KEY}" | jq -r '.url')
curl -o "report_blackfriday_2026.csv" "$DL_URL"
Variants
Multi-channel report
The export request has no channel filter: the export of a multi-channel campaign (for example sendingMode: "WHATSAPP_SMS") includes SMS, RCS and WhatsApp messages in the same CSV, told apart by the Sent type column.
Aggregate report by period
For a weekly report including all campaigns, omit campaignId and specify startDateTime/endDateTime instead (format yyyy-MM-dd HH:mm:ss.SSSZ, for example 2026-11-23 00:00:00.000+0100).
Common errors
404 Not Found — Campaign does not exist
{ "status": "fail", "data": { "error": "Campaign not found" } }
Solution: Verify the campaign ID. Use GET /campaigns to list available campaigns.
400 Bad Request — Campaign and date range together
{ "status": "fail", "data": { "error": "Provide either campaignId or startDateTime/endDateTime, not both" } }
Solution: campaignId and the date range are mutually exclusive: pass campaignId for a single campaign, startDateTime/endDateTime for a period.
Next steps
- UC-006 — Bulk SMS Campaign: Learn more about campaign creation
- UC-011 — Export Delivery Reports: Details on async exports
- UC-007 — Manage Contacts and Lists: Prepare lists for campaigns
- UC-030 — Complete Template Lifecycle: Full WhatsApp template lifecycle