Crelora Mark SDK
Mark is Crelora's lightweight attribution SDK for capturing user journeys, conversions, and consent across browsers and server-side runtimes. The npm package includes both browser and Node entry points so you can send consistent data from any surface.
Use it to feed OneLence with first‑party behavioral and conversion data so you can build analytics, insights, signals, and decisions on top of a unified event stream.
API keys and documentation
- Keys: Publishable keys (
pk_…) for browser and secret keys (sk_…) for server-side use are available in the OneLence dashboard: API keys.
- Guides: For integration patterns, alternative integration types (e.g. server-only, tag managers), and deeper technical documentation, see the Integrations overview.
Installation
npm install @crelora/mark
yarn add @crelora/mark
Runtime Imports
- Browser runtime:
import { Mark } from '@crelora/mark'
- Node runtime:
import { createNodeMark } from '@crelora/mark/node'
CDN / Script Tag Usage
You can also drop the SDK directly into your browser applications via a script tag. This exposes the global window.Mark object.
<script src="https://unpkg.com/@crelora/mark@latest/dist/browser.umd.js"></script>
<script>
Mark.init({
key: 'pk_xxxxx',
});
Mark.track('Page View');
</script>
Browser Quickstart
import { Mark } from '@crelora/mark';
Mark.init({
key: 'pk_xxxxx',
require_consent: 'auto',
cross_domain: { cookie_domain: '.example.com' },
site_id: 'uuid-...',
site_host: 'shop.example.com',
autocapture: { pageview: true },
});
Mark.identify('user_123', {
email: 'customer@example.com',
display_name: 'John Doe',
language: 'en-US'
});
Mark.track('Checkout Started', { value: 12900, currency: 'usd' });
Mark.setConsent('granted');
Mark.track('Conversion', {
site_id: 'different-site-uuid',
site_host: 'other.example.com',
value: 5000,
});
Mark.init should be called once during app bootstrap. Subsequent calls to track, identify, or setConsent reuse the same client instance.
Node / Server Usage
import { createNodeMark } from '@crelora/mark/node';
const mark = createNodeMark({
key: process.env.MARK_SECRET_KEY!,
site_id: process.env.MARK_SITE_ID,
site_host: process.env.MARK_SITE_HOST,
});
mark.track('Server Conversion', {
visitor_id: 'vis_abc',
order_id: 'ord_789',
value: 19900,
});
The Node factory accepts optional custom storage or transport adapters so you can plug the SDK into queues or serverless environments.
Available Methods
Mark.init(config) / createNodeMark(config) – bootstraps the client with your publishable or secret key.
track(eventName, payload?) – records custom events with arbitrary properties (numbers, strings, arrays, objects). Use site_id and site_host for per-event site overrides.
conversion(eventName, payload?) – records conversion events (same endpoint path as track, with is_conversion: true for backend compatibility).
identify(userId, traits?) – ties a known user identifier to previous anonymous activity. Recommended traits: email, display_name, language. Custom traits are also supported.
setConsent(status) – enforces consent gating; pass 'granted' or 'denied'.
getVisitorId() – returns the current pseudonymous visitor ID when available. On the browser, returns undefined until consent is granted when require_consent is set. Use it to send the ID to your backend (e.g. in a header or body) for server-side attribution when you don't have an authenticated user ID.
flush() – flushes queued/persisted delivery items.
reset() – clears user/session/attribution state and rotates visitor identity for logout flows.
getStats() – returns runtime delivery stats { queued, sent, failed, dropped }.
Reserved SDK fields (for example event_name, user_id, consent_state, and internal identity metadata) are sanitized from user payloads/traits and cannot override SDK-managed values.
Automatic Attribution Tracking
The SDK automatically captures and persists the following URL parameters:
- UTM keys:
utm_source, utm_medium, utm_campaign, utm_term, utm_content
- Referral aliases:
ref, referral, affiliate_id (normalized to ref)
- Generic IDs:
click_id, ch_click_id, cid, campaign_id
- Paid platform IDs:
gclid, gbraid, wbraid, dclid, msclkid, fbclid, ttclid, twclid, li_fat_id
These parameters are included in every tracked event to ensure proper attribution.
Capture is evaluated at SDK initialization and refreshed before each auto page view emit when autocapture.pageview + route tracking are enabled.
- If consent is not yet granted (
require_consent: true or 'auto'), attribution is kept in runtime memory and not persisted.
- After consent is granted, pending in-memory attribution and current URL attribution are persisted.
Custom Query Param Capture
You can expand the default attribution allowlist or opt into full query capture:
Mark.init({
key: 'pk_xxxxx',
capture_query_params: ['sub_id1', 'sub_id2', 'publisher_code'],
});
Configuration Reference
All config options use snake_case. Stored event payloads and database columns match 1:1.
key | string | Publishable (browser) or secret (server) key from OneLence API keys. |
debug | boolean | Enables verbose console logging to help with integration tests. |
before_send | (event) => event | null | Mutate/redact payloads before send, or return null to drop events. |
on_error | (error, event?) => void | Hook for transport and queue failures. |
sample_rate | number | Fraction (0..1) for track event sampling. identify and conversion are never sampled. |
honor_dnt | boolean | When true, blocks tracking when browser DNT/GPC is enabled. |
session_timeout_ms | number | Inactivity window for rotating session_id (default 30 minutes). |
request_timeout_ms | number | HTTP timeout per request in milliseconds (default 10000). |
rotate_visitor_on_consent_change | boolean | Rotate visitor_id after denied -> granted transition. |
batching | { enabled?: boolean, max_size?: number, flush_interval_ms?: number, endpoint_path?: string } | Optional batch mode (/events by default). |
require_consent | boolean | 'auto' | true blocks tracking until consent is granted, 'auto' requires stored granted consent and treats missing consent as denied, default false ('auto' recommended for production). |
consent_source | { type: 'tcf', purposes: number[] } | Optional IAB TCF v2 integration: the SDK listens for CMP updates and only allows tracking when the listed numeric purpose IDs are consented. Combine with require_consent and setConsent as your legal team requires. |
autocapture | { pageview?: boolean, click?: boolean | { selector?: string }, form_submit?: boolean, outbound_link?: boolean, scroll_depth?: boolean, web_vitals?: boolean } | Auto-capture toggles for page views and optional interaction/perf signals. |
track_route_changes | boolean | When autocapture.pageview is true, also emits on SPA route changes (pushState/replaceState/popstate); defaults to true. |
include_page_context | boolean | When true (default), enriches events with page, title, referrer, site (full url is only sent if you pass it explicitly in payload). |
cross_domain | CrossDomainConfig | Controls cookie domain, bridge URL, and allowlist for multi-domain attribution. |
site_id | string | Optional UUID for associating events with a registered site. Included in all event payloads as site_id. |
site_host | string | Optional site host for audit/debug purposes. Included in all event payloads as site_host. If not provided, browser SDK uses window.location.host. |
capture_query_params | string[] | Additional query keys to capture for attribution (merged into the default allowlist). |
capture_all_query_params | boolean | Capture all query params after denylist/limits. Defaults to false. |
query_param_denylist | string[] | Query keys that are never captured (exact-key matching; default includes sensitive keys like email, token, password). |
max_captured_query_params | number | Maximum number of captured query keys stored per visitor (default 30). |
max_query_param_value_length | number | Maximum stored length per captured query value (default 256). |
Server runtimes can also pass storage, storageDefaults, or transport via createNodeMark to fully control persistence and delivery.
Consent & Privacy
These behaviors matter for compliance-sensitive setups (GDPR-style consent, CMPs, enterprise security reviews):
- Consent gating: Use
require_consent: true or 'auto' so events and identify only run after a positive consent signal. In 'auto' mode, missing consent is treated as denied until setConsent('granted').
- TCF v2: Set
consent_source: { type: 'tcf', purposes: [/* IAB purpose IDs */] } so tracking follows your CMP’s current purpose consents (the SDK subscribes to CMP updates rather than relying on a one-time read).
- Withdrawal:
setConsent('denied') stops tracking and clears stored attribution plus cookie-backed visitor identity where applicable.
- Stricter identity hygiene: Enable
rotate_visitor_on_consent_change if you want a fresh visitor_id after a denied → granted transition.
- DNT / GPC:
honor_dnt: true blocks tracking when the browser reports Do Not Track or Global Privacy Control.
- Data minimization: Use
before_send to strip or redact fields before they leave the client; use on_error for observability without logging raw payloads.
- Payloads cannot bypass consent via event properties; reserved fields are sanitized.
- Pre-consent attribution: URL attribution is held in memory only until consent is granted, then persisted.
- Cross-domain: First-party iframe bridges keep identifiers under your control.
- Delivery without long-lived local queues: Failed sends can be retried from a browser outbox with a 48-hour TTL; on tab hide / unload, pending items are flushed with
sendBeacon where possible to improve delivery without weakening consent checks.
- IP / geo: IP is not read in the browser; it is taken server-side, hashed, and used for coarse geo only when allowed by consent and tenant settings.
For product-level privacy commitments and processor terms, rely on your OneLence agreement and documentation; this README describes SDK behavior only.
Visitor ID for server attribution
When you don't have an authenticated user ID, you can pass the SDK's visitor ID to your backend so server-side events (e.g. checkout, webhooks) are attributed to the same visitor as browser events.
Browser: Call Mark.getVisitorId() after init. If you use require_consent: true or 'auto', it returns undefined until consent is granted. Once available, send it in a header or request body to your API and use it as the visitor_id when calling the Node SDK or your ingestion.
Node: Call mark.getVisitorId() to read the visitor ID from the storage you passed to createNodeMark (e.g. from storageDefaults.visitor_id). Use it to associate server events with the same visitor.
The visitor ID is a pseudonymous, SDK-scoped identifier. Do not use it as a cross-site or long-term user identity; use it only for joining browser and server events within your attribution flow.
User Identification
The identify() method links anonymous visitors to known users. Recommended traits:
email (string) - User's email address. Will be hashed server-side for privacy.
display_name (string) - User's display name or full name.
language (string) - User's preferred language code (e.g., 'en', 'en-US', 'fr').
phone (string) - User's phone number. Will be hashed server-side for privacy.
You can also include any custom traits as key-value pairs. All traits are stored in the user profile and can be used for segmentation and personalization.
Mark.identify('user_123', {
email: 'customer@example.com',
display_name: 'John Doe',
language: 'en-US',
plan: 'premium',
signup_date: '2024-01-15',
});
Page Views
- Event name:
page_view (canonical). Use display names in your product UI if you prefer human-readable labels.
- Autocapture (optional): set
autocapture: { pageview: true } (and optionally track_route_changes: true) to emit on first load and SPA route changes. If consent is required and not yet granted, initial pageview is deferred and emitted once consent is granted.
- All SDK config and event fields use snake_case (
site_id, site_host, etc.) and map directly to stored payloads and database columns.
Support
Need help? Reach out through your account team or file a ticket via the OneLence dashboard. Please include the SDK version, runtime (browser or Node), and any reproduction steps so we can assist quickly.