The card route
GET /card answers 200 { card } exactly once while a card is waiting, then 204 until another is placed. The card is read with a single atomic take, so a refresh cannot show it twice. A card waits three minutes; a visitor who left never sees a stale one. Both routes are origin-gated.
The card object:
cta_path is always a same-origin path; the engine never emits an absolute URL. cta_label and cta_path are null when there is no link.
The ack
event is one of shown, clicked, dismissed. Send shown when the sentence is on the screen. This is the report that makes the visitor count as spoken to; without it, no order can be credited to the moment. Send clicked when the link is followed and dismissed when the visitor closes it.
The poll
The reference snippet starts 12 seconds after the visitor id is known, polls every 6 seconds while the tab is visible, backs off, and stops after 15 minutes or once a card has been delivered. It remembers delivery insessionStorage so a visitor sees one card per session. Follow the same shape if you write your own: never poll a hidden tab, never poll after delivery.
Getting the visitor id
The SDK keeps the visitor id in the frame it opened fromiframe.nextintent.ai. Ask the frame with postMessage({ action: 'find', source: '<nsp>/user_id', id, channel }, ...) where nsp and channel come from the frame’s src query string; the reference snippets show the exact exchange. Accept replies only from https://iframe.nextintent.ai.
The event
Whatever the output setting, the reference snippet fires a DOM event when a card is delivered:output set to event in the facts sheet, nothing is drawn and the snippet sends shown as soon as the event fires; your listener owns the rendering. With output set to card, the event still fires and the card is drawn.
Popup tools
Withoutput set to popup, the snippet reads window.NextIntentOutput = { provider, campaign_id, code_campaign_id } from the page and asks the tool to show that campaign: OptiMonk (OptiMonk.Api.campaigns.show), Justuno (juapp('trackFunnel', 'nextintent:' + id)), Privy (Privy('show', id)), Wisepops (wisepops('event', 'nextintent:' + id)), Klaviyo (_klOnsite.push(['openForm', id])). code_campaign_id is used when the card is about a declined code. If the tool is absent or refuses, the snippet falls back to its own card.
Rendering your own card
Keep three rules: put the text in withtextContent (never HTML), accept cta_path only when it starts with /, and send the shown ack when the element is on the screen. The sentence must never be shown twice in a session.
