Skip to content
 

API Reference · Query

Offer Query

Send intent, context, constraints, and pagination fields to discover offers for AI product or agent experiences.

Runtime projection

This route documents the public HTTP request and response surface. It is not the canonical AgentOffer Protocol: use Protocol v1.0 for normative Query and Offer semantics. Service access and credentials are configured separately in the Developer Portal.

Protocol v1.0 also defines optional alternative_offersalongside an empty main list and empty_reason, with a selection reason for each alternative. Service, SDK and Agent integration is pending; these fields are not a guarantee of the deployed HTTP response. See Query alternatives for the example, compatibility limits and rollout order. Existing requests, including force_offer, are unchanged.

Operations

POST/v1/offers/queryPlacement ID
Search offers by intent
Curated overlay
Query Parameters

Optional URL query string modifiers.

Fields
1·0 required/1 optional
Showing the complete documented schema.
offer_idstring
optional

Offer Wall placement extension: filter the wall's eligible offers by canonical Offer UUID before sorting and pagination. It never adds an Offer outside the placement's wall. Catalog list queries apply viewer country, location, device, and OS targeting. Only explicit offer_id lookups skip those dimensions; serving availability and explicit age eligibility always apply. The same field is also accepted at the top level of an Offer Wall JSON body as a hosted-service extension outside the canonical v1.0 schema. If both URL and body supply it, their UUIDs must match. Invalid or empty values, conflicting values, and URL use with other placement types return HTTP 400 BAD_REQUEST. Omit it to list the wall with normal country, location, device, and OS targeting. No match returns an empty offers array; catalog walls retain CATALOG_NO_AVAILABLE_OFFERS when empty.

format: uuid
Headers

Headers that should be present on this request.

Fields
5·2 required/3 optional
Showing the complete documented schema.
Required or conditional
AON-Protocol-Versionstring
required

Required exact current version selector. Send one value equal to `1.0`; every other value is unsupported.

Content-Typeheader
required

Request body format for JSON endpoints.

example: application/json
Optional
X-AON-Placement-Idstring
optional

Optional placement id, also supported by MCP HTTP requests. Used only when body placement_id (MCP: params.arguments.placement_id) is omitted; an explicit body value always takes precedence. Empty or whitespace-only values are treated as not provided (omitted: anonymous mode when available). Resolved as an active-placement lookup only — the placement decides which application receives attribution, with no ownership check against an API key. OfferWall placements apply configured selection and sorting; catalog OfferWall lists apply viewer country, location, device, and OS targeting (including IP and User-Agent inference); only explicit offer_id lookups skip those dimensions, while serving availability and explicit age eligibility always apply; AI Assistant and Content Assistant placements associate statistics while preserving normal recommendations and engagement suggestions.

example: plc_homepage
x-aon-user-pseudo-idstring
optional

Optional hosted-service extension carrying a pseudonymous viewer id. It is not part of the canonical v1.0 Query body, is never forwarded to Providers, and is never echoed in the response.

example: pseudo-viewer-xyz
x-aon-consentstring
optional

Optional hosted-service extension carrying `personalization=granted|denied;jurisdiction=<code>;age=<value>`. It is outside the canonical v1.0 Query body and is never forwarded to Providers.

example: personalization=granted;jurisdiction=EU;age=verified
Request Body

JSON payload fields accepted by this endpoint.

Fields
65·5 required/23 conditional/37 optional
Showing the complete documented schema.
Required or conditional
object
required

Bounded calling and session context for the current Query. It is not a raw conversation transcript or long-term profile.

5 nested fields
object
optional

Calling platform or agent metadata used for diagnostics and integration context.

3 nested fieldsexample: { "name": "shopping-bot", "channel": "api" }
object
optional

Bounded session continuity for the current conversation.

2 nested fieldsexample: { "previous_request_id": "019dd200-1234-7890-abcd-ef0123456788" }
session_idstring
optional

Optional session identifier for support correlation.

example: sess_123maxLength: 256
conversation_idstring | number
optional

Optional host conversation identifier.

example: conversation_123
object
optional

Optional viewer targeting context: location and a verified minimum age. Only location_ids, country, and verified_age_over are accepted; other profile attributes are rejected.

3 nested fieldsexample: { "location_ids": ["1014221"], "country": "US", "verified_age_over": 21 }
object
required

The user's current intent for this Query, not the complete conversation history.

6 nested fields
object
optional

Explicit opt-in Flight search constraints.

2 nested fields
object[]
required

Exactly one current text or image input item.

3 nested fieldsexample: { "type": "input_text", "text": "noise cancelling headphones under $200" }
typestring
required

Content item kind. Most server integrations start with `input_text`.

example: input_textAllowed valuesinput_textinput_image
textstring
required when intent.content[].type is input_text

