CDN integration
Section titled “CDN integration”The CDN integration adds Frak’s rewarded referral program to any website: customers get share links and a share UI, you track purchases, and both the sharer and the friend who bought get rewarded when a sale confirms. No bundler, no build step: one script tag gives you the Frak web components and the full SDK on window.
Prerequisites
Section titled “Prerequisites”- A merchant account on the Frak business dashboard with your domain registered. The main domain is registered at sign-up; add subdomains under Allowed Domains.
- Any HTML page you can add a script tag to.
If you have not done that yet, start with the Get started guide.
Which bundle
Section titled “Which bundle”| Bundle | URL | What it is |
|---|---|---|
| Components | https://sdk.frak.id/components.js | The first-party CDN pointer. Registers the <frak-*> elements, boots the SDK, and exposes the SDK on window.FrakSetup.core. This is the one you want. |
| Core only | https://cdn.jsdelivr.net/npm/@frak-labs/core-sdk@1/cdn/bundle.js | IIFE exposing window.FrakSDK. No UI, no auto-boot. Still served directly from jsDelivr, not on the pointer. Only for a page that drives the SDK entirely by hand. |
The rest of this page uses the components bundle. Swap the host for sdk-dev.frak.id to load
pre-release (beta) builds instead: its onerror fallback below follows to jsDelivr’s @beta
tag. This is the CDN host the bundle is fetched from; do not confuse it with the env config
option further down, which targets the wallet/backend the SDK talks to once it is running.
1. Set the config, then load the script
Section titled “1. Set the config, then load the script”The loader reads window.FrakSetup.config when it boots, so the config object must exist before the script tag runs.
<head> <!-- 1. Configure Frak --> <script> window.FrakSetup = { config: { metadata: { name: "Your Store", currency: "eur", }, }, }; </script>
<!-- 2. Load the components (registers the elements and boots the SDK) --> <link rel="dns-prefetch" href="https://sdk.frak.id"> <link rel="preconnect" href="https://sdk.frak.id"> <link rel="dns-prefetch" href="https://cdn.jsdelivr.net"> <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin> <script src="https://sdk.frak.id/components.js" defer="defer" onerror="var s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/@frak-labs/components@latest/cdn/components.js';s.defer=true;document.head.appendChild(s)" ></script></head>This is a classic deferred script, not type="module". sdk.frak.id is a single-line import()
shim behind a CDN pointer with a 5-minute cache, so a release reaches you in minutes instead of
jsDelivr’s 7-day floating-tag TTL. If it fails to load at all (network error, DNS, outage),
onerror swaps in jsDelivr’s floating tag instead: the pointer file is a single import(), so a
failed load ran nothing and nothing double-runs. The preconnects are asymmetric on purpose: the
pointer’s own fetch is no-cors, so its <link> must not carry crossorigin, while the jsDelivr
fallback’s import() is CORS-mode, so its <link> does: a preconnected socket is only reused
when the credentials mode matches.
Config options
Section titled “Config options”| Field | Default | Meaning |
|---|---|---|
env | "prod" | The environment to run against: "prod", "dev", or { wallet, backend }. |
metadata.name | none | Your application name, shown in modals and SSO. |
metadata.merchantId | resolved from your domain | Your merchant ID (UUID) from the dashboard. |
metadata.currency | "eur" | "eur", "usd", or "gbp". |
metadata.lang | browser language | "en" or "fr". |
metadata.logoUrl | none | Logo used by some components. |
metadata.homepageLink | none | Fallback link used by some components. |
domain | window.location.host | Override only if the page host differs from your registered domain. |
customizations.css | none | URL of a stylesheet applied to the modals and components. |
customizations.i18n | none | Inline translation overrides, per locale or flat. |
waitForBackendConfig | true | Wait for the backend configuration before rendering components. |
attribution | none | Default UTM, via, and ref values appended to sharing URLs. |
preload | ["sharing"] | Views preloaded inside the listener iframe. Pass [] to disable. |
See Configuration and FrakSetup for the full reference.
2. Drop in the components
Section titled “2. Drop in the components”The loader watches the DOM, so an element added at any time registers itself:
<body> <frak-banner></frak-banner> <frak-button-share classname="button"></frak-button-share></body>Every element and attribute is documented in the components reference.
3. Call the SDK from your own code
Section titled “3. Call the SDK from your own code”Once booted, the SDK publishes two things:
window.FrakSetup.client, the client instancewindow.FrakSetup.core, every SDK function and action
The client is created asynchronously, so wait for the frak:client event before using it:
<script> function waitForClient() { if (window.FrakSetup?.client) return Promise.resolve(window.FrakSetup.client); return new Promise((resolve) => { window.addEventListener( "frak:client", () => resolve(window.FrakSetup.client), { once: true } ); }); }
(async () => { const client = await waitForClient();
// Watch the wallet status await window.FrakSetup.core.watchWalletStatus(client, (status) => { console.log(status.key === "connected" ? status.wallet : "not connected"); });
// Open a modal await window.FrakSetup.core.displayModal(client, { steps: { login: {}, final: { action: { key: "reward" } }, }, }); })();</script>Track a purchase
Section titled “Track a purchase”trackPurchaseStatus is the exception: it takes no client, so you can call it as soon as the SDK is loaded.
<script> window.addEventListener("frak:client", () => { window.FrakSetup.core.trackPurchaseStatus({ customerId: "cust_123", orderId: "order_456", token: "a-unique-order-token", }); });</script>Rewards are only released once your backend confirms the order with a signed webhook. See Validate purchases from your backend.
Events
Section titled “Events”| Event | Target | Detail | Fired when |
|---|---|---|---|
frak:client | window | none | The client is ready, window.FrakSetup.client is set |
frak:config | window | the resolved config | The backend configuration is resolved or refreshed |
frak:referral-success | window | none | An inbound referral was processed successfully |
The share query parameter
Section titled “The share query parameter”The loader also handles ?frakAction=share on page load, with optional link, products, and placement parameters. It opens the sharing flow and then strips those parameters from the URL. That is how a link out of an email or a QR code can open the share sheet directly.
Advanced: the core-only bundle
Section titled “Advanced: the core-only bundle”If you do not want the components at all, load the core bundle and create the client yourself.
This bundle is not on the sdk.frak.id pointer: it is still loaded straight from jsDelivr, so pin
the major the same way you would have pinned the components bundle before:
<script src="https://cdn.jsdelivr.net/npm/@frak-labs/core-sdk@1/cdn/bundle.js"></script><script> let clientPromise;
function getClient() { // Call setupClient once. Each call recreates the listener iframe. clientPromise ??= FrakSDK.setupClient({ config: { metadata: { name: "Your Store" } }, }); return clientPromise; }
(async () => { const client = await getClient(); if (!client) return; await FrakSDK.watchWalletStatus(client, console.log); })();</script>window.FrakSDK carries the same functions and actions as window.FrakSetup.core. It exists only for this bundle: the components bundle is ESM and exposes no global.