POST /api/v1/respond
Records the outcome of a recommendation delivered via the Recommend API. Outcomes feed into behavioral metrics, adaptive model training, experiment analysis, and attribution.
Request Body
{
"customerId": "CUST001",
"creativeId": "creative_001",
"outcome": "click",
"idempotencyKey": "click-cust001-offer001-1710590400",
"interactionId": "550e8400-e29b-41d4-a716-446655440000",
"offerId": "offer_001",
"channelId": "channel_email",
"placementId": "placement_hero",
"rank": 1,
"context": {
"source": "email_campaign_q1",
"utm_medium": "email"
},
"outcomeDetails": {
"product_sku": "SKU-12345"
},
"timestamp": "2026-03-16T14:35:00.000Z",
"conversionValue": 149.99,
"attributes": {
"device": "mobile",
"browser": "safari"
}
}
Field Reference
| Field | Type | Required | Description |
|---|
customerId | string | Yes | The customer who interacted with the recommendation |
creativeId | string | Yes* | The Creative that was shown. Alias: treatmentId (deprecated) |
outcome | string | Yes* | The outcome type key (e.g., "impression", "click", "convert", "dismiss"). Must match a registered Outcome Type. Alias: interactionType |
idempotencyKey | string | Yes | Unique key to prevent double-counting. Can also be sent as the Idempotency-Key HTTP header |
interactionId | string | No | The interactionId returned by the Recommend API, used to link the outcome to the original decision |
offerId | string | No | Offer ID. If omitted, resolved automatically from the creative’s parent offer |
channelId | string | No | Channel ID. If omitted, resolved from the creative |
placementId | string | No | Placement ID. If omitted, resolved from the creative |
rank | integer | No | The position at which the offer was displayed (1-indexed) |
context | object | No | Arbitrary context metadata (campaign source, UTM params, etc.) |
outcomeDetails | object | No | Structured details about the outcome (e.g., product SKU, plan selected) |
timestamp | string | No | ISO 8601 timestamp of the interaction. Defaults to the current server time |
conversionValue | number | No | Monetary value of the conversion (used for attribution and revenue reporting) |
attributes | object | No | Additional attributes (device, browser, etc.). Used for online model learning |
channel | string | No | Channel name string (stored in context) |
placement | string | No | Placement name string (stored in context) |
responseTime | number | No | Time in milliseconds the customer took to respond |
deviceType | string | No | Device type string (stored in context) |
Fields marked Yes* require at least one of the aliased pair. You must provide either creativeId or treatmentId, and either outcome or interactionType.
Idempotency
Every call to the Respond API requires an idempotency key to prevent duplicate outcome recording. You can provide it in two ways:
- Request body:
"idempotencyKey": "unique-key-here"
- HTTP header:
Idempotency-Key: unique-key-here
If both are provided, the body value takes precedence.
When a duplicate key is detected, the API returns the original record with "status": "already_recorded" and a 200 status code (not 201).
Response: New Record (201)
{
"id": "ih_abc123",
"interactionId": "550e8400-e29b-41d4-a716-446655440000",
"offerId": "offer_001",
"outcomeType": "click",
"classification": "positive",
"status": "recorded",
"timestamp": "2026-03-16T14:35:00.000Z"
}
Response: Duplicate (200)
When the same idempotencyKey is sent again:
{
"id": "ih_abc123",
"interactionId": "550e8400-e29b-41d4-a716-446655440000",
"offerId": "offer_001",
"outcomeType": "click",
"status": "already_recorded",
"timestamp": "2026-03-16T14:35:00.000Z"
}
Outcome Types
Outcomes must be registered in the platform before they can be recorded. Each outcome type has a classification that drives downstream behavior:
| Classification | Effect | Examples |
|---|
positive | Increments positive counters, triggers attribution, updates adaptive models with reward | click, accept, convert, purchase |
negative | Increments negative counters, updates models with penalty | dismiss, not_interested, unsubscribe |
neutral | Tracked but does not affect scoring | impression, view, requested_info |
Register outcome types in Studio > Outcome Types or via POST /api/v1/outcome-types.
Examples
Record an Impression
curl -X POST https://playground.kaireonai.com/api/v1/respond \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: my-tenant" \
-H "Authorization: Bearer sk_live_abc123" \
-d '{
"customerId": "CUST001",
"creativeId": "creative_001",
"outcome": "impression",
"idempotencyKey": "imp-cust001-creative001-20260316-1430",
"interactionId": "550e8400-e29b-41d4-a716-446655440000",
"rank": 1
}'
Record a Click
curl -X POST https://playground.kaireonai.com/api/v1/respond \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: my-tenant" \
-H "Authorization: Bearer sk_live_abc123" \
-d '{
"customerId": "CUST001",
"creativeId": "creative_001",
"outcome": "click",
"idempotencyKey": "click-cust001-creative001-20260316-1435",
"interactionId": "550e8400-e29b-41d4-a716-446655440000",
"context": {
"source": "email_hero_banner"
}
}'
Record a Conversion with Value
curl -X POST https://playground.kaireonai.com/api/v1/respond \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: my-tenant" \
-H "Authorization: Bearer sk_live_abc123" \
-d '{
"customerId": "CUST001",
"creativeId": "creative_001",
"outcome": "convert",
"idempotencyKey": "conv-cust001-offer001-20260316",
"interactionId": "550e8400-e29b-41d4-a716-446655440000",
"offerId": "offer_001",
"conversionValue": 299.99,
"outcomeDetails": {
"plan": "platinum",
"term_months": 12
}
}'
Error Responses
| Status | Cause |
|---|
400 | Missing required fields (customerId, creativeId/treatmentId, outcome/interactionType, idempotencyKey), unknown outcome type, or invalid JSON |
401 | Missing or invalid X-Tenant-Id / API key |
404 | Creative not found or belongs to another tenant |
415 | Content-Type header is not application/json |
429 | Rate limit exceeded (1,000 requests per 60s per tenant) |
500 | Internal server error |
Example error (unknown outcome type):
{
"error": {
"code": "BAD_REQUEST",
"message": "Unknown outcome type: \"purchased\". Register it in Outcome Types first.",
"status": 400,
"traceId": "d4e5f678-90ab-cdef-1234-567890abcdef",
"timestamp": "2026-03-16T14:35:00.000Z"
}
}
See also: Outcome Types | Behavioral Metrics | API Tutorial