Skip to content

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).

MethodPathPurpose
GET/developers/analytics/summaryVolume, delivery, spend and balance per channel
GET/developers/analytics/messagingFailure reasons, networks, WhatsApp categories, email health, opt-outs, queues
GET/developers/analytics/engagementContacts, groups, campaigns, workflows, chatbots
GET/developers/analytics/developerAPI 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.

ParameterMeaning
fromISO date or date-time. Defaults to the first day of the current month.
toISO 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.

bash
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 ​

ChannelCounted as delivered
SMSThe 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.
WhatsAppMeta reported the message delivered or read.
EmailThe email provider reported it delivered. A message that is sent but not yet confirmed is not counted.
PushFirebase 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

json
{
  "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

json
{
  "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 }]
}
  • failureReasons lists up to 10 reasons per channel, most common first.
  • smsByCarrier works the network out from the number prefix; unknown means no prefix matched. delivered follows the SMS definition above.
  • email.hardBounces counts bounced emails. complaints are only counted for emails sent with one of your API keys, because a complaint is recorded against the key that sent the message.
  • optOuts.inPeriod is entries added in the period; total is the whole list.
  • queues lists messages still waiting to be sent; stuck is those waiting more than 15 minutes.

Engagement ​

GET /developers/analytics/engagement

json
{
  "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.delivered is reported for SMS and WhatsApp campaigns; other channels add nothing to it.
  • workflows counts your workflows and their runs started in the period. Campaign workflows are counted under campaigns, and sandbox runs are left out.
  • chatbots.conversations is conversations started in the period and replies is the bot's replies in them.

Developer ​

GET /developers/analytics/developer

json
{
  "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.sandbox and sends.live count messages across all channels in the period.
  • Only email records which API key sent a message, so sends.byKey lists email only. SMS, WhatsApp and push sends are not attributed to a key.
  • emailSuspended is 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 ​

StatusCause
400Invalid from or to, from not before to, or a range over 2 years
401Missing, invalid or revoked API key
403The key lacks READ_MESSAGES or FULL_ACCESS
429Rate limit exceeded; see Rate limits

Released under the MIT License.