Developer

Runtime API

Reference the Apex SDK runtime functions for initialization, tracking, consent, flags, diagnostics, and assignment state.

Import runtime functions from @drip-apex/sdk, or call the browser globals after the hosted script has loaded. The globals are compatibility surfaces, but they do not all have the same shape.

Browser global surfaces

SurfaceShape and function names
window.DripThe hosted IIFE's module namespace. It exposes the named SDK exports, including init, track, trackStep, trackGoal, trackRevenue, setConsent, flush, getAssignments, getExperiments, getRuntimeTools, and the flag and diagnostics functions documented below.
window.dripThe callable queue-compatible runtime API. It exposes init, track, step and its additive alias trackStep, goal/revenue/consent/lifecycle methods, flush, assignment/cart/experiment helpers, getTools and its additive alias getRuntimeTools, get, flag evaluation, signals, diagnostics, and push.
window.ApexThe same callable runtime API object as window.drip, with the same function names.
window.apexToolsThe runtime-tools object itself, not the callable API. It exposes custom-JS and page-trigger helpers such as waitForElement, elementInView, and domChanged; do not call getRuntimeTools on this object.

The tables below use the npm/window.Drip export names. On window.drip and window.Apex, trackStep/step and getRuntimeTools/getTools are equivalent aliases.

Initialization and state

FunctionSignatureUse
init(config: InitConfig) => Assignment[]Initialize the SDK with inline experiments or fetched config.
reInit(forceReapply?: boolean) => Assignment[]Re-evaluate assignments manually, optionally forcing mutations to reapply.
recheckPrerequisites() => voidRe-run project prerequisite gates and start or stop analytics if consent or state changed.
getAssignments() => Assignment[]Read current active assignments.
getExperiments() => Experiment[]Read loaded experiment configs.
getRuntimeTools() => ApexRuntimeToolsGet the runtime tools object used by custom JS and page triggers.
getDiagnostics() => SdkDiagnosticsRead public diagnostics; debug-only fields appear only when debug mode is enabled.
getCartAttributes() => CartAttributeEntry[]Build the authoritative cart-attribute attribution stamp for a headless Shopify cart.
getCartMetafields(options?: { namespace?: string }) => Array<{ key: string; type: string; value: string }>Build an optional BI mirror for cartMetafieldsSet. key is composite (namespace.attributeKey); namespaces accept 2–255 ASCII letters, digits, _, or -, and invalid values fall back to apex. See the headless integration guide.
getCartMetafieldStaleKeys(options?: { namespace?: string }) => string[]Return every mirrorable composite key missing from the current getCartMetafields() output. Pass these keys to cartMetafieldDelete to clear stale optional mirror values. Use the same namespace option for both helpers.

Tracking

FunctionSignatureUse
track(type: string, data?: Record<string, unknown>, experimentId?: string, variationId?: string) => voidSend a custom event and trigger matching custom goals. Defaults to the first active assignment when IDs are omitted.
trackStep(name: string, data?: Record<string, unknown>) => voidSend a virtual funnel step as funnel_step; dedupes once per session unless data.oncePerSession is false.
trackGoal(goalId: string, data?: Record<string, unknown>) => voidTrigger a configured Apex goal by ID for current assignments.
trackRevenue(revenue: number, data?: RevenueEventData) => voidRecord each purchase at shop scope and attribute it to every running test assigned to the visitor.
flush() => Promise<void>Start analytics if ready and flush queued events. Resolves immediately when no tracker exists.
ts
import { flush, track, trackGoal, trackRevenue, trackStep } from "@drip-apex/sdk";
 
track("add_to_cart", { product_id: "example-product", value: 49 });
trackStep("postcode_submitted");
trackGoal("checkout-started");
trackRevenue(119, { orderId: "order-example-1002", currency: "EUR" });
 
await flush();
FunctionSignatureUse
setConsent(granted: boolean) => voidPersist consent state, clear storage when denied, and recheck assignment/tracking prerequisites.

