Skip to content
 

aon.js Web Plugin

aon.js is the script that puts a Content Assistant placement on a web page. Add it once per page and it reads the placement's styles from AON, draws them, opens the chat panel, and attributes everything to the placement. This page is the reference for the tag, the DOM it expects and creates, the requests it makes, and the window.AON API. For the step-by-step setup, read the Content Assistant guide first.

The tag

<script
  src="https://plugin.aon.pro/aon.js"
  data-placement-id="plc_your_placement_id"
  data-lang="en"
  referrerpolicy="origin"
  async
></script>

Put it once on every page, before </body>, normally in the shared layout. A second copy on the same page does nothing.

AttributeRequiredValue
srcyeshttps://plugin.aon.pro/aon.js
data-placement-idyesThe Content Assistant placement id from the Developer Portal, [A-Za-z0-9_-], 1–64 characters. Also the attribution key: offers shown by this assistant are credited to the placement's application.
data-langnoLanguage of the panel's own text (header, buttons, prompts). BCP 47 tag, matched case-insensitively. See Panel languages. Default en. It never follows the visitor's browser.
referrerpolicyyesorigin. Keeps the full page URL (path and query) out of the script request when the page sends a permissive Referrer-Policy.
asyncrecommendedThe script never blocks rendering; it does all its work after load.
data-prewarmnooff (default), idle or eager. Whether to build the hidden chat panel before the first click. idle and eager download the panel (about 40 KB gzipped) on every visit unless the visitor has asked for Save-Data; use them on pages with a high open rate.
data-publisher-idnoAn identifier of your choice. It only namespaces the device id stored on your origin (see Requests) and is never sent to AON.
data-debugnoAny value except false turns on the panel's diagnostic logging.

data-prewarm values are case-sensitive. An invalid data-placement-id or data-prewarm is ignored with one console.debug line and never replaces an earlier valid value.

If document.currentScript is not available to the script (bundled, loaded as a module, or inlined by a tag manager), the attributes cannot be read. Then load it with a classic non-async tag and call AON.init({ placementId: "…" }) synchronously right after it. The placement id is fixed when the document finishes parsing (DOMContentLoaded), or earlier if the panel is opened before that; a later init only logs a debug line.

What the script draws

The placement's styles are switched on and off in the Developer Portal. The script fetches that setup on load and, once the document has parsed, draws what is on:

StyleDrawn asNeeds in your HTML
Floating buttonA fixed button in the viewport corner (button[data-aon-launcher], aria-label="Open assistant"), with an optional prompt bubble when "Show a prompt message" is on. A click opens the panel with the first suggested question for the page, or an empty panel when AON has not read the page.Nothing.
End of contentUp to five suggested questions, plus a question box when "Include a question box" is on, rendered inside your spot as section[aria-label="Assistant suggestions"].One <div data-aon-slot="end-of-content"></div> on every page, right after its main content. The script looks for it once, when the document has parsed and the placement setup has arrived, and does not watch for later insertions; put it in the server-rendered or static HTML so it is there in time.
Inline mention, Inline cardNot drawn by the web script yet. Switching them on has no effect on web.Nothing.

The spot renders nothing until AON has read the page (see Requests). Leave the div empty with no fixed height; the script appends its own nodes and removes them again when the page's reading is no longer valid. One spot per page: only the first is used.

On every page the script also adds a page-level div[data-aon-chat] shell that holds the chat panel and a one-line disclosure, "Sponsored results by agent offer network (AON)", linking to aon.pro with rel="sponsored nofollow noopener". The panel itself is a cross-origin <iframe title="AON"> from https://plugin.aon.pro, sandboxed (allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox, no top navigation) with allow="web-share; clipboard-write" and referrerpolicy="origin". Everything the assistant says, every offer and every price lives inside that iframe; the host page never receives offer data.

