AI API
POST /v1/ai/process matches a contextual ad to one assistant message. The
AI SDK integrations call this endpoint, and you can
call it directly.
POST /v1/ai/process
POST https://api.adzen.ai/v1/ai/processHeaders
| Header | Required | Value |
|---|---|---|
X-API-Key | One of X-API-Key or Authorization | Your AI API key |
Authorization | One of X-API-Key or Authorization | Bearer <key>. If you send both headers, Adzen uses X-API-Key. |
Content-Type | Yes | application/json |
Idempotency-Key | No | A unique string for this request. Send the same value on every retry of it. Publisher insights counts requests that share a value once. See Idempotency and retries. |
See Authentication for how to get a key.
Body
| Field | Type | Required | Rules |
|---|---|---|---|
message_id | string | Yes | Your id for this assistant message. Must not be empty. Use a new value for each assistant message: a reused message_id can make a later message’s beacons count as duplicates. |
content | string | Yes | The text to match an ad to. Must not be empty. At most 10,000 bytes of UTF-8, not characters. |
location | string | Yes, to receive an ad | The viewer’s US state as an uppercase ISO 3166-2 code, such as US-CA. A request without location, or with a value that does not start with US-, returns empty ads. |
conversation_id | string | No | Your id for the conversation. Adzen adds it to the impression beacon URLs. |
prompt | string | No | The user prompt that produced content. |
content_role | string | No | Who wrote content: user or assistant. Case-insensitive. Other values are ignored. |
Adzen checks the API key first, then parses the body, then checks the field rules above.
Example request
curl -X POST https://api.adzen.ai/v1/ai/process \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 0f8b6d2e-5c1a-4e7b-9d3f-2a6c8e1b4f70' \
-d '{
"message_id": "msg_abc123",
"content": "For a leaking pipe, shut off the main water valve first, then call a plumber.",
"conversation_id": "conv_7f3a9b2e",
"location": "US-WA"
}'Response
A match returns 200 with one ad:
{
"message_id": "msg_abc123",
"ads": [
{
"ad_id": 1071,
"title": "24/7 Emergency Plumbing",
"cta": "Book a licensed plumber",
"click_through_url": "https://api.adzen.ai/dea/ai/redirect/k3x9q2m",
"advertiser_name": "Example Plumbing Co.",
"sponsor_logo_url": "https://cdn.example.com/logos/example-plumbing.png",
"creative_url": "https://cdn.example.com/creatives/plumber-van.jpg",
"description": "Same-day repairs for leaks, clogs and burst pipes.",
"pricing_model": "CPC",
"relevancy_score": 0.9473,
"placement": "after_message",
"pixel_trackers": [],
"render_impression_url": "https://api.adzen.ai/dea/ai/impressions/render?ad_id=1071&message_id=msg_abc123&slug=k3x9q2m&conversation_id=conv_7f3a9b2e&ad_unit_position=after_message×tamp=2026-10-30T14%3A02%3A11.483920517%2B00%3A00",
"view_impression_url": "https://api.adzen.ai/dea/ai/impressions/view?ad_id=1071&message_id=msg_abc123&slug=k3x9q2m&conversation_id=conv_7f3a9b2e&ad_unit_position=after_message×tamp=2026-10-30T14%3A02%3A11.483920517%2B00%3A00&viewability_ms=1000"
}
]
}No match also returns 200, with an empty ads array:
{
"message_id": "msg_abc123",
"ads": []
}| Field | Type | Meaning |
|---|---|---|
message_id | string | The message_id you sent. |
ads | array | At most one ad. Empty when no ad is returned for this message. |
Each object in ads has these fields. Fields typed string or null are sent as
null when they have no value; no field is left out.
| Field | Type | Meaning |
|---|---|---|
ad_id | integer | Adzen’s id for the ad. |
title | string | The ad headline. |
cta | string | The call-to-action text. |
click_through_url | string | The URL the ad links to. See Click URL. |
advertiser_name | string | The advertiser’s name, to show as the sponsor. |
sponsor_logo_url | string | URL of the advertiser’s logo. Can be an empty string. |
creative_url | string or null | URL of the ad image. |
description | string or null | Body text for the ad. |
pricing_model | string | How the advertiser pays for this ad: CPC or CPM. |
relevancy_score | number | How closely the ad matches content, from 0 to 1. Higher is closer. |
placement | string | after_message: show the ad below the assistant message named by message_id. |
pixel_trackers | array | Reserved. An empty array. Impressions are counted through render_impression_url and view_impression_url. |
render_impression_url | string | Beacon to fire when the ad is shown. See Impression beacons. |
view_impression_url | string | Beacon to fire when the ad is viewed. See Impression beacons. |
Errors
Errors from this endpoint have this body:
{
"error": "content must not be empty"
}Malformed JSON (400), a wrong Content-Type (415) and a body that does not
fit the schema (422) return a plain-text body instead.
| Status | Body | When | Retry? |
|---|---|---|---|
400 | {"error": "message_id must not be empty"} | message_id is an empty string | No |
400 | {"error": "content must not be empty"} | content is an empty string | No |
400 | {"error": "content must not exceed 10000 characters"} | content is over 10,000 bytes | No |
400 | Plain text | The body is not valid JSON | No |
401 | {"error": "missing profile id"} | The API key is missing or is not an AI API key | No. Check the key. |
401 | {"error": "unknown profile"} | The key’s account is not set up for the AI API. Contact your Adzen account representative. | No |
403 | {"error": "publisher profile is disabled"} | Your account is disabled | No |
403 | Not JSON | The request came from an IP address outside the US | No |
415 | Plain text | Content-Type is missing or is not application/json | No |
422 | Plain text | message_id or content is missing, or a field has the wrong type | No |
429 | {"error": "endpoint overloaded, retry shortly"} | Adzen is at capacity for the moment. The response has Retry-After: 1. | Yes, after Retry-After seconds |
500 to 599 | Varies | A server or gateway error | Yes, with backoff |
No fill is not an error. It returns 200 with "ads": []. See
Rate limits for limits across products.
Click URL
Link the ad to click_through_url exactly as returned. The URL has the form
https://api.adzen.ai/dea/ai/redirect/<slug> and answers 302 with the
advertiser’s landing page. Adzen counts a click only when the viewer goes
through this URL.
Impression beacons
Each ad carries two beacon URLs. Fire each one once, from the viewer’s browser, exactly as returned.
| Beacon | Fire it when |
|---|---|
render_impression_url | The ad is shown in the conversation. |
view_impression_url | The ad has been 100% visible for one continuous second. |
The view URL already carries viewability_ms=1000. Do not change it.
- Beacons are
GETrequests. A beacon returns204 No Content. - A beacon with
ad_idormessage_idremoved, or a view beacon without an integerviewability_ms, returns400with{"error": "..."}. - Adzen counts one beacon of each type per
message_idandad_idpair for the same IP address and user agent. A repeat within 5 minutes still returns204and counts as invalid. - Beacon and click URLs return
403to requests from IP addresses outside the US.
Fire beacons from the viewer’s browser. Adzen validates each beacon against the viewer’s IP address and user agent.
Idempotency and retries
Send the same Idempotency-Key on every retry of a request.
Publisher insights counts requests that share
an Idempotency-Key once in ad_requests. Each request sent without the
header counts as a separate request.
Each retry returns a fresh match. It can return a different ad, or none.
Retry 429 after the Retry-After delay and 5xx with backoff. Do not retry
400, 401, 403, 415 or 422 without changing the request.
Timing
Matching has a deadline of 2.5 seconds. A request that takes longer returns
200 with empty ads.