GET /api/v1/interaction-summaries reads rows from the materialized interaction-summary store — the aggregate that powers contact-policy enforcement, behavioral-metric values, and frequency-cap evaluation. The endpoint is intended for verification, debugging, and integration tests; production read paths use the cached read helpers directly.
What it does
Each materialized row aggregates raw interaction events for one(customerId, offerId | creativeId | channelId, periodType, periodKey) tuple. The endpoint accepts query filters and returns the matching rows with the count fields (impressions, positive, negative, neutral, converts, totalValue) and last-contact metadata. customerId is required — there is no all-customers list mode.
For the related materialized-aggregate writer and decay/retention rules, see Interaction Summary.
Quick start
Read all summaries for a customer across every period type:How it works
Authentication and roles
The endpoint requires theadmin, editor, or viewer role and a tenant header. All reads are scoped by tenantId. Cross-tenant reads are not possible.
Filter composition
The endpoint composes a filter from query parameters.tenantId and customerId are always required. Optional periodType, offerId, and channelId filters are AND-ed onto the base clause when supplied. Results are ordered by (periodType ASC, periodKey DESC) so the most recent period in each type appears first.
Pagination
A single integerlimit clamps results to 200 maximum. Cursor pagination is not supported — for large result sets, narrow the query with periodType or offerId filters.
Reference
Query Parameters
string
required
Customer identifier. Missing this parameter returns
400 customerId is required.string
One of
daily, weekly, monthly, alltime. When omitted, all period types are returned.string
Filter to summaries for one offer.
string
Filter to summaries for one channel.
number
default:"50"
Maximum rows returned. Clamped to
[1, 200] (route.ts:46).Response
Returned atroute.ts:54-73.
array
Matching summary rows, ordered by
(periodType ASC, periodKey DESC).number
Length of
data after the limit was applied. Not the total matching count — there is no separate count query.data[] per-item shape
Built at route.ts:55-71.
string
daily, weekly, monthly, or alltime.string
Period bucket identifier. For
daily, YYYY-MM-DD. For weekly, YYYY-Www. For monthly, YYYY-MM. For alltime, the literal "alltime".string
string | null
Present when the summary is per-offer. Null on aggregate-by-channel rows.
string | null
string | null
number
Count of impression-class outcomes in this period.
number
Count of outcomes whose
OutcomeType.classification is "positive".number
Count of outcomes whose classification is
"negative".number
Count of outcomes whose classification is
"neutral".number
Count of conversions specifically (typically a subset of
positive).number
Sum of
conversionValue across all positive outcomes in this period.string | null
ISO timestamp of the most recent interaction in this period.
string | null
OutcomeType.key of the most recent interaction.string | null
"inbound" or "outbound" — the direction of the most recent interaction.Status codes
Required headers
Honest limits
- The
totalfield counts rows in the current response, not total matching rows. Callers needing a true count must run a separateCOUNT(*)query against the underlying table. - No cursor pagination. With
limitcapped at 200, a customer with > 200 daily rows must narrow the query (e.g., addperiodType=monthlyorofferId=...) to read the rest. - The
400and500envelopes use the legacy{ title, detail }shape (route.ts:32) rather than the standardapiErrorenvelope fromlib/api-error.ts. Callers parsing errors should accept both shapes.
Related
- Interaction Summary — the materialized-aggregate writer + decay/retention helpers.
- Interaction History — the raw event-by-event read surface.
- Contact Policies — frequency-cap and suppression rules that consume these summaries.
- Behavioral Metrics — derived metrics that aggregate from this table.