Page requirements

  • HTTPS. On an http:// page the script still loads and the floating button still opens the panel, but AON never reads the page, so the End of content spot stays empty and the panel opens without suggestions.
  • Own domain. AON reads a page by fetching its public URL. Pages behind a login or on shared preview domains (*.vercel.app, *.netlify.app, *.pages.dev, *.github.io, tunnels) are not read.
  • Single-page apps. AON reads a page on a full load. After a client-side route change (pushState, replaceState, popstate) the script hides the suggestions it drew for the previous URL and does not read the new one until the next full load (or until init({ lang }) changes the locale, which reads the current URL). The chat panel keeps working.

Content-Security-Policy

If the page sends one, allow:

script-src  https://plugin.aon.pro
frame-src   https://plugin.aon.pro
connect-src https://plugin.aon.pro https://api.aon.pro

No style-src change is needed: styles are set property by property, never through injected <style> elements or style attributes.

Requests

The script makes three kinds of requests. None carries page text, query strings, hashes or anything read from the DOM.

RequestWhenSendsNotes
GET https://api.aon.pro/v1/public/placements/{placement_id}/setupOnce per page load, after the placement id is fixed. No credentials.The placement id in the pathReturns type, status and content_assistant_setup (styles, with_prompt, with_ask_box). A valid response is stored in localStorage as aon:setup:<placement_id>. With a cached copy, the cached setup controls the current load and the fresh response only updates the next one, so a portal change shows on the second load. 404, a status other than live or a type other than content_assistant means there is no setup: an uncached load draws nothing, and the cache is cleared so the next load draws nothing either.
POST https://plugin.aon.pro/api/page-contexts/resolveOn HTTPS pages: on load, again if init({ lang }) changes the locale or the page is restored from the back-forward cache; polled while the answer is PENDING. No credentials.submitted_url = origin + pathname; semantic_context.locale when the language tag is validThis is how AON reads the page. READY carries one to five suggested questions and the prompt text; anything else, including network errors and 403/429/503, degrades to "no suggestions" without touching the host page. Never sent on HTTP pages.
https://plugin.aon.pro/panel/…When the panel is first opened, or earlier with data-prewarmThe placement id (aon_pid) on the iframe URL; language, theme and your input go to the panel by postMessageAn ordinary cross-origin document, so it may carry plugin.aon.pro cookies. It talks to its own backend; every offer query it makes carries X-AON-Placement-Id with your placement id.

Two localStorage keys may be written on your origin: aon:setup:<placement_id> (above) and aon:did:<publisher_id> (a random, persistent device identity created the first time the panel connects). Neither contains data from your page.

JavaScript API

window.AON has exactly four keys: version, init, placement and chat. version, placement and chat are read-only, and placement and chat are frozen objects. Methods never throw: init and placement ignore invalid values, chat.open with non-object options behaves as open() and warns, and invalid input fields come back as invalid in the result event.

AON.version

The script's version string.

AON.init(config)

Publisher-level configuration. Every field is optional and a missing field keeps its current value; the tag's data-* attributes go through the same call before window.AON exists.

FieldTypeEffect
placementIdstringSame as data-placement-id. Honoured only before the id is fixed (see The tag).
langstringSame as data-lang. A new value re-reads the page for that locale and applies to the next panel session; an open panel keeps its language.
prewarm"off" | "idle" | "eager"Same as data-prewarm.
publisherIdstringSame as data-publisher-id. Takes effect only before the panel first connects.
debugbooleanSame as data-debug.

AON.placement

Manual control of the two web styles. These calls always work, whatever the portal switches say, and they take precedence: once you have called one of them on a page, the script no longer draws that style automatically there. Each returns a function that removes what the call drew and nothing else.

// Floating button: "icon" alone, or "prompt" = icon plus the suggestion bubble.
const removeButton = AON.placement.floating("prompt");
 
// End of content, into a container you own. "ask" = questions + text box, "buttons" = questions only.
const removeSpot = AON.placement.endOfContent({
  container: document.querySelector('[data-aon-slot="end-of-content"]'),
  variant: "ask",
});

