Skip to main content

Experimentation and personalization

Appcues

Appcues shows onboarding flows, tooltips and checklists inside your app. The c15t appcues helper waits for experience consent before it sets AppcuesSettings or requests the SDK.

On withdrawal, the helper calls Appcues.reset() and removes the loader, then c15t reloads the page. reset() clears the user and any flow in progress, but the SDK keeps running until the reload, so keep reloadOnConsentRevoked on.

Configure Appcues

Copy your account ID from the Appcues installation snippet. Set region: 'eu' for an EU account. Then remove the Appcues snippet and any tag-manager entry so Appcues can't load outside c15t.

npm install @c15t/integrations
src/consent-scripts.ts
import { appcues } from '@c15t/integrations/appcues';

export const scripts = [
	appcues({
		accountId: 'YOUR_APPCUES_ID',
		onReady: () => {
			// Read the signed-in visitor from your auth state here.
			// Enable only with their real, unique, stable ID; skip if signed out.
			// window.Appcues?.identify?.('CURRENT_USER_ID');
			// After identify, enable with the visitor's real group ID if needed.
			// window.Appcues?.group?.('CURRENT_GROUP_ID');
			// After identify, track only an event that actually happened.
			// window.Appcues?.track?.('Started onboarding');
		},
	}),
];

Inside onReady, read the signed-in user from your auth state. Replace the identify placeholder with that user's real, unique, stable ID before you enable the call. Enable group after identify with the real group ID, if you need it. Send track only for an event that actually happened. Skip these calls when nobody is signed in. onReady runs once per successful loader load, including when consent was already granted at page startup.

If sign-in completes after onReady, identify the user from your app's auth change handler. Right before you call window.Appcues.identify, check that the method exists, the user is still signed in, and c15t's current consent allows both the script's category and the appcues vendor. The category defaults to experience; check your override instead if you changed it. Skip identification if any check fails. If Appcues hasn't loaded yet, let onReady read the current user when it runs.

Register the scripts

Complete your framework quickstart first. Keep its Inth endpoint, policy, styles and consent UI. Remove the vendor's original script, SDK initializer or tag-manager entry, so the vendor loads only through c15t.

The vendor pages put the helper in src/consent-scripts.ts. If your framework quickstart already has a scripts array, such as the one in c15t.config.ts in the Next.js guide, add the helper to that array instead of creating a second file. The scripts export is a configuration, not an initializer. Add it to the c15t provider you already have, at the registration point for your framework below. These are edits to that provider, not a second provider.

Add the configuration to scripts in c15t.config.ts, next to next.config.ts:

import { defineConsentConfig } from 'c15t/next';
import { scripts } from './src/consent-scripts';

export default defineConsentConfig({ scripts });

Keep the rest of your config, such as mode and routePrefix, in the same call. ConsentRoot reads the config in the browser, so the layout keeps passing only state. App Router, Pages Router and static export all read the same file. See Next.js scripts and embeds.

Options

OptionDefaultBehavior
accountIdRequiredAccount ID from Appcues. Surrounding whitespace is trimmed and the ID is encoded as one URL path segment. Missing or blank IDs log an error and skip loading.
region'us'Loads from fast.appcues.com, or from fast.eu.appcues.com for 'eu'.
settings{ enableURLDetection: true }JSON-serializable AppcuesSettings, merged over the default URL detection setting. Replaces any existing window.AppcuesSettings. Set enableURLDetection: false to handle page changes yourself.
scriptUrlhttps://fast.appcues.com/<accountId>.jsOverride for a proxy or custom loader. A blank override uses the default. This changes only the account loader, not later SDK requests.
onReadyNoneSynchronous callback after the account loader sets window.Appcues, while the script still has consent. Runs once per successful loader load. Use it for identify, group and track.

The helper has no category option. To change the consent mapping, override the returned script's category when you register it. The helper has no mode that loads before consent.

Loading and revocation

After experience consent, c15t sets AppcuesSettings and loads https://fast.appcues.com/<accountId>.js, or the EU host for region: 'eu'. The account loader requests the Appcues runtime. Put startup calls to identify, group and track inside onReady. The account loader exposes these methods and buffers calls until the runtime finishes loading, as in Appcues' documented AppcuesReady pattern. c15t doesn't create an AppcuesReady global or queue visitor callbacks. If the loader fails or doesn't set window.Appcues, onReady doesn't run.

Revoking consent or removing a pending loader stops its onReady from running. A new grant starts a new loader. Recreating the helper before the download completes uses the current callback. Recreating it after completion doesn't replay onReady. Read the current visitor inside the callback, and check consent again for later calls. If you start async work inside onReady, check consent and auth again before you call Appcues after an await, timer or event.

On revocation, c15t calls the documented Appcues.reset() when it's available and removes the loader element. Reset clears user state and flows in progress. It doesn't unload the SDK, stop every listener or cancel remote JavaScript that was already downloading. Keep reloadOnConsentRevoked enabled so the next page starts without Appcues. A new grant loads the SDK again, and repeated grants don't add another loader.

Cookies and storage

Appcues documents local and session storage, including the localStorage keys apc_ann_errors, apc_local_id, apc_user_id and apc_next_content_id. Session keys include apc_user, apc_debug_enabled, apc_debug_url, apc_curr_flow, apc_curr_checklist, apc_cl:{id} and apc_followed_test_link. The SDK can change its storage across releases. Reset doesn't promise to erase every Appcues key.

Verify Appcues

In a fresh browser profile, deny all optional consent and allow measurement alone. Check that neither Appcues host is requested, window.Appcues, window.AppcuesSettings and window.AppcuesReady are absent, and no Appcues cookie, localStorage or sessionStorage key appears. Grant experience and check the account loader, the runtime and onReady. Revoke consent while the loader is still downloading and confirm that onReady doesn't run when that download finishes. Revoke experience and confirm the page reloads and the loader stays absent until a new grant.

The automated monitor uses a fake account and checks that the loader is reachable. It doesn't prove that a real account's onboarding flow starts. Use your own account to check flow targeting and identification.

Sources: install the Appcues SDK, JavaScript API and cookies, local storage and session data.

Test in a private window with an opt-in policy. Open DevTools Network, disable the cache and filter by the vendor's domain:

  1. Load the page. No request goes to the vendor before you choose.
  2. Click Reject, then reload. There is still no vendor request.
  3. Open Privacy settings and allow the helper's category. The vendor script loads without a page reload.
  4. Turn the category off again and save. c15t reloads the page, and the new page makes no vendor request.

c15t reloads on revocation because removing a script element does not stop code that already ran. The vendor's listeners, timers and queued events stay alive until the page unloads. If you set reloadOnConsentRevoked: false, stop the vendor yourself. Register a callback-only script whose onConsentChange calls the vendor's opt-out API, as shown in custom integrations, and check the permission before each of your own event calls. The reload does not delete cookies the vendor already set; see clear on revocation for your framework.

The helper lists its vendor by name in the preference dialog, so a visitor can turn it off inside an allowed category. See vendor consent for your framework. The consent verification guide covers navigation, expiry and hosting checks.