Skip to content
 

API integration

Call Offer Query with your placement id in the X-AON-Placement-Id header. The placement is the identity. The placement id is not a secret, so it is safe to ship in a web page or app.

In the example, replace plc_YOUR_PLACEMENT_ID with your Offer Wall placement id from the Developer Portal. The example sends US explicitly; replace it with the user's actual country.

Offer Wall page request
curl
curl --request POST \
  "https://api.aon.pro/v1/offers/query" \
  --header "Content-Type: application/json" \
  --header "AON-Protocol-Version: 1.0" \
  --header "X-AON-Placement-Id: plc_YOUR_PLACEMENT_ID" \
  --data '{
    "context": {
      "user_profile": { "country": "US" }
    },
    "pagination": { "limit": 20, "offset": 0 }
  }'
HeaderValue
Content-Typeapplication/json
AON-Protocol-Version1.0
X-AON-Placement-IdYour Offer Wall placement id, plc_…

All placement types accept nested pagination as an AON private extension. Offer Wall placements also support these conveniences:

  • intent may be omitted for a list. A Network wall uses literal lexical matching when you explicitly supply text, including featured offers. Network walls do not call intent models, Embedding, or model reranking. Catalog walls keep their configured membership and ordering.
  • Historical top-level limit and offset are accepted too. Prefer nested pagination ({ "limit": 20, "offset": 0 }) for both walls and ordinary queries. The effective limit is 1–20 and defaults to 3 for walls when omitted; values above 20 are clamped, so send your desired page size.

Other placement types still require intent and use nested pagination. Ordinary Query responses do not promise the wall's extra.pagination metadata. Use a new request_id for each page and reuse it only for retries of that same page.

User context

Offer Wall uses the same v1.0 context schema as Offer Query. It accepts {}, the session fields platform (name, version, channel), session_id, conversation_id and session, and an optional user_profile with these fields:

Field under context.user_profileMeaning
countryThe user's country as an uppercase ISO 3166-1 alpha-2 code, such as US.
location_idsOne to ten unique AON Location Registry IDs as numeric strings, most specific first. Resolvable IDs take precedence over country.
verified_age_overA verified minimum-age assertion, as an integer from 13 through 120, such as 18 or 21. Supply it only from a real age-verification process.

The schema remains strict: other profile fields, including language and device_info, are rejected with INVALID_PROTOCOL_PAYLOAD. Put country inside context.user_profile, not directly inside context.

Offer Wall lists apply the configured selection and the viewer's country, location, device, and operating-system targeting before sorting and pagination. Only an explicit offer_id lookup skips location, device, and OS matching. Offer availability checks (such as active state, valid dates, and ready goals) and explicit age eligibility apply to both paths.

For example, to request offers for a user in the United States:

{
  "context": {
    "user_profile": { "country": "US" }
  },
  "pagination": { "limit": 20, "offset": 0 }
}

Replace US with the user's actual country. See Location Targeting for location IDs and where to call from for IP-based fallback.

Filter by Offer ID

To request one Offer from the wall, add ?offer_id=<Offer UUID> to the /v1/offers/query URL, or include offer_id in the JSON body:

{
  "offer_id": "019fc211-c6cd-7e62-9c4d-699c243e6c96",
  "context": { "user_profile": { "country": "US" } },
  "pagination": { "limit": 20, "offset": 0 }
}

This hosted-service extension is available only for Offer Wall placements. It filters before sorting and pagination, while keeping the wall's selection and availability checks. This explicit lookup skips country, location, device, and OS matching; the normal list still applies those filters. An Offer outside the wall or no longer available is not returned. When nothing matches, data.offers is empty; catalog walls keep the CATALOG_NO_AVAILABLE_OFFERS empty-state response described below.

Use the canonical offer_id returned by AON, not an offer_instance_id or a Partner's upstream ID. Invalid or empty IDs return HTTP 400 BAD_REQUEST. If both the URL and body contain offer_id, they must identify the same UUID. Omit the parameter to request the full wall as before.

Where to call from

You can call Offer Query from the user's device or through your own server. Browser, web view, and app calls are supported; Offer Query allows cross-origin requests from any origin.

An explicit, valid context.user_profile.country takes precedence over the country inferred from the caller's IP. If you omit country, AON uses the caller's IP as the country fallback. Resolvable location_ids take precedence over either country source.

For server-side calls, send the user's actual country or location_ids and forward their User-Agent so list results match the viewer's location, device, and operating system. Without explicit location context, the IP fallback identifies your server's country.

Response

The response uses the standard envelope (code, message, data, extra). Offers are in data.offers in the v1.0 Offer shape, and Offer Wall placements also return paging state in extra.pagination. The example below shows one offer with every display field and one with only the fields that are always present:

