Skip to content

Ad Fetch API

Overview

The fetch API returns one rendered ad for a moment in your application. It is the single serving endpoint behind AdKit; you can also call it directly.

POST https://ads.advertising.chat/v1/fetch/v2
Content-Type: application/json

GET is also supported, with the same parameters passed as query strings.

Authentication

Pass an API key as a query parameter (?key= or ?apikey=) or as an Authorization: Bearer <key> header. API keys are not accepted in the request body — credentials stay out of message payloads by design.

Use a per-application fetch key (the dataplane credential shown under API Keys in the Console) for serving. A fetch key carries an application context, so when you omit adUnit the request resolves that application's default placement. Management keys authenticate too, but resolve to an org-scoped default.

Request parameters

Parameter Type Description
adUnit string Optional. Code of the ad unit (moment) to fill. Omit it and the request serves into your application's default placement, lazily provisioned on first use — a brand-new account fills with zero configuration. Supply a code only if you manage explicit ad units in Offer Manager.
format string Rendering format for the response payload (e.g. direqt, sms). Defaults to direqt.
subscriber string Optional opaque, application-assigned end-user id. Enables frequency capping and unique-reach reporting. Never use personal data (emails, phone numbers) as the id.
attributes object Optional key/value targeting attributes evaluated against your line-item rules. Body only; in query strings use the leading-dot form (.tier=gold).
context string or array Optional conversational context used to improve ad relevance. See below.
test any If non-zero, marks the fetch as a test: it is excluded from reporting and the response includes rule-by-rule disqualifications.

The context parameter

context carries the conversation the ad will appear in — the signal a conversational ad server selects on. Two forms are accepted:

{ "adUnit": "my-ad-unit", "context": "User asked for gift ideas for a coffee lover." }
{
  "adUnit": "my-ad-unit",
  "context": [
    { "role": "user", "text": "any gift ideas for a coffee lover?" },
    { "role": "assistant", "text": "A few! What's your budget?" }
  ]
}
  • Either a plain-string summary or an array of { role, text } turns (role is optional and defaults to user). This is the same shape the AdKit SDK and MCP tools send for you.
  • Content beyond 4KB (UTF-8) is truncated; send a summary or the most recent turns rather than a whole transcript.
  • Malformed values are ignored — the fetch still succeeds, without context.

Context improves ad relevance; it is not stored. The text is used at serve time to choose the ad and is then discarded: it is never written to our journals, analytics events, or logs. The serving decision record keeps only a one-way hash and the byte length of what was received. There are no cookies, no cross-site profiles, and no accumulated user history — the serve-time conversation is the targeting signal.

When context is provided, selection is semantic: the conversation's meaning is matched against candidate ads (embedding similarity), and an appropriateness check can decline to serve any ad at all in sensitive moments — such as a health crisis, grief, or a heated support conversation — returning a normal no-fill (204) instead.

When a line item has multiple creatives, rotation is adaptive: creatives that earn more engagement (clicks, flow interactions) are automatically served more often, while every creative keeps receiving a share of exploration traffic. Line items configured for sequential rotation are not affected. Ads with daily budgets are paced evenly across the day.

Response

{
  "messageId": "direqt-6a3f…",
  "format": "direqt",
  "payload": { "…rendered ad for the requested format…" },
  "properties": {}
}
Field Description
messageId The fetch id (a direqt-<uuid> string — a retained legacy wire identifier). Pass it as fetchEventId when reporting ad events to tie deliveries, clicks, and conversions back to this impression.
payload The rendered ad, in the requested format. Pass it to your messaging surface without inspection.
properties Optional extra creative properties.

A 204 response means no ad was eligible for this request (targeting, frequency caps, or availability). Client errors return 4xx with a JSON body containing reason and description.

Testing

Sandbox — a guaranteed sample ad

For a response that always fills — regardless of account state or available demand — send the reserved sandbox value. It's handy in a test harness where you want a deterministic card:

curl -X POST "https://ads.advertising.chat/v1/fetch/v2?key=YOUR_FETCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{"adUnit": "__sandbox__", "context": "anything at all"}'

You'll always get a 200 with a Sponsored disclosure that renders a demo card like this:

The sandbox demo ad card

The sandbox short-circuits selection and records no impression, click, or billing events, so it's safe to call repeatedly. In the SDK the same value is exported as SANDBOX_AD_UNIT.

Diagnosing a no-fill

A 204 from a live fetch is normal — there was simply no good ad to show. To see why, add ?debug=1 (or the header X-Adchat-Debug: 1). On a no-fill the request returns 200 with { "filled": false, "reason": "...", "message": "..." } instead of a bare 204. Reasons include frequency_capped, appropriateness_suppressed, and no_demand. In the SDK, fetchAdDebug() returns the same result. Default production responses are unchanged — debug is opt-in per request.