Analytics
Read your own sending, delivery, spend and API usage over an API key. Every figure is for your account only and covers real sends: sandbox sends are never counted and never charged.
All paths are under https://api.msgine.net/api/v1. Each needs the READ_MESSAGES scope (or FULL_ACCESS).
| Method | Path | Purpose |
|---|---|---|
GET | /developers/analytics/summary | Volume, delivery, spend and balance per channel |
GET | /developers/analytics/messaging | Failure reasons, networks, WhatsApp categories, email health, opt-outs, queues |
GET | /developers/analytics/engagement | Contacts, groups, campaigns, workflows, chatbots |
GET | /developers/analytics/developer | API keys, sandbox versus live sends, webhook health |
/developers/analytics/overview and /developers/analytics/daily keep their original response shapes. The new overview is served at summary.
Period
Every endpoint takes the same query parameters.
| Parameter | Meaning |
|---|---|
from | ISO date or date-time. Defaults to the first day of the current month. |
to | ISO date or date-time. A bare date (2026-11-08) includes that whole day. Defaults to now. |
The range can be at most 2 years. Series are daily for ranges up to 92 days and monthly beyond that. Dates are UTC. A malformed date or a range where from is not before to returns 400.
curl "https://api.msgine.net/api/v1/developers/analytics/summary?from=2026-11-01&to=2026-11-08" \
-H "X-Api-Key: YOUR_API_KEY"What "delivered" means
| Channel | Counted as delivered |
|---|---|
| SMS | The provider accepted the message (recipient status sent or delivered). Carrier delivery reports are not reliable, so receipt is not required. Failed and refused recipients are not counted. |
| Meta reported the message delivered or read. | |
| The email provider reported it delivered. A message that is sent but not yet confirmed is not counted. | |
| Push | Firebase accepted the notification for the device. |
deliveryRate is delivered / recipients as a percentage with two decimals (for push, delivered / (delivered + failed)), and null when there is nothing to divide by.
Summary
GET /developers/analytics/summary
{
"currency": "UGX",
"period": { "from": "2026-11-01T00:00:00.000Z", "to": "2026-11-08T00:00:00.001Z", "granularity": "day" },
"channels": {
"sms": { "messages": 12, "recipients": 40, "delivered": 38, "failed": 2, "deliveryRate": 95 },
"whatsapp": { "messages": 3, "recipients": 3, "delivered": 3, "read": 2, "failed": 0, "deliveryRate": 100 },
"email": { "messages": 5, "recipients": 5, "delivered": 4, "bounced": 1, "failed": 1, "deliveryRate": 80 },
"push": { "messages": 1, "recipients": 4, "delivered": 3, "opened": 1, "failed": 1, "deliveryRate": 75 }
},
"spend": {
"total": 65350,
"byChannel": {
"sms": { "total": 300 },
"whatsapp": { "total": 35050, "messages": 50, "numberFees": 35000 },
"email": { "total": 20000, "bundles": 20000, "domainFees": 0 },
"chatbots": { "total": 10000, "botCredits": 10000, "aiCredits": 0 },
"push": { "total": 0 }
}
},
"balance": 1500,
"emailBundleEmailsRemaining": 9000,
"series": [
{ "date": "2026-11-07", "sms": 4, "whatsapp": 1, "email": 2, "push": 0, "spend": 1200 }
]
}The numbers above are illustrative. spend is what you were charged, net of refunds, and matches your billing history. series has one entry per day (or month) in the period, with zeroes where nothing was sent.
Messaging
GET /developers/analytics/messaging
{
"period": { "from": "...", "to": "..." },
"failureReasons": {
"sms": [{ "reason": "Subscriber absent", "count": 1 }],
"whatsapp": [{ "reason": "Template paused", "count": 1 }]
},
"smsByCarrier": [
{ "carrier": "mtn", "recipients": 20, "delivered": 19, "failed": 1, "deliveryRate": 95 }
],
"whatsappByCategory": [{ "category": "marketing", "messages": 1, "recipients": 3 }],
"email": {
"recipients": 3,
"hardBounces": 1,
"complaints": 0,
"bounceRate": 33.33,
"complaintRate": 0
},
"optOuts": [
{ "channel": "sms", "total": 1, "inPeriod": 1 },
{ "channel": "whatsapp", "total": 0, "inPeriod": 0 },
{ "channel": "email", "total": 0, "inPeriod": 0 }
],
"queues": [{ "channel": "sms", "pending": 1, "stuck": 1 }]
}failureReasonslists up to 10 reasons per channel, most common first.smsByCarrierworks the network out from the number prefix;unknownmeans no prefix matched.deliveredfollows the SMS definition above.email.hardBouncescounts bounced emails.complaintsare only counted for emails sent with one of your API keys, because a complaint is recorded against the key that sent the message.optOuts.inPeriodis entries added in the period;totalis the whole list.queueslists messages still waiting to be sent;stuckis those waiting more than 15 minutes.
Engagement
GET /developers/analytics/engagement
{
"period": { "from": "...", "to": "..." },
"contacts": { "total": 2, "addedInPeriod": 2 },
"groups": { "total": 1, "addedInPeriod": 1 },
"campaigns": { "createdInPeriod": 2, "completed": 0, "recipients": 11, "sent": 10, "delivered": 9 },
"workflows": { "active": 1, "paused": 0, "runsStarted": 3, "runsCompleted": 2, "runsFailed": 1 },
"chatbots": { "bots": 1, "conversations": 1, "replies": 2 }
}campaigns.deliveredis reported for SMS and WhatsApp campaigns; other channels add nothing to it.workflowscounts your workflows and their runs started in the period. Campaign workflows are counted undercampaigns, and sandbox runs are left out.chatbots.conversationsis conversations started in the period andrepliesis the bot's replies in them.
Developer
GET /developers/analytics/developer
{
"period": { "from": "...", "to": "..." },
"apiKeys": {
"total": 2, "active": 2, "sandbox": 1, "live": 1, "usedInPeriod": 1, "suspendedForEmail": 1,
"items": [
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"label": "live key",
"environment": "production",
"active": true,
"lastUsedAt": "2026-11-07T09:12:00.000Z",
"createdAt": "2026-10-01T08:00:00.000Z",
"emailSuspended": true,
"bounceCount": 4,
"complaintCount": 0,
"emailSentCount": 120,
"emailsInPeriod": 3
}
]
},
"sends": {
"sandbox": 2,
"live": 7,
"byKey": {
"recordedFor": ["email"],
"email": [{ "keyId": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "label": "live key", "environment": "production", "messages": 3 }],
"emailWithoutKey": 1
}
},
"webhooks": {
"endpoints": 1, "deliveries": 4, "failed": 1, "failureRate": 25,
"items": [
{
"id": "0b2f5a52-9e0a-4f45-8f6c-a2d2c6a3e111",
"url": "https://shop.example.com/hook",
"active": true,
"deliveries": 4,
"failed": 1,
"failureRate": 25,
"lastFailureAt": "2026-11-07T10:00:00.000Z",
"lastFailure": { "httpStatus": 500, "error": "Server error" }
}
]
}
}sends.sandboxandsends.livecount messages across all channels in the period.- Only email records which API key sent a message, so
sends.byKeylists email only. SMS, WhatsApp and push sends are not attributed to a key. emailSuspendedis true when a key's email sending was suspended for a high bounce or complaint rate.- Idempotent replays are not tracked and are not reported.
- Key secrets are never returned.
Errors
| Status | Cause |
|---|---|
400 | Invalid from or to, from not before to, or a range over 2 years |
401 | Missing, invalid or revoked API key |
403 | The key lacks READ_MESSAGES or FULL_ACCESS |
429 | Rate limit exceeded; see Rate limits |