Skip to main content
Use the analytics query endpoint to report on journeys and campaigns over a selected period. The same request shapes are available through the MCP query_analytics tool.

Before you start

Create an API client v2 with analytics.query and the datasets you need: Use Find entity IDs to resolve campaign or journey names. Replace the example UUIDs below with IDs from your workspace. The catalog lists query shapes available to your client.

Attributed revenue and estimated ROAS

Send this JSON body to POST /platform-api/v1/analytics/query:
For a journey, replace campaign_ids with journey_ids. To compare selected campaigns and journeys, provide both arrays. Attribution returns either selected entity type, further restricted by entity_types if provided. An empty ID array imposes no restriction; each array accepts up to 100 UUIDs. An example row in rows:
These illustrative values use INR 91 in estimated messaging cost. roas: 10 means ten units of attributed revenue per unit of estimated spend; it is a ratio, not a percentage.

Attribution settings and pagination

  • Omit filters.attribution_type and filters.window_hours to use your workspace attribution settings. Override the event with delivery, read, or clicked, and the lookback with 1–8760 hours. The lookback is separate from the reporting period.
  • meta.applied_filters reports the effective attribution event, lookback, and entity types even when no rows match.
  • Use the exact dimensions ["time_bucket", "entity_type", "entity_id"]. Choose day, week, or month; order_created_at is the supported time anchor.
  • Request attribution_revenue, estimated_roas, or both. The existing attribution_cost measure can also be requested. Only requested measures are returned.
  • Results use a fixed order by time bucket, entity type, and entity ID. Custom sort and summary are unsupported. Follow meta.cursor with the same query until it is null. The default and maximum page size is 1000.
Selecting one entity does not reassign orders that were attributed elsewhere. Attribution selects the winning eligible interaction before applying campaign/journey ID filters.

How to interpret estimated ROAS

Estimated ROAS divides attributed revenue by estimated messaging cost in the same currency. The existing estimate covers delivered/read WhatsApp marketing messages at INR 0.91 and utility messages at INR 0.15. Authentication messages and other channels are outside this estimate. These are estimation inputs, not actual invoiced charges or a pricing quotation. No currency conversion is performed. Keep currency breakdowns separate. Revenue uses gross attributed order totals and includes cancelled/refunded orders; it is not net revenue. Do not average daily ROAS values to calculate a period’s ROAS. Where matching revenue and cost are available, sum each in the same currency and divide those totals. Revenue buckets use order creation time; spend buckets use message send time. A reporting period can therefore include spend for orders attributed in another period. Campaign and journey spend can overlap, so do not add their costs together as a workspace total.

Delivery by template category

Send this body to the same query endpoint:
An example row in rows:
Choose one of these dimension lists, in the order shown. time_bucket is added to each response row automatically; do not include it in these request dimensions. Campaign/journey groupings include only messages linked to that entity type. Category-only grouping also includes messages without campaign or journey IDs. You can request any nonempty subset of the five measures in the example. sent_count counts messages whose current status is sent, delivered, or read. delivered_like_count counts delivered or read; read_count counts read; failed_count counts failed. delivery_rate is delivered_like_count / sent_count: 0–1, so 0.8 means 80%. A zero denominator returns 0. Failed messages are reported separately and are not in this denominator. For a combined delivery rate, divide the summed counts; do not average the row rates.

Category and filter rules

  • Categories are MARKETING, UTILITY, AUTHENTICATION, and UNKNOWN. They reflect the current WhatsApp template category, so reclassification can change historical reports.
  • Missing/deleted templates and non-WhatsApp messages use UNKNOWN. For a WhatsApp-only report, add channel_kinds: ["whatsapp"] to filters.
  • template_categories filters categories and requires one of the category groupings above. Category values are case-sensitive.
  • campaign_ids, journey_ids, has_campaign, has_journey, channel_ids, and channel_kinds combine with AND on message rows. With both ID arrays supplied, each message must match both.
  • Results contain observed buckets only, with no empty-date/category rows added. summary and cursors are unsupported. Omit limit to receive all matching buckets; a limit truncates the result without a next page.

Time windows and errors

Use ISO 8601 timestamps with an explicit offset. Timestamps without an offset are treated as UTC. time.timezone controls day/week/month grouping; it does not reinterpret start/end offsets. Revenue and category delivery include both start and end timestamps. Estimated cost includes the start and excludes the end. Category delivery uses send time, falling back to message creation time for messages not yet sent. These boundaries and different event times matter when reconciling reports. Marketing attribution and category queries reject reversed windows, unknown timezones, aggregation, and business_hours. Unsupported or misspelled filters return an error rather than an unfiltered total. A missing scope/dataset grant returns 403; invalid combinations return 400, and malformed fields return 422. For MCP examples and the distinction between date-bounded analytics and lifetime product overviews, see Journey and campaign tools.