Offer Wall response (trimmed to two offers)
JSON
{
  "code": "SUCCESS",
  "message": "",
  "data": {
    "request_id": "0195ef94-f17d-7a4f-b6e0-2c52bb49e13f",
    "protocol_version": "1.0",
    "language": "en",
    "offers": [
      {
        "offer_id": "019fc211-c6cd-7e62-9c4d-699c243e6c96",
        "offer_instance_id": "c41d7e9a-3f62-8b05-a7d1-6e0b9c2f8a14",
        "offer_info": {
          "title": "TeamFlow Pro Plan",
          "short_description": "Docs, wikis, and planning in one workspace.",
          "description": "Collaborative workspace for docs, wikis, and project planning."
        },
        "entity": {
          "id": "ent_teamflow",
          "name": "TeamFlow",
          "logo": "https://cdn.teamflow.example/logo.png"
        },
        "material": [
          {
            "url": "https://cdn.teamflow.example/cover-1200x628.png",
            "format": "image",
            "tag": "banner",
            "dimensions": "1200x628"
          }
        ],
        "claims": [
          {
            "kind": "user_benefit",
            "text": "14-day free trial"
          }
        ],
        "action": {
          "type": "open_url",
          "name": "Start free trial",
          "payload": {
            "url": "https://aon.link/Ab3xY9qPk7Lm2Nd4"
          }
        },
        "goals": [
          {
            "event": "conversion",
            "pricing": {
              "model": "cpa",
              "amount": "12.000000",
              "currency": "USD"
            }
          }
        ]
      },
      {
        "offer_id": "019fc211-c6cd-7e62-9c4d-6a0f1d2e3b47",
        "offer_instance_id": "e8a2d5c7-91b4-8f3e-b2c6-7d4a0e9f1b58",
        "offer_info": {
          "title": "Harbor Town: Build & Trade",
          "description": "Build your own harbor town, trade with neighbours, and unlock new islands. Reach level 10 to complete the offer."
        },
        "entity": {
          "id": "ent_harbortown",
          "name": "Harbor Town"
        },
        "material": [
          {
            "url": "https://cdn.harbortown.example/icon-512.png",
            "format": "image",
            "tag": "icon",
            "dimensions": "512x512"
          },
          {
            "url": "https://cdn.harbortown.example/banner-1024x500.jpg",
            "format": "image",
            "tag": "banner",
            "dimensions": "1024x500"
          }
        ],
        "action": {
          "type": "open_url",
          "payload": {
            "url": "https://aon.link/Hb7tQw2XfR5sJ9zE"
          }
        },
        "goals": [
          {
            "event": "conversion",
            "pricing": {
              "model": "cpa",
              "amount": "1.800000",
              "currency": "USD"
            }
          }
        ]
      }
    ]
  },
  "extra": {
    "pagination": {
      "limit": 20,
      "offset": 0,
      "returned": 20,
      "total": 37,
      "has_more": true,
      "next_offset": 20
    }
  }
}
extra.pagination fieldMeaning
limit, offsetThe page you asked for
returnedOffers in this page
totalCatalog count when available; null for Network walls. It is not a cross-request inventory snapshot.
has_moreWhether another page is available
next_offsetThe offset to send for the next page

To show each offer, see Render offers.

Paging

Request the first page with offset: 0. While extra.pagination.has_more is true, request the next page with offset set to extra.pagination.next_offset — for example, when the user scrolls near the end of the list. Stop when has_more is false. Drive paging from has_more and next_offset rather than from total, since total can change between pages. A Network wall's offset is a position in the current eligible list, before excluding offers already shown to the same declared user/session. Skipped shown offers consume positions, so next_offset can exceed offset + returned. A lookahead offer is left for the next page. Keep the same filters between pages; restart at offset 0 after changing them. Inventory changes can move positions between requests; pagination does not provide a snapshot.

An empty catalog wall returns HTTP 200 with code CATALOG_NO_AVAILABLE_OFFERS, an empty data.offers, and no extra.pagination. A completed Network query with no matches uses its normal empty success response. Show an empty state for these outcomes.

A failed required lookup is different: HTTP 503 UPSTREAM_UNAVAILABLE or HTTP 504 QUERY_TIMEOUT means the query could not complete. Show a temporary failure state instead of claiming there are no offers. SERVICE_BUSY permits a backoff retry with the same request_id; HTTP 409 QUERY_REPLAY_UNAVAILABLE means the original operation has ended without a replayable success, so a new attempt needs a new request_id. Only cached successful pages replay without new execution. Anonymous requests retain their existing no-replay behavior.

Which offers appear

For Catalog walls, the offers and their order are selected on the placement's Offers panel in the Developer Portal. total is how many of them can be shown to this user right now. If the list is short or covers only one kind of offer, change the selection there — no code change is needed.

Network walls browse eligible network inventory or match explicit text literally. To use semantic inference or multiple intents, use an AI/Content Assistant placement with ordinary Query. That mode only serves its existing all visibility inventory; offerwall_only inventory remains restricted to walls, so the two modes can return different inventories. Also:

  • Offers are matched to the device and operating system named in the request's User-Agent, so an offer limited to iOS isn't shown to Android visitors. Requests whose User-Agent names no platform, such as most server-side calls, aren't filtered by device. Some offer sources don't supply platform data yet, so their app offers can still appear on any platform.
  • Offer text is in the offer's own language, given in content_language (usually en). There is no language parameter, so translate only your own interface text.

Next

Render offers