Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt

Use this file to discover all available pages before exploring further.

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:
curl 'https://playground.kaireonai.com/api/v1/interaction-summaries?customerId=cust_42' \
  -H "X-API-Key: krn_your_api_key" \
  -H "X-Tenant-Id: 5a9904b9-..."
Response (abbreviated):
{
  "data": [
    {
      "periodType": "daily",
      "periodKey": "2026-04-30",
      "customerId": "cust_42",
      "offerId": "off_premium_card",
      "creativeId": "crv_email_a",
      "channelId": "ch_email",
      "impressions": 3,
      "positive": 1,
      "negative": 0,
      "neutral": 0,
      "converts": 0,
      "totalValue": 0,
      "lastContactAt": "2026-04-30T14:22:01.123Z",
      "lastOutcomeKey": "click",
      "direction": "outbound"
    }
  ],
  "total": 1
}

How it works

Authentication and roles

The endpoint requires the admin, 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 integer limit 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

customerId
string
required
Customer identifier. Missing this parameter returns 400 customerId is required.
periodType
string
One of daily, weekly, monthly, alltime. When omitted, all period types are returned.
offerId
string
Filter to summaries for one offer.
channelId
string
Filter to summaries for one channel.
limit
number
default:"50"
Maximum rows returned. Clamped to [1, 200] (route.ts:46).

Response

Returned at route.ts:54-73.
data
array
Matching summary rows, ordered by (periodType ASC, periodKey DESC).
total
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.
periodType
string
daily, weekly, monthly, or alltime.
periodKey
string
Period bucket identifier. For daily, YYYY-MM-DD. For weekly, YYYY-Www. For monthly, YYYY-MM. For alltime, the literal "alltime".
customerId
string
offerId
string | null
Present when the summary is per-offer. Null on aggregate-by-channel rows.
creativeId
string | null
channelId
string | null
impressions
number
Count of impression-class outcomes in this period.
positive
number
Count of outcomes whose OutcomeType.classification is "positive".
negative
number
Count of outcomes whose classification is "negative".
neutral
number
Count of outcomes whose classification is "neutral".
converts
number
Count of conversions specifically (typically a subset of positive).
totalValue
number
Sum of conversionValue across all positive outcomes in this period.
lastContactAt
string | null
ISO timestamp of the most recent interaction in this period.
lastOutcomeKey
string | null
OutcomeType.key of the most recent interaction.
direction
string | null
"inbound" or "outbound" — the direction of the most recent interaction.

Status codes

CodeWhenSource
200Returns matching rows (possibly empty)route.ts:54
400Missing customerId query parameterroute.ts:31-33
401Caller is not authenticatedrequireRole
403Caller is not viewer, editor, or adminroute.ts:26
500Unexpected errorroute.ts:75

Required headers

HeaderRequiredRead atPurpose
X-API-KeyYes (one of the two)tenant.ts:97API key (krn_…)
X-Tenant-IdYes (one of the two)tenant.ts:113Direct tenant id

Honest limits

  • The total field counts rows in the current response, not total matching rows. Callers needing a true count must run a separate COUNT(*) query against the underlying table.
  • No cursor pagination. With limit capped at 200, a customer with > 200 daily rows must narrow the query (e.g., add periodType=monthly or offerId=...) to read the rest.
  • The 400 and 500 envelopes use the legacy { title, detail } shape (route.ts:32) rather than the standard apiError envelope from lib/api-error.ts. Callers parsing errors should accept both shapes.