Integration docs

How to embed the SmartAudiences widget and handle messages from the parent page. Prototype / pseudo-documentation.

Embed URL

Serve the atomic widget at:

http://localhost:3000/embed/widget?apiKey=<PUBLIC_KEY>&campaign_id=<required>&info=<optional>&font=<GoogleFontName>&bg=<hex>&text=<hex>&button=<hex>&buttonText=<hex>&poweredBy=1&setupNotice=1&prompt=<urlencoded>

Query parameters

Param Required Description
apiKeyYesPublic API key. In this prototype any non-empty value is accepted; Apex uses the seeded demo key.
campaign_idYesDSP campaign identifier. Widget shows an error and will not run if omitted. Echoed on postMessage and webhooks.
infoNoOpaque URL-encoded string (up to ~2KB). Stored and returned as-is on postMessage and webhooks.
setupNoticeNoShow the “custom audiences take extra time” caption. 1 (default) or 0.
fontNoGoogle Font family name (default: Instrument Sans).
bgNoBackground color hex (default #ffffff).
textNoText color hex.
buttonNoPrimary button color hex.
buttonTextNoButton label color hex.
poweredByNoShow centered “Powered by inMarket” footer. 1/true (default) or 0/false. Widget UI is otherwise white-label (no SmartAudiences badge).
promptNoPrefills the audience description textarea.

Example iframe

<iframe
  id="sa-widget"
  src="http://localhost:3000/embed/widget?apiKey=a1b2c3d4-e5f6-4789-a012-3456789abcde&campaign_id=demo-campaign&font=Instrument%20Sans&bg=%23ffffff&text=%23212529&button=%2343A047&buttonText=%23ffffff&poweredBy=1&setupNotice=1"
  title="Audience builder"
  style="width:100%;min-height:720px;border:0;"
></iframe>

postMessage contract

When the user clicks Save audience, the widget posts to window.parent:

{
  "source": "smartaudiences",
  "type": "audience.saved",
  "version": 1,
  "payload": {
    "audienceId": "<uuid>",
    "name": "pickleball players",
    "package": "large",
    "devices": 37430954,
    "description": "people who played pickleball",
    "timeWindow": "1w",
    "apiKey": "<public-key-used>",
    "comments": "",
    "refresh": "once",
    "campaign_id": "camp_abc123",
    "info": "",
    "staged": true
  }
}

package is one of: small, medium, large, xlarge. Save stages the audience; generation starts when the DSP launches the campaign.

Optional events

Parent listener example

const SA_ORIGIN = "http://localhost:3000";

window.addEventListener("message", (event) => {
  // Production: always verify origin
  if (event.origin !== SA_ORIGIN) return;

  const data = event.data;
  if (!data || data.source !== "smartaudiences") return;

  if (data.type === "audience.saved") {
    const { audienceId, name, package, devices } = data.payload;
    // Attach to your campaign draft / form state
    console.log("Audience saved", audienceId, name, package, devices);
  }

  if (data.type === "audience.selected") {
    // Optional: highlight package choice before final save
  }

  if (data.type === "widget.ready") {
    // Optional: hide loading shell
  }
});

Webhooks

Configure a single HTTPS endpoint on the Webhooks page. After the DSP launches a campaign, each staged audience is generated asynchronously. When an audience is ready, we POST one event per audience to that URL.

This prototype: Send test event on the Webhooks page may POST to your URL. The Apex mock does not POST across apps — it only simulates completion locally.

{
  "source": "smartaudiences",
  "type": "audience.generated",
  "version": 1,
  "payload": {
    "campaign_id": "camp_abc123",
    "audience_id": "aud_…",
    "info": "",
    "delivery": {
      "method": "s3",
      "url": "https://files.inmarket.com/audiences/aud_….csv"
    }
  }
}

If delivery is SFTP, delivery is:

{
  "method": "sftp",
  "host": "sftp.example.com",
  "path": "/inbound/audiences/aud_….csv",
  "audience_id": "aud_…"
}

Signing (optional)

If you set a signing secret, requests include:

X-SmartAudiences-Signature: sha256=<hex(hmac-sha256(secret, raw_body))>

Verify the HMAC of the raw POST body before trusting the event.

Parent-aware behavior

Public key vs secret

The public API key is safe to place in the iframe src. The secret must never go in the embed — it would be visible to end users.

Future design: when a user presses Generate, the widget (or parent) calls the DSP’s backend, which holds the secret and authenticates the generate request with SmartAudiences. That prevents stripping the iframe and calling generate from arbitrary sites. See devnotes/future-apis.md in the monorepo.

Demo keys caveat

Important: Keys created in this portal are not wired into the Apex DSP mock. Apex always uses the seeded public key: a1b2c3d4-e5f6-4789-a012-3456789abcde. This keeps the local demo reliable without cross-app key sync. Documented in devnotes/demo-keys.md.