floating keeps at most one button on the page; a second valid call replaces the first. endOfContent draws only when AON has read the page and clears itself when that reading stops being valid.

AON.chat

Direct control of the chat panel, for sites that build their own entry points. The placement styles use these same methods.

MethodReturnsBehaviour
open(options?)input id or undefinedShows the panel. With options.input the message is submitted in the same call. options.presentation sets lang and a theme (accent, radius) for this open. options.awaitInput: true opens a "preparing" state that waits for a following push.
push(input)input idSubmits host input without changing visibility. Before the first open it is buffered and sent when the panel opens. In a visible conversation it appears as a tappable suggestion rather than being sent. In a panel hidden by close() it is dropped (result with reason hidden).
close()—Hides the panel and keeps the conversation. Submissions still in flight are dropped without a result.
destroy()—Removes the iframe and the conversation, and clears anything buffered before the first open. Event subscriptions survive; a later open starts fresh. The div[data-aon-chat] shell stays.
isOpen()booleanWhether the panel is visible right now.
on(event, handler)unsubscribe functionEvents: opened, closed ({ reason: "host-close" | "visitor-close" | "destroy" }), result ({ inputId, results: [{ field, outcome, reason? }] }, at most one per submitted input). Handlers run asynchronously; one throwing handler does not affect the others.

input is a plain object:

FieldTypeMeaning
messagestringThe visitor message to submit, trimmed, up to 200 Unicode code points. This is the only field the assistant reads.
refs.page{ url } | nullOverride the page URL AON reads. The script already uses origin + pathname; set this only for hash or query routing, a canonical URL, or null to opt a page out.
refs.category{ id } | nullA canonical AON taxonomy id to scope offers. Accepted but not applied yet (outcome: "dropped").
refs.offer{ id } | nullReserved (outcome: "unsupported").
attribution{ placementId?, trigger? }Analytics labels only (trigger: click, auto, api). Offer attribution comes from the tag's placement id, not from here.
const inputId = AON.chat.open({
  input: { message: "Which of these laptops is best for video editing?" },
});
AON.chat.on("result", ({ inputId: id, results }) => {
  if (id === inputId) console.log(results);
});

Panel languages

data-lang, init({ lang }) and presentation.lang accept the same set, case-insensitively. For Chinese, a Hant script subtag or a TW, HK or MO region selects Traditional; anything else is Simplified. Other tags are matched on their primary subtag (pt-BR → pt); an unknown tag falls back to English. The assistant's answers follow the language the visitor writes in, not this setting.

TagLanguage
enEnglish
zh-CN, zh-Hans, zhSimplified Chinese
zh-TW, zh-HantTraditional Chinese
jaJapanese
koKorean
idIndonesian
msMalay
thThai
viVietnamese
fil, tlFilipino
esSpanish
pt, pt-PTPortuguese
deGerman
frFrench
hiHindi
bnBengali
ruRussian
arArabic
trTurkish

Attribution

The placement id on the tag is the only attribution you need. It travels with the panel to the chat backend as aon_pid, which adds X-AON-Placement-Id to every offer query it makes, so impressions, clicks and conversions land on the placement's application in the Developer Portal. Conversions arrive on the application's conversion Webhook.

Verify an integration

  1. curl -s https://your-site/page | grep -c 'data-placement-id="plc_…"' returns 1, and the same for data-aon-slot="end-of-content".
  2. curl -s https://api.aon.pro/v1/public/placements/plc_…/setup returns "status": "live" and a non-empty styles list.
  3. On the live page, the floating button appears and opens an <iframe title="AON"> whose src contains aon_pid=plc_…. The End of content spot fills with questions once AON has read the page; a page AON has never seen can take a while on its first visit.
  4. For details, open the browser console with the Verbose level: loader messages start with [AON].

The Developer Portal's Copy for LLM button on the placement page gives a coding agent this integration as a runbook, including a Playwright script that performs the checks above.