Feature flags

FunctionSignatureUse
evaluateFlag(flagId: string, context?) => booleanReturn true when the visitor is assigned to a non-control variation.
evaluateFlagValueevaluateFlagValue<T>(flagId, fallback?, context?)Return the assigned variation value, or the fallback when unavailable.
ts
import { evaluateFlag, evaluateFlagValue } from "@drip-apex/sdk";
 
const enabled = evaluateFlag("checkout-progress-ui");
const ctaCopy = evaluateFlagValue("checkout-cta-copy", "Continue to checkout");

Diagnostics

ts
import { getDiagnostics } from "@drip-apex/sdk";
 
const diagnostics = getDiagnostics();
console.log({
  initialized: diagnostics.initialized,
  assignments: diagnostics.assignments,
  configSource: diagnostics.configSource,
  runtime: diagnostics.runtime
});

getDiagnostics() is safe for support tooling because it redacts consent and tracking-block details unless SDK debug mode is enabled.

All builds expose customEventsHeld and tracking. The latter reports whether the tracker is started, whether calls are blocked, the reason (consent_required, dnt, screenshot_mode, or not_started), how many events are waiting behind an activation gate, and whether sent events are QA-tagged. This lets QA distinguish a console call from an event that reached the wire.

Decision timing

Read window.drip.getDiagnostics().load.timing (or the imported getDiagnostics().load.timing) in any build, including a forced preview. The same values are page-readable on window.__dripPerf without enabling debug or analytics.

Diagnostics fieldwindow.__dripPerf fieldMeaning
navigationStartAtnavigation_start_atNavigation origin, epoch milliseconds from performance.timeOrigin, with a Date.now() - performance.now() fallback.
configFetchedAtconfig_fetched_atConfig fetch resolved, epoch milliseconds. Absent when no successful fetch occurred.
decisionAtdecision_atAssignment ready, the existing assignment_ready_at mark, in epoch milliseconds.
firstMutationAtfirst_mutation_atFirst DOM mutation, epoch milliseconds, when one occurred.
revealAtreveal_atAnti-flicker reveal, epoch milliseconds, when recorded.
decisionLatencyMsdecision_latency_msdecisionAt - navigationStartAt, milliseconds since navigation.
forcedExperimentIdsforced_experiment_idsExperiment IDs in the active force map. Empty for natural loads.

The initial page's decisionAt, decisionLatencyMs, and load.assignmentReadyAt remain unchanged on SPA route changes. timing.lastRouteChange is null until a route evaluation completes, then contains { startedAt, decisionAt, decisionLatencyMs }. Both timestamps are epoch milliseconds; its latency is decisionAt - startedAt for that route evaluation, independent of how long the page has been open. The matching window.__dripPerf fields are last_route_change_started_at, last_route_change_decision_at, and last_route_change_decision_latency_ms (all null before the first route evaluation). A route_change sdk_perf event uses this route-local latency; an init event uses navigation-relative latency.

window.__dripPerf.qa_session_id identifies the QA session (also available as getDiagnostics().qaSessionId). Unreached timestamps are absent, not zero. Timing diagnostics do not change consent or telemetry gates. The sdk_perf event includes decision_latency_ms and forced_experiment_ids when telemetry is allowed.

js
const timing = window.drip.getDiagnostics().load.timing;
console.table(timing);
console.log(window.__dripPerf.decision_latency_ms);

The runtime remains unchanged. Natural runtime decides within tens of milliseconds of SDK start when config ships inline with the snippet (measured p50 12 ms / p95 54 ms on a large storefront). QA previews, screenshot-mode previews and cookie-forced sessions take the QA config lane and fetch fresh config over the network (measured p50 1.3 s / p95 7 s); they are not representative of launch behaviour. URL-forced sessions without a qa parameter keep the inline or cached config, so these measurements do not describe them. No config preload or anti-flicker hold is planned.