Free-text user intent when the item type is `input_text`.

example: noise cancelling headphones under $200minLength: 1maxLength: 200
image_urlstring
required when intent.content[].type is input_image

URI of the current image input for an input_image item.

example: https://example.com/current-product.jpgformat: uri
provenancestring
required

Whether the current intent was directly expressed by the user or inferred from context.

example: user_expressedAllowed valuesuser_expressedinferred_context
confidencenumber
required when intent.provenance is inferred_context

Confidence assigned to an inferred intent. It is required for inferred_context and forbidden for user_expressed intent.

example: 0.8minimum: 0maximum: 1
object[]
optional

At most three unique references that led to a user-expressed intent. Inferred intent cannot carry origin entries.

2 nested fields
object
optional

Structured signals for the current turn, such as purchase stage, timeframe, or a budget with an explicit currency.

3 nested fields
Optional
object
optional

AON private extension accepted by the hosted REST Query API and MCP. Contains only limit and offset; it is not part of the canonical AgentOffer v1.0 request or the OfferProvider request.

2 nested fields
limitinteger
optional

Maximum offers to return. Defaults to 3 (20 for Flight queries); values above 20 are clamped to 20. Fewer offers may be returned when fewer qualify.

example: 5minimum: 1maximum: 4294967295
offsetinteger
optional

Offers to skip; defaults to 0. For pages of 5, send offsets 0, 5, 10. Ordinary recommendations are ranked again for each request, so pages are not a frozen snapshot and can overlap.

example: 0minimum: 0maximum: 4294967295
request_idstring
optional

Optional client request identifier for log correlation. UUIDv7 is recommended.

example: 019dd200-1234-7890-abcd-ef0123456789format: uuid
timestampstring
optional

Optional RFC 3339 timestamp for when the request was created.

example: 2026-04-28T03:30:00Zformat: date-time
test_modeboolean
optional

Optional test-execution marker that defaults to false. Deployments may apply stricter credential or environment controls.

example: false
object
optional

Explicit public taxonomy constraints for the current Query.

4 nested fields
offer_types[]string[]
optional

Hard filter on final public offer_info.offer_type. Supported values are physical_product, digital_goods, content, online_service, and offline_service. Omission preserves type inference; a non-empty array overrides inference; [] clears this filter and prevents reinference for this request. Values in the array are OR; offer_types and other constraint dimensions are AND. Raw duplicates are invalid.

Allowed valuesphysical_productdigital_goodscontentonline_serviceoffline_service
listing_source_names[]string[]
optional

Protocol constraint on final public listing_source.name. Each raw string has 1–160 Unicode code points and must not be ASCII-whitespace-only; raw duplicates are invalid. Match after trimming U+0009–U+000D/U+0020 and folding ASCII A–Z only; other characters match exactly, without aliases or substrings. Omission preserves inference; a non-empty array overrides inference; [] clears and prevents reinference. Values are OR, dimensions are AND. Unknown valid names remain constraints. Runtime support is unvalidated: confirm server support before sending; do not automatically retry by stripping this field.

category_ids[]string[]
optional

AON Taxonomy v1 category ids used as public query constraints. Parent ids match descendant offer categories. Ids are case-sensitive; use lowercase registry ids from /protocol/category-taxonomy.

example: others
excluded_category_ids[]string[]
optional

AON Taxonomy v1 category ids to exclude from the current query. Parent ids exclude descendant offer categories.

example: arts_entertainment.adult_entertainment
force_offerboolean
optional

Request a fallback recommendation when normal matching returns no offer.

example: false
object
optional

Optional controls for the shape of the current Query response.

1 nested field
thinking_modeboolean
optional

Defaults to true. Set false when the client does not want `match_reason` in returned offers.

example: true
Behavior, compatibility, and source notes

