Skip to content

Compass Web SDK: Integration Guide

Audience: Front-end and site developers integrating a publisher site with the Compass Web SDK.

The Compass Web SDK β€” published as @arcxp/compass-sdk and exposed in the browser as the global ArcCompass β€” is Arc XP’s first-party, client-side library for collecting reader interaction events (page views, video progress, clicks, and more) and posting them to the event collector. These events train the recommendation models.

It ships as a single bundled JavaScript file for <script> inclusion, and also as an npm ESM module for build-pipeline consumers (React / Next). The target is a roughly one-hour integration: paste a snippet, call init() once, resolve consent, and call track() for each event.

Quickstart

<script>
(function (w, d) {
w.ArcCompass = w.ArcCompass || { q: [] };
["init", "setConsent", "setUserId", "track", "trackExposure", "flush"].forEach(function (m) {
w.ArcCompass[m] = w.ArcCompass[m] || function () { w.ArcCompass.q.push([m, [].slice.call(arguments)]); };
});
// getUserId returns a value, so it can't ride the queue: until the bundle
// attaches there is no identifier yet, so the stub returns null.
w.ArcCompass.getUserId = w.ArcCompass.getUserId || function () { return null; };
// observeRecommendations returns a handle: queue the call and return a
// deferred handle that wires to the real detector once the bundle loads.
w.ArcCompass.observeRecommendations = w.ArcCompass.observeRecommendations || function (options) {
var record = { disconnected: false, real: null };
w.ArcCompass.q.push(["observeRecommendations", [options, record]]);
return { disconnect: function () { record.disconnected = true; if (record.real) record.real.disconnect(); } };
};
var s = d.createElement("script");
s.async = true;
s.src = "https://arc-rec.app-recommend.aws.arc.pub/arc-rec/v1/compass-sdk.iife.min.js";
d.head.appendChild(s);
})(window, document);
window.ArcCompass.init({
// Events collector base path for your org. The SDK appends `/events/batch`.
domain: "https://<org-id>-config-prod.api.arc-cdn.net/collect-web/v1",
apiKey: "<arc-delivery-token>",
});
// Single collect-for-personalization gate: pass true once your CMP reports
// consent. Nothing is sent or persisted until then.
window.ArcCompass.setConsent(true);
window.ArcCompass.track("page_view");
</script>

The rest of this guide walks through each step.

1. Provision a delivery token

Provision a delivery token through the Arc XP Delivery API β€” see Provisioning tokens through the Delivery API. The SDK sends this token as the X-API-Key header on every request.

Use a key scoped to the browser events collection (the collect-web API). That key can post events but cannot reach the content catalog, so it is safe to embed in page JavaScript.

2. Add the snippet

Place this block in <head>, before any code that calls ArcCompass.*:

<script>
(function (w, d) {
w.ArcCompass = w.ArcCompass || { q: [] };
["init", "setConsent", "setUserId", "track", "trackExposure", "flush"].forEach(function (m) {
w.ArcCompass[m] = w.ArcCompass[m] || function () { w.ArcCompass.q.push([m, [].slice.call(arguments)]); };
});
// getUserId returns a value, so it can't ride the queue: until the bundle
// attaches there is no identifier yet, so the stub returns null.
w.ArcCompass.getUserId = w.ArcCompass.getUserId || function () { return null; };
// observeRecommendations returns a handle: queue the call and return a
// deferred handle that wires to the real detector once the bundle loads.
w.ArcCompass.observeRecommendations = w.ArcCompass.observeRecommendations || function (options) {
var record = { disconnected: false, real: null };
w.ArcCompass.q.push(["observeRecommendations", [options, record]]);
return { disconnect: function () { record.disconnected = true; if (record.real) record.real.disconnect(); } };
};
var s = d.createElement("script");
s.async = true;
s.src = "https://arc-rec.app-recommend.aws.arc.pub/arc-rec/v1/compass-sdk.iife.min.js";
d.head.appendChild(s);
})(window, document);
</script>

The snippet defines a tiny stub on window.ArcCompass so calls made before the bundle loads queue up and replay in order.

The …/arc-rec/v1/… URL is major-pinned, so you receive backwards-compatible fixes automatically. For production, add Subresource Integrity (integrity + crossorigin) as shown in Distribution, which also covers exact-version pinning, the npm (ESM) install, and the self-host fallback.

3. Initialize the SDK

ArcCompass.init({
domain: "https://<org-id>-config-prod.api.arc-cdn.net/collect-web/v1",
apiKey: "<arc-delivery-token>",
});

domain is the browser events collector base path for your org: the shared <org-id>-config-prod.api.arc-cdn.net hostname followed by the collect-web/v1 prefix. The SDK appends /events/batch, so events post to https://<org-id>-config-prod.api.arc-cdn.net/collect-web/v1/events/batch. Arc provides the exact base URL during onboarding.

Translate whatever your CMP returns into the SDK’s single yes/no gate:

ArcCompass.setConsent(true); // collect-for-personalization granted

Events that fired before this call are flushed once consent resolves. Nothing is sent or persisted until then. See Consent & Identity for the full state machine.

5. Add the content and site meta tags

