Skip to content

Meta Insights & Analytics

Meta Insights surfaces the analytics that Meta computes for your WhatsApp Business account — messaging volume, conversation pricing, template performance, and calling stats. Whatomate proxies these live from the Meta Graph API and caches the results in Redis so repeated dashboard loads don't re-hit Meta on every view.

GET /api/analytics/meta proxies four different Graph API insight sets, selected with the analytics_type query parameter:

analytics_typeWhat it reports
analyticsMessaging analytics — sent/delivered message volume over the range.
pricing_analyticsPricing analytics — conversation counts and cost broken down by pricing category.
template_analyticsTemplate analytics — per-template sent/delivered/read/clicked. Limited to a 90-day lookback.
call_analyticsCall analytics — WhatsApp Business Calling volume and outcomes.
ParamNotes
analytics_typeRequired. One of the four values above.
start, endRequired. YYYY-MM-DD. end must not be before start.
account_idOptional. A specific WhatsApp account UUID, or omit to query all accounts in the org.
granularityOptional. HALF_HOUR, DAY, or MONTH. Defaults to DAY.
template_idsOptional, template analytics only. JSON array of Meta template IDs.

Use GET /api/analytics/meta/accounts to list the accounts available for analytics (id, name, phone_id).

Whatomate normalizes granularity to what the Graph API will actually accept for the given range, avoiding Meta-side errors:

  • MONTH requires at least 30 days — a shorter range is downgraded to DAY.
  • HALF_HOUR only makes sense for short ranges — anything over 7 days is downgraded to DAY.

When an adjustment happens, the response echoes both adjusted_granularity and original_granularity so the UI can explain why the resolution changed.

Results are cached in Redis under the meta:analytics: key prefix. The cache key is scoped per organization, account (or all), analytics type, date range, and granularity — so different views don't collide.

The TTL scales with granularity, since coarser data changes more slowly:

GranularityCache TTL
HALF_HOUR1 hour
DAY3 hours
MONTH6 hours

Each response carries a cached boolean so you can tell a fresh Graph API fetch from a cache hit. To force fresh data, call:

POST /api/analytics/meta/refresh

This clears every cached analytics entry for the current organization (all accounts, types, and ranges). Reading analytics requires analytics:read; the refresh endpoint requires analytics:write.

{
"status": "success",
"data": {
"accounts": [
{
"account_id": "<uuid>",
"account_name": "Support Line",
"data": { /* Meta Graph API analytics payload */ },
"template_names": { "<meta_template_id>": "order_update" }
}
],
"cached": false
}
}

When an account has no data (e.g. no templates for template analytics, or a Graph API error on that one account), its data is null and the other accounts still return — one failing account never sinks the whole request.