Current Offer Query contract for new integrations. Send exactly one `AON-Protocol-Version` header whose field value is exactly `1.0` to select this contract. If the header is omitted, the server falls back to the legacy 0.1 response shape (no `data.protocol_version`) for backward compatibility, so new integrations must always send it. A duplicated header, or an unknown, approximate, range, or otherwise non-exact selector, returns HTTP 400 `UNSUPPORTED_PROTOCOL_VERSION`; no fallback is performed for those. Successful responses echo `1.0` in both the response header and `data.protocol_version`, send `Vary: AON-Protocol-Version`, and return `data.offers[]` following the canonical public Offer v1.0 schema. The caller's identity for this endpoint is resolved from a placement, not an API key: pass body `placement_id`, or the `X-AON-Placement-Id` header (body takes precedence), or (production only, during the compatibility window) rely on a legacy API key that is listed in the server's key-to-placement compatibility map. Empty or whitespace-only placement values are treated as not provided. With no identity at all, the request is served in anonymous mode through the AON default placement (response `extra.identity.mode="anonymous"`, no earnings, no idempotent replay: a retry recomputes) when the deployment configures it; only when anonymous mode is unavailable or off does the request return HTTP 401 `UNAUTHORIZED`. A `Bearer` Authorization header does not supply the placement: a live key without a placement is served in anonymous mode with an `extra.key_notice`, and a non-sandbox `aon_test_` key without a placement returns HTTP 401 `UNAUTHORIZED`. The placement decides which application receives attribution; it is not checked against any API key. OfferWall placements apply configured selection and sorting; catalog OfferWall lists apply viewer country, location, device, and OS targeting (including IP and User-Agent inference); only explicit offer_id lookups skip those dimensions, while serving availability and explicit age eligibility always apply; AI Assistant and Content Assistant placements associate statistics while preserving normal recommendations and engagement suggestions. Non-empty but invalid, unknown, or inactive placement values return `INVALID_PLACEMENT`; an empty catalog placement returns `CATALOG_NO_AVAILABLE_OFFERS`; a placement whose application is deleted or not serving returns HTTP 403 `APPLICATION_NOT_SERVING`; a placement owned by a suspended developer returns HTTP 403 `DEVELOPER_SUSPENDED`. Unknown or inactive `constraints.category_ids` fail closed with HTTP 400 `INVALID_CATEGORY_ID` and an indexed `extra.field_path`; resolve category ids first rather than guessing. If the caller supplies `request_id`, this endpoint replays a cached SUCCESS response for that (caller identity, request id) pair for 10 minutes without duplicating the five write-side effects: inbound journal, impressions/billing projection, outbound metrics, partner journal, and embedding journal. `request_id` should be an unguessable value (UUID). A retryable HTTP 503 `SERVICE_BUSY` may be retried with the same `request_id`; HTTP 409 `QUERY_REPLAY_UNAVAILABLE` means the durable query is finalized but its response body can no longer be replayed, so issue a new logical request with a new `request_id`. Network Offer Wall placements use a model-free eligible list when intent is omitted, or literal lexical matching when text is explicitly supplied; they do not infer hidden constraints or split multiple intents. Catalog walls retain their configured membership and ordering. Ordinary AI/Content Assistant queries retain semantic search and only their existing visibility=all inventory; switching away from a wall does not retain offerwall_only inventory. Network wall pagination uses an absolute offset in the current eligible sequence before shown-history exclusions; follow extra.pagination.next_offset rather than calculating offset + returned. Network total is null and inventory is not snapshotted across requests. A necessary upstream failure is not a successful empty match: generic hosted Query returns 503 UPSTREAM_UNAVAILABLE, or 504 QUERY_TIMEOUT when a necessary call actually timed out. These are deployment-owned errors; specialized Flight/Hotel contracts remain unchanged. There is no new short Query-wide deadline. Only a cached SUCCESS response can be replayed: a finalized failed/cancelled operation without a success cache returns QUERY_REPLAY_UNAVAILABLE on the same logical key.

  • This is the main entry point for offer discovery. Identify your integration with your placement id; no other credential is needed.
  • Persist `offers[].offer_instance_id` for downstream attribution and conversion reporting.
  • Placement identity: send the placement id in the `X-AON-Placement-Id: plc_…` header. Traffic is attributed to the application that owns the placement. A placement id is not a secret, so you can call from a browser or app.
  • All placement types accept nested `pagination: { limit, offset }` as an AON private extension under v1.0. The effective limit is 1–20 and defaults to 3 (20 for Flight); values above 20 are clamped. Offset defaults to 0. Ordinary placements still require intent. These fields are not part of the canonical AgentOffer request or the outbound OfferProvider contract.
  • Use a new request_id for each page and reuse that id only for retries of the same page. Ordinary recommendation pages can overlap as ranking and inventory change; deduplicate by offer_id. Their responses do not promise a stable total or extra.pagination metadata.
  • Offer Wall placements (placement type `offerwall`) also allow omitted intent and historical top-level limit/offset aliases. Their responses add extra.pagination with limit, offset, returned, total, has_more, and next_offset. Fetch next_offset while has_more is true. See /offer-wall/api.
  • Flight `reference_search` forbids travelers and returns price_basis reference; `traveler_quote` requires traveler groups and returns price_basis itinerary_total with tax_status included. Hard itinerary constraints must match; intent text does not override them.
  • Typed Flight success returns usable Offers without source execution metadata. Empty offers means no usable results for this request; it does not claim market-wide coverage. Total source failure remains an error.
  • Ordinary queries, including non-real-time requests, may return registered `offer_info.details` with profile-required price, tax and quote facts. Details alone do not imply a live lookup. Opt in to strict real-time Flight matching with `intent.details.profile: flight` and explicit `data.query_kind` and itinerary legs. Typed Flight results require complete matching Flight details and original price including tax_status. Public responses omit execution metadata; empty typed results mean no usable Offers for this request. Canonical protocol support does not by itself certify a deployed provider capability.