Add these tags to each page’s <head> so the SDK can attribute events to the right content and Site:

<!-- Required. The content item this page represents; it must match the ID the
content was ingested under. -->
<meta name="arc-compass:item-id" content="ARTICLE-ABC123" />
<!-- Optional. The Site this page belongs to β€” multi-site tenants only. -->
<meta name="arc-compass:site-id" content="the-daily" />

item-id must be present for automatic page_view capture to fire. You can override either value for a single event with track(type, { itemId, siteId }). See Events & Reliability for the full resolution order.

6. Track events

ArcCompass.track("video_progress", { itemId: "VIDEO123", eventValue: 0.75 });

page_view is captured automatically on load and on SPA route changes β€” you do not need to call track("page_view") yourself unless you disable auto capture (autoPageView: false). item_id and the optional site_id come from the meta tags you set above; pass itemId or siteId to track() to override them for a single event. See Events & Reliability.

7. (Optional) Set a known user ID after login

ArcCompass.setUserId("user-abc-123");
// Pass null on logout to revert to the persistent anonymous ID.
ArcCompass.setUserId(null);

A stable, host-managed ID is what makes personalization work across devices. See Consent & Identity.

Configuration reference

ArcCompass.init(options) accepts the following options. Only domain and apiKey are required.

OptionTypeDefaultDescription
domainstringβ€” (required)Events collector base path for your org, e.g. https://<org-id>-config-prod.api.arc-cdn.net/collect-web/v1. No trailing slash. The SDK appends /events/batch.
apiKeystringβ€” (required)Arc-provisioned delivery token scoped to the browser events (collect-web) collection. Sent as X-API-Key.
cookieDomainstringinferred eTLD+1 (e.g. .host.com)Cookie Domain attribute for the anonymous-ID cookie. Set explicitly for multi-subdomain sites.
batch.maxEventsnumber20Flush when the buffer reaches this many events.
batch.flushIntervalMsnumber2000Flush this many milliseconds after the last flush.
queue.maxBufferednumber500Hard cap on in-memory queued events. Oldest are dropped on overflow with a console.warn.
autoPageViewbooleantrueFire page_view automatically on load and SPA route changes, reading item_id from the <meta name="arc-compass:item-id"> tag (and the optional site_id from <meta name="arc-compass:site-id">). Set false to call track('page_view', { itemId }) yourself.
attributionRootElement | string | falsedocument.bodyRoot under which the SDK scans for host-rendered recommendations carrying the data-arc-compass-* binding β€” also the click-delegation container, so tagged elements (and elements passed to trackExposure) must live inside it. Accepts an element or a CSS selector (resolved once the DOM is ready). Narrow it to the container that holds recommendations to avoid a page-wide MutationObserver; pass false to disable the scan entirely. A structurally-broken root (invalid selector, or a non-Element value) disables the binding with a console.warn; a valid selector whose container has not mounted yet is deferred and attaches on the next trackExposure. See Recommendation Attribution.
debugbooleanfalseLog lifecycle and transport diagnostics.
onError(e: ArcCompassErrorInfo) => voidβ€”Called when the SDK abandons a batch (a terminal 4xx/429, or a 5xx/network failure that exhausts retries). Use it to forward otherwise-silent data loss to your observability. Runs in a detached flush context, so it never interrupts the page; anything it throws is swallowed. See Error visibility.

Error visibility

The SDK is fire-and-forget: track() never throws, and a batch that the collector rejects (or that never gets through) is dropped rather than surfaced to the page. To make those drops observable, wire onError to your tooling β€” it fires only for terminal transport failures, off the user’s interaction path:

ArcCompass.init({
domain: "https://<org-id>-config-prod.api.arc-cdn.net/collect-web/v1",
apiKey: "<collect-web token>",
onError: (e) => {
// e: { message, reason: "rejected" | "exhausted", status?, droppedEvents, eventTypes, cause? }
Sentry.captureException(e.cause ?? new Error(e.message), { extra: e });
// or: datadogRum.addError(e.cause ?? new Error(e.message), e);
},
});

reason: "rejected" is a 4xx/429 the collector refused (fix the payload or key β€” retrying won’t help); reason: "exhausted" means repeated 5xx/network failures used up the retry budget. status is absent for network/CORS errors (no response). Per-event issues (an unresolved item_id, a coerced event_value) are logged only under debug, not through onError.

When cookieDomain is omitted, the SDK falls back to the last two labels of location.hostname (e.g. www.example.com β†’ .example.com). This is correct for most single-suffix TLDs but cannot handle multi-segment public suffixes (e.g. .co.uk, .com.au, GitHub Pages) β€” set cookieDomain explicitly in those cases.

Browser support

The SDK assumes baseline support for fetch with keepalive: true, crypto.randomUUID (with a fallback when absent), visibilitychange, and localStorage / sessionStorage / cookies.

Tested matrix:

  • Chrome / Edge / Chromium β‰₯ 90
  • Safari / WebKit β‰₯ 14
  • Firefox β‰₯ 88

The SDK is browser-only. The IIFE bundle is safe to include in a server-rendered page β€” it no-ops on import when window is undefined.

See also