# API integration

Source: https://docs.aon.pro/offer-wall/api

> Derived from the same AON Docs release as the source page.

Call [Offer Query](https://docs.aon.pro/api/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 }
  }'
```

| Header | Value |
| --- | --- |
| `Content-Type` | `application/json` |
| `AON-Protocol-Version` | `1.0` |
| `X-AON-Placement-Id` | Your 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](https://docs.aon.pro/api/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_profile` | Meaning |
| --- | --- |
| `country` | The user's country as an uppercase ISO 3166-1 alpha-2 code, such as `US`. |
| `location_ids` | One to ten unique AON Location Registry IDs as numeric strings, most specific first. Resolvable IDs take precedence over `country`. |
| `verified_age_over` | A 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:

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

Replace `US` with the user's actual country. See [Location Targeting](https://docs.aon.pro/protocol/location-targeting) for location IDs and [where to call from](https://docs.aon.pro/offer-wall/api#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:

```json
{
  "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` field | Meaning |
| --- | --- |
| `limit`, `offset` | The page you asked for |
| `returned` | Offers in this page |
| `total` | Catalog count when available; `null` for Network walls. It is not a cross-request inventory snapshot. |
| `has_more` | Whether another page is available |
| `next_offset` | The `offset` to send for the next page |

To show each offer, see [Render offers](https://docs.aon.pro/offer-wall/rendering).

## 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](https://developer.aon.pro/). `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](https://docs.aon.pro/offer-wall/rendering)