Quick Recipes

Minimal request

Use this shape to validate placement identity and the required intent/context envelope.

  • Send intent.content[] with at least one user input item.
  • Include intent.provenance and send only bounded context for the current conversation.
  • Use the full request example in Endpoints when you need a copyable payload.

Production request

Add platform, bounded session context, structured signals, and public constraints once the minimal request works.

  • Add context.platform and context.session for bounded continuity.
  • Keep sending your placement id in the X-AON-Placement-Id header.
  • Use constraints.category_ids when the user intent maps to AON Taxonomy v1.
  • Set response_options.thinking_mode to false when the client does not need match reasons.

Page through recommendations

Send the AON pagination extension with the same intent and constraints for each page.

  • Use pagination: { limit: 5, offset: 0 }, then offsets 5, 10, and so on. The effective limit is at most 20.
  • Use a new request_id for each page; reuse it only when retrying that same page, otherwise idempotent replay can return the previous response.
  • Ordinary recommendations can change between requests. Deduplicate accumulated results by offer_id; offset pagination does not guarantee distinct pages or a stable total.
  • Stop on a short or empty page. Ordinary Query responses do not promise extra.pagination; Offer Wall responses provide their own paging metadata.

Empty result

Treat an empty `offers` array as a valid response. Show an empty state or relax constraints.

  • code: SUCCESS with data.offers: [] is not an integration failure.
  • Log data.request_id before retrying or relaxing constraints.
  • Keep the complete empty-result payload in Endpoints as the response reference.
  • Do not treat an empty match as HTTP 204; the JSON body is intentional so clients keep request correlation.

Error response

Handle malformed payloads, invalid credentials, placement errors, and rate limits explicitly.

  • Use 400 to fix request shape or missing required fields.
  • INVALID_PLACEMENT and CATALOG_NO_AVAILABLE_OFFERS are platform envelope code values for placement-scoped queries.
  • Use 401 to check that the request sends a placement id (without one it is served in anonymous mode when available).
  • Use 429 backoff with jitter and avoid immediate retry loops.

Common Questions

Can ordinary Query requests choose a result count and page through recommendations?

Yes. Send nested `pagination.limit` and `pagination.offset` to the hosted REST Query API or MCP. These are AON private extensions, not canonical AgentOffer v1.0 fields. The default limit is 3 (20 for Flight), the effective maximum is 20, and offset defaults to 0. Ordinary placements still require intent. Only Offer Wall also accepts the historical top-level limit/offset aliases. Use a new request_id per page and deduplicate by offer_id because ranking and inventory can change between requests.

What is the difference between `intent` and `constraints`?

`intent` describes what the user wants in natural language or multimodal content. `constraints` is for low-ambiguity structured constraints. The first public version only exposes canonical category constraints.

Should `category_ids` use root ids or leaf ids?

Send AON Taxonomy v1 ids from /protocol/category-taxonomy. A root id such as `travel_tourism` matches descendants, a leaf id such as `computers_electronics.computers.software` is more precise, and `others` currently matches only stored id `others`. Historical v0.1 aliases may be tolerated in migration paths, but public integrations should store and send taxonomy ids.

How do we correlate conversions?

Persist the returned `offer_instance_id`. It is the dispatch-level identity for click, conversion, and settlement attribution; do not replace it with a legacy tracking URL.

What should we do when no offers are returned?

Render a normal empty state, log the `request_id`, and consider relaxing constraints before retrying. Do not treat `offers: []` as a failed request.

How should we debug 401 responses?

Check that the request sends your placement id in the `X-AON-Placement-Id` header and does not send `X-AON-Test`. Offer Query never needs an API key; only the Public Test Sandbox uses its shared test key.

How should we handle 429 responses?

Apply exponential backoff with jitter, honor retry windows when present, and avoid immediate retry loops from the same session or worker.

Where should we put the placement id?

Send it in the `X-AON-Placement-Id` header on `POST /v1/offers/query`. Do not nest it inside `constraints`.

Is `/v1/sdk/offers/query` still available?

No — `/v1/sdk/offers/query` was retired in SVC-PLATFORM-F260813162251. All REST integrations, including former SDK-facing callers, now use `POST /v1/offers/query`.

Implementation checklist · 3 items
  • Get your placement id from Quick Start or Developer Portal first (or omit it to run in anonymous mode, with no earnings).
  • Send a structured `intent` payload, even for simple text-only queries.
  • Persist returned tracking URLs or identifiers for downstream attribution.