LoyumiDeveloper docsOpen Sandbox
SERVER + WEB + NATIVE CLIENTS

Integrate faster without weakening identity.

Use typed server and mobile clients, Beta native UI kits, a versioned Web Widget, or a privacy-minimized AI-native rewards surface without weakening identity or value controls.

SDK 2.3.0 · API 1.13.0Stable clients + Beta customer surfacesServer gateway keeps secrets off devices

Use only what is actually published.

TypeScript SDKStable download · 2.3.0

A dependency-free, typed API 1.13 server client with all 121 operations, safe retries, exact ledger-close evidence, 40 webhook event types, Reward Gifts frameworks, and synthetic Shopify and Klaviyo mapping conformance. Public npm-registry availability and provider connectivity are not claimed.

Review the SDK →
React wrapperStable download · 1.0.0

A typed, SSR-safe Web Widget v1 wrapper for React 18.2 and 19 with session handoff, events, theming, and an imperative sign-out control. Public npm-registry availability is not claimed.

Review the React wrapper →
Loyumi CLIStable download · 1.5.1

A guided command line for a persisted official-origin Sandbox proof and common membership, commerce, governed value, import, operation, audit, analytics, ledger-close, webhook, program, export, and widget workflows. It writes API 1.11 project configuration and does not claim command parity with all 121 API 1.13 operations.

Install from the CLI guide →
iOS SDKStable source · 1.1.0

A dependency-free Swift Package for iOS 15+ that calls the merchant's authenticated backend gateway and includes Native UI Kit 1.0.0-beta.1.

Install the iOS SDK →
Android SDKStable source · 1.1.0

A Kotlin library for Android API 23+ that calls the merchant's authenticated backend gateway and includes Native UI Kit 1.0.0-beta.1. Coroutines remain its only runtime dependency.

Install the Android SDK →
Native UI KitsBeta · 1.0.0-beta.1

SwiftUI and Android View components for balance, tier progress, reward cards and catalog, activity, and a composed rewards home. They emit intent; they do not move value.

Build a native surface →
Web Widget v1Available Beta

A versioned browser loader, a Shadow DOM host with a sandboxed hosted iframe, four widget families, guest mode, and signed personalized mode.

Copy the embed →
AI-native rewards surfaceBeta · 1.0

Semantic balance, tier, catalog, and activity components plus confirmed, idempotent entitlement actions under the existing widget session and origin boundary.

Integrate the semantic surface →
GraphQL APIBeta

A POST-only typed surface for selected reads and value lifecycle mutations. It delegates to existing scopes and state machines rather than claiming full API parity.

Open GraphQL guide →
HTTPS JSON APIAvailable

121 environment-scoped API 1.13 operations across 106 paths, represented by OpenAPI 3.1 for trusted server integrations.

Open reference →
Widget Studio publish flowBeta

Configure, preview, validate, publish, pause, and version a Web Widget deployment identifier.

Publish safely →

One current API. Separate package release states.

API compatibility and package maturity are different facts. The checked-in OpenAPI document is the source for the public HTTPS JSON API contract; the rendered API reference uses that same document.

HTTPS JSON API + OpenAPI

Current: 1.13.0. The account-free contract publishes 121 operations across 106 paths and 40 privacy-minimized webhook event types.

GraphQL API

Beta · selected lifecycle. POST-only JSON over HTTPS with checked-in SDL, bounded execution, one top-level mutation, and delegation to the existing scope and value paths. It does not claim 121-operation parity.

@loyumi/sdk

2.3.0 → API 1.13.0. Official stable downloadable ESM server package with all 121 operations, 40 webhook event types, Reward Gifts frameworks, portability 1.6, and merchant-operated Shopify 2026-07 and Klaviyo 2026-07-15 mapping conformance for Node.js 20+ and compatible Web Crypto edge runtimes.

@loyumi/cli

1.5.1 · API 1.11 project config. Stable downloadable release for common integration and operating workflows, including the opt-in official-origin Sandbox proof, with portability 1.6. It does not claim command parity with all 121 API 1.13 operations.

Web Widget

v1 · Available Beta. Use /widgets/v1/ for compatible loader fixes or the immutable /widgets/v1.0.0/ loader with its published SRI. That freezes the merchant-page loader, not the hosted iframe runtime.

@loyumi/react

1.0.0 → Web Widget v1. Official stable downloadable wrapper, typed and SSR-safe for React 18.2 and 19, with event callbacks, theming, session handoff, and optional immutable loader URL plus SRI; public npm-registry availability is not claimed.

LoyumiMobile for iOS

1.1.0 · stable source distribution. Swift Package for iOS 15+ and macOS 12+ with typed merchant-gateway calls and additive Native UI Kit 1.0.0-beta.1. It has no Loyumi API-key setting and is not claimed on a package registry.

Loyumi Mobile for Android

1.1.0 · stable source distribution. Kotlin library for Android API 23+ with typed merchant-gateway calls, coroutines, and additive Native UI Kit 1.0.0-beta.1 built with framework Views. It has no Loyumi API-key setting and is not claimed on a package registry.

AI-native rewards surface

1.0 · Beta. Versioned, privacy-minimized semantic JSON derived from a published widget deployment, with a checked-in JSON Schema and confirmed, idempotent native-entitlement redemption action.

Portability export

1.6.0 · 53 collections. Preserves migration-critical business state, including durable Reward Gifts evidence, while excluding and naming the authority and runtime records that must be reissued.

Integration starters

One synthetic mapping pack plus three contract-reviewed patterns. Shopify and Klaviyo mapping conformance, commerce earn and return, recipient-bound Reward Gifts, and signed webhooks are published with merchant-owned boundaries. The provider mapping status remains configuration_required; a native or connected provider integration and broad turnkey connector catalog are not claimed.

Typed API 1.13 server integration with guarded retries.

@loyumi/sdk covers all 121 Public API 1.13.0 operations with typed requests and responses, Reward Gifts frameworks, explicit membership, exact ledger-close evidence, cursor-paged back-office queries, 40 lifecycle webhook types plus verification, portability schema 1.6.0, and merchant-operated Shopify and Klaviyo mapping helpers. It retries reads and idempotency-protected mutations safely; member updates, widget session creation, and one-time code exchange deliberately never retry.

Install the stable download
npm install --save-exact https://docs.loyumi.com/downloads/loyumi-sdk-2.3.0.tgz
Verify the download193,541 bytesSHA-256 6ae6ca12753af4168e4cd3a041d9cd4d153d005ae790320ef2a316d3af6611a2

The package manifest names the downloaded artifact @loyumi/sdk, so import from that package name after installation. It is ESM-only, requires Node.js 20 or a compatible Web Crypto edge runtime, and belongs only in trusted server code. Commit the resulting lockfile and do not copy dist files into browser or mobile bundles.

Server example
import { LoyumiClient } from "@loyumi/sdk";

const loyumi = new LoyumiClient({
  apiKey: process.env.LOYUMI_API_KEY!,
  environment: process.env.LOYUMI_ENVIRONMENT_ID!,
});

const member = await loyumi.getMember("<external-customer-id>");
Explicit membership
await loyumi.enrollMemberProgram(
  "<external-customer-id>",
  "<program-id>",
  {
    consentStatus: "opted_in",
    noticeVersion: "<rewards-notice-version>",
    evidence: { source: "<consent-capture-channel>" },
  },
  { idempotencyKey: "<stable-enrollment-key>" },
);

await loyumi.unenrollMemberProgram(
  "<external-customer-id>",
  "<program-id>",
  {
    reason: "<approved-withdrawal-reason>",
    evidence: { source: "<withdrawal-channel>" },
  },
  { idempotencyKey: "<stable-unenrollment-key>" },
);
postPurchase + postReturn

Types receipt-time and pinned event-time earning plus original-line return evidence without falling back to untyped JSON.

Shopify + Klaviyo mapping helpers

Normalize HMAC-gated Shopify 2026-07 webhook payloads, build credential-free Klaviyo 2026-07-15 requests, and run eight synthetic local vectors while status remains configuration_required.

LoyumiRewardGiftExperienceFramework

Orchestrates invitation-first, recipient-bound catalog-entitlement gifts across separate value, Widget, delivery, fulfillment, and recovery authorities.

createMember + enrollMemberProgram + unenrollMemberProgram

Separates identity from explicit consent and zero-value program exit without erasing governed history.

listTransactions + getTransaction

Searches by customer, program, event, source reference, state, or time and opens balanced entry detail.

getLedgerCloseReport

Returns exact decimal-string roll-forward and double-entry controls, policy valuation, readiness lights, and a deterministic evidence hash.

webhook endpoint + verification methods

Creates, rotates, inspects, retries, and verifies 40 privacy-minimized public lifecycle event types under separate scopes.

exportPortabilityBundle

Reads portability schema 1.6.0 across all 53 published business-state collections.

Typed React bindings for Web Widget v1.

@loyumi/react is a stable SSR-safe wrapper for React 18.2 and 19 around the shipped custom element. It types the four widget event families, same-origin sessionEndpoint, supported theme variables, and the imperative clearSession() sign-out boundary. It contains no Loyumi API credential and does not infer customer identity.

Install the stable React download
npm install --save-exact https://docs.loyumi.com/downloads/loyumi-react-1.0.0.tgz
Verify the download8,303 bytesSHA-256 89903851bc27ba38ebade9cf0e88b3a82745376ed46968f0c35d7e84c9860c5c
React customer surface
"use client";

import { useRef } from "react";
import { LoyumiRewards, type LoyumiRewardsHandle } from "@loyumi/react";

export function Rewards({ cspNonce }: { cspNonce: string }) {
  const widget = useRef<LoyumiRewardsHandle>(null);

  async function signOut() {
    widget.current?.clearSession();
    await endMerchantSession();
  }

  return <LoyumiRewards
    ref={widget}
    deployment="wdg_…"
    sessionEndpoint="/api/loyumi/widget-session"
    nonce={cspNonce}
    onAction={({ action }) => handleLoyaltyAction(action)}
    style={{ "--loyumi-accent": "#176348" }}
  />;
}

Framework-native rewards views, with value movement still behind your gateway.

The stable iOS and Android 1.1.0 source packages include additive Beta presentation components. iOS uses SwiftUI. Android uses framework View classes, not a claimed Jetpack Compose package. Both consume the SDK's typed member, catalog, and activity models and return reward-selection intent to the merchant app.

Merchant gatewayAuthenticates the customer and returns customer-safe SDK models
Native UI KitRenders explicit loading, content, empty, and failure states
Merchant callbackReviews intent, asks for confirmation, then calls the governed gateway lifecycle
Balance

Available value plus pending and reserved context.

Tier progress

Current tier, progress, and distance to the next threshold.

Reward card

Name, description, point cost, availability, and a selection callback.

Reward catalog

A typed collection with its own empty and retry states.

Activity

Customer-facing activity rows using SDK models.

Rewards home

A composed balance, tier, catalog, and activity surface with separate retry callbacks.

MERCHANT-CONTROLLED

Theme and states

  • Accent, on-accent, surface, text, secondary text, separator, radius, and spacing tokens
  • Loading, content, empty, and failure as explicit values
  • Platform-standard typography and localized point formatting
  • Independent retry callbacks for member, catalog, and activity
BUILT-IN BASELINE

Accessibility hooks

  • Descriptive balance, tier-progress, reward-action, and activity semantics
  • Platform heading traits where available
  • At least 44-point iOS and 48-dp Android action targets
  • Merchant remains responsible for contrast, localization, VoiceOver/TalkBack, text scaling, and device testing

Add loyalty to iOS without putting a Loyumi secret on the phone.

LoyumiMobile is a dependency-free Swift Package for iOS 15+ and macOS 12+. It gives the app typed member summary, explicit program enrollment and unenrollment, activity, reward catalog, quote, reserve, commit, release, and reverse calls against a merchant-owned backend gateway.

  1. 1

    Download and verify

    Unpack the exact source archive. In Xcode choose File → Add Package Dependencies → Add Local, select the folder containing Package.swift, and add LoyumiMobile to the app target.

  2. 2

    Build the merchant gateway first

    Expose the documented /loyalty/v1/… routes from your authenticated backend. The backend derives member identity from the signed-in customer and calls Loyumi with its least-privilege server credential.

  3. 3

    Give the SDK only a customer session

    The token provider returns your short-lived, audience-bound customer session. It never returns a Loyumi API key.

Configure the Swift client
import LoyumiMobile

let loyalty = LoyumiMobileClient(
  configuration: try LoyumiMobileConfiguration(
    backendURL: URL(string: "https://<merchant-host>/api")!,
    tokenProvider: {
      try await customerSessionStore.loyaltyToken()
    }
  )
)

let summary = try await loyalty.memberSummary()
let rewards = try await loyalty.catalog(limit: 25)
Swift program membership
let enrollment = try await loyalty.enrollInProgram(
  programID: "<program-id>",
  input: .init(
    noticeVersion: "<rewards-notice-version>",
    evidence: ["source": "<consent-capture-channel>"]
  ),
  options: .init(idempotencyKey: "<stable-enrollment-key>")
)

let exit = try await loyalty.unenrollFromProgram(
  programID: "<program-id>",
  input: .init(
    reason: "<approved-withdrawal-reason>",
    evidence: ["source": "<withdrawal-channel>"]
  ),
  options: .init(idempotencyKey: "<stable-unenrollment-key>")
)
SwiftUI rewards home · Native UI Kit Beta
import SwiftUI
import LoyumiMobile

LoyumiRewardsHomeView(
  member: .content(summary.value),
  catalog: .content(rewards.value),
  activity: .content(activity.value),
  unit: summary.value.balance.unit,
  theme: .loyumi,
  onSelectedReward: { reward in
    // Show your review + confirmation screen. Do not redeem here.
    pendingReward = reward
  },
  onRetryMember: reloadMember,
  onRetryCatalog: reloadCatalog,
  onRetryActivity: reloadActivity
)

Use LoyumiBalanceView, LoyumiTierProgressView, LoyumiRewardCard, LoyumiRewardCatalogView, or LoyumiActivityView when the composed home is more than the screen needs. Supply .loading, .content, .empty, or .failure explicitly so the customer never sees invented placeholder value.

Verify the download20,406 bytesSHA-256 0aff636dfdf5265a8dae88db3b2d285aeba329b17079449c39548e993b38b63e

Add loyalty to Android through the same safe gateway.

com.loyumi.mobile is a Kotlin library for Android API 23+ with Kotlin coroutines as its only runtime dependency. It exposes the same typed merchant-gateway member, explicit program enrollment and unenrollment, activity, catalog, quote, reserve, commit, release, and reverse lifecycle as the iOS SDK.

  1. 1

    Download and verify

    Unpack the exact source archive beside the app, include it as :loyumi-mobile, and add the project dependency. The app's root build supplies its Android and Kotlin plugin versions.

  2. 2

    Use the same reviewed gateway contract

    The merchant backend authenticates the customer, derives identity, authorizes value movement, and keeps the Loyumi credential server-side.

  3. 3

    Call from lifecycle-aware coroutines

    Give the SDK only a short-lived merchant customer session and cancel work with the owning lifecycle.

Add the Android source module
// settings.gradle.kts
include(":loyumi-mobile")
project(":loyumi-mobile").projectDir = file("../loyumi-android-1.1.0")

// app/build.gradle.kts
dependencies {
  implementation(project(":loyumi-mobile"))
}
Configure the Kotlin client
val loyalty = LoyumiMobileClient(
  LoyumiMobileConfiguration(
    backendUrl = "https://<merchant-host>/api",
    tokenProvider = {
      customerSessionStore.loyaltyToken()
    },
  ),
)

viewModelScope.launch {
  val summary = loyalty.memberSummary()
  val rewards = loyalty.catalog(limit = 25)
}
Kotlin program membership
val enrollment = loyalty.enrollInProgram(
  programId = "<program-id>",
  input = MemberProgramEnrollmentRequest(
    noticeVersion = "<rewards-notice-version>",
    evidence = mapOf("source" to "<consent-capture-channel>"),
  ),
  options = LoyumiMutationOptions(idempotencyKey = "<stable-enrollment-key>"),
)

val exit = loyalty.unenrollFromProgram(
  programId = "<program-id>",
  input = MemberProgramUnenrollmentRequest(
    reason = "<approved-withdrawal-reason>",
    evidence = mapOf("source" to "<withdrawal-channel>"),
  ),
  options = LoyumiMutationOptions(idempotencyKey = "<stable-unenrollment-key>"),
)
Android rewards home · Native UI Kit Beta
val rewardsHome = LoyumiRewardsHomeView(requireContext()).apply {
  theme = LoyumiRewardsTheme()
  onRewardSelected = { reward ->
    // Show your review + confirmation screen. Do not redeem here.
    viewModel.reviewReward(reward)
  }
  onRetryMember = viewModel::reloadMember
  onRetryCatalog = viewModel::reloadCatalog
  onRetryActivity = viewModel::reloadActivity
}

rewardsHome.submit(
  member = LoyumiSurfaceState.Content(summary.value),
  catalog = LoyumiSurfaceState.Content(rewards.value),
  activity = LoyumiSurfaceState.Content(activity.value),
  unit = summary.value.balance.unit,
)

Use LoyumiBalanceView, LoyumiTierProgressView, LoyumiRewardCard, LoyumiRewardCatalogView, or LoyumiActivityView directly when the composed home is more than the screen needs. Call submit with Loading, Content, Empty, or Error; the UI kit does not fetch or invent customer value.

Verify the download23,455 bytesSHA-256 452f23950622e9f0f2d22c06c84c262e746417e71adefe09c5e869667c9d286b

Give an AI host structured rewards, not screenshots or prompts.

A published Web Widget deployment can also produce a vendor-neutral semantic JSON surface. It contains versioned balance, tier-progress, reward-catalog, and activity components plus explicit action descriptors. The endpoint derives this projection from the same widget runtime, so publication status, exact origin, session permission, customer scope, and rate limits remain authoritative.

Read the semantic surface in a trusted adapter
const endpoint = new URL(
  "https://app.loyumi.com/api/v1/widgets/<deployment-id>/agent/v1",
);
endpoint.searchParams.set("parentOrigin", "https://<approved-merchant-origin>");

const response = await fetch(endpoint, {
  headers: {
    Accept: "application/json",
    // Omit for guest mode. Keep this token in the adapter, not model context.
    Authorization: "Widget <origin-bound-widget-session>",
  },
});

if (!response.ok) throw new Error("Rewards surface unavailable");
const surface = await response.json();

// Validate against the published JSON Schema before rendering or planning.
console.log(surface.surface.version);   // "1.0"
console.log(surface.surface.stability); // "beta"
loyumi.balance

Available, pending, and reserved values with the program unit, or an explicit sign_in_required state.

loyumi.tier_progress

Current tier, qualification points, and the next configured threshold, or an explicit sign-in state.

loyumi.reward_catalog

Bounded published rewards, eligibility, points needed, pagination state, and references to available actions.

loyumi.activity

Bounded customer-facing event type, point movement, and timestamp without internal transaction IDs.

loyumi.reward.redeem

A POST action is returned only for an eligible reward when the signed session may redeem. It binds the exact reward ID and declares confirmation and idempotency requirements.

Execute only after explicit customer confirmation
const action = surface.actions.find(
  (item) => item.kind === "loyumi.reward.redeem",
);

await requireCustomerConfirmation({
  rewardId: action.input.properties.rewardId.const,
  label: action.label,
});

const result = await fetch(new URL(action.href, "https://app.loyumi.com"), {
  method: action.method,
  headers: {
    Authorization: "Widget <same-origin-bound-widget-session>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    action: "redeem",
    rewardId: action.input.properties.rewardId.const,
    confirmed: true,
    idempotencyKey: "agent-confirmation:<durable-confirmation-id>",
  }),
});
SEMANTIC SAFETY

Data stays data

  • Every merchant-authored label is marked merchantContentIsData: true
  • The safety envelope marks merchant content as untrusted_data
  • No API secret or signed token is projected, and direct identifiers such as member ID, email, or phone are omitted
  • Personalized balances, tier, eligibility, and activity are still customer-sensitive and must remain access-controlled
VALUE SAFETY

Actions stay explicit

  • Guest mode has no redeem action
  • Confirmation must be exactly true
  • Reward ID and idempotency key must be strings; the key is stable and 12–160 characters
  • Unknown action-body fields are rejected instead of ignored
  • The action re-verifies origin, session, permission, deployment, inventory, balance, enrollment, program readiness, and rate limit

Publish once, then add two lines.

Replace wdg_… with a deployment published by Widget Studio. With no customer session, the element deliberately starts in public guest mode.

Guest HTML
<script async src="https://docs.loyumi.com/widgets/v1/loyumi.js"></script>
<loyumi-rewards deployment="wdg_…">
  <a href="/account/rewards">View rewards</a>
</loyumi-rewards>

Public content is easy. Customer value is verified.

NO CUSTOMER SESSION

Guest catalog

  • Published reward catalog and program explanation
  • No customer balance, tier, or activity
  • No redemption or value-moving action
  • Works with the copy-paste embed alone
SHORT-LIVED SESSION

Signed personalized mode

  • Member balance, tier progress, rewards, and activity
  • Customer identity mapped by the merchant backend
  • The page handles only a single-use code valid for at most 60 seconds
  • The signed token stays inside the isolated frame and expires within 10 minutes

Your backend vouches for the customer.

BrowserCalls your same-origin session endpoint
Merchant backendReturns a one-time code after authenticating the customer
Hosted frameExchanges that code for a signed token that never leaves the frame

Your merchant backend sends a trusted server request to POST https://app.loyumi.com/api/v1/widget-sessions. Loyumi returns an opaque sessionCode that is single-use and expires in at most 60 seconds. Your same-origin endpoint passes only that code to the loader. The loader hands it to the isolated frame on widgets.loyumi.com, where it is atomically exchanged for an origin-bound signed token. The reusable token stays inside the hosted iframe, lives for no more than 10 minutes, and never enters the merchant page, DOM, URL, storage, analytics, or logs.

Merchant backend endpoint
// Runs on your server at POST /api/loyumi/widget-session.
export async function POST(request) {
  const customer = await requireSignedInCustomer(request);
  const { deploymentId, origin } = await request.json();

  const response = await fetch(
    "https://app.loyumi.com/api/v1/widget-sessions",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer <server-api-key>",
        "X-Environment": "<environment-id>",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        deploymentId,
        externalCustomerId: customer.id,
        origin,
        permissions: ["profile:read"],
      }),
    },
  );

  return new Response(response.body, {
    status: response.status,
    headers: {
      "Content-Type": "application/json",
      "Cache-Control": "no-store",
    },
  });
}
Personalized HTML
<script async src="https://docs.loyumi.com/widgets/v1/loyumi.js"></script>
<loyumi-rewards
  deployment="wdg_…"
  session-endpoint="/api/loyumi/widget-session"
>
  <a href="/account/rewards">View rewards</a>
</loyumi-rewards>
Merchant logout
document.querySelector("loyumi-rewards")?.clearSession();
  • Authenticate the current customer before mapping externalCustomerId.
  • Accept deploymentId and origin from the loader, but derive the customer ID from your authenticated session.
  • Configure an exact origin allowlist for every deployment. Match scheme, host, and non-default port; do not use wildcards or suffix matching.
  • Request only profile:read unless this surface truly needs native entitlement redemption; that action also requires rewards:redeem and a credential with widgets:redemptions.
  • Never put a Loyumi API secret or signed session token in a custom-element property, attribute, URL, mobile code, analytics, logs, or screenshots.
  • Treat sessionCode as one-use delivery material: do not persist or log it, and return Cache-Control: no-store.
  • Call clearSession() before ending the merchant login session when the customer signs out or changes account.
Content Security Policy
Content-Security-Policy:
  script-src 'self' https://docs.loyumi.com;
  style-src 'self' 'nonce-<per-request-nonce>';
  frame-src https://widgets.loyumi.com;
  connect-src 'self';

Merge these exact sources into an existing CSP rather than replacing the rest of the merchant's policy. For a strict style-src, put the same per-request nonce on the loader <script>; the loader carries it into its Shadow DOM stylesheet. The hosted frame applies its own restrictive CSP and exact frame-ancestors origin policy.

One deployment, the loyalty surfaces customers expect.

Use <loyumi-rewards> for the Studio-composed experience, or one of these exact custom-element tags for a focused surface.

<loyumi-balance>

Shows the signed-in member’s available balance. Guest mode renders no personal value.

<loyumi-tier-progress>

Shows current status and progress toward the next configured tier.

<loyumi-reward-catalog>

Shows the published catalog in guest mode and member eligibility when signed. Direct hosted redemption is deliberately limited to Loyumi-native entitlements; coupons and external fulfillment use a merchant connector.

<loyumi-activity>

Shows aggregated member-facing activity only in signed personalized mode, without exposing internal transaction identifiers.

Widget Studio decides which families appear and in what order. The runtime provides accessible loading, empty, error, offline, and success states for each family.

Match the brand without giving away the boundary.

The custom element uses Shadow DOM and the hosted loyalty UI runs in a sandboxed iframe on a dedicated origin, so merchant-page CSS and console cookies cannot cross into its structure. Customize supported design tokens on the host element; private selectors, frame markup, and post-message details are not APIs.

Theme tokens
loyumi-rewards {
  --loyumi-accent: #176348;
  --loyumi-background: #f4f6f4;
  --loyumi-surface: #ffffff;
  --loyumi-text: #18211d;
  --loyumi-radius: 16px;
  --loyumi-min-height: 220px;
}

The loader emits composed browser events across the Shadow DOM boundary. Event details never include an API secret, session code, or signed session token. For sign_in and view_all_rewards, the loader clicks the fallback link inside <loyumi-rewards> by default. A merchant handler can call event.preventDefault() only when it takes over navigation.

Lifecycle events
const widget = document.querySelector("loyumi-rewards");

widget.addEventListener("loyumi:ready", () => {
  // No detail payload. The isolated runtime is ready.
});

widget.addEventListener("loyumi:loaded", (event) => {
  const { personalized, version } = event.detail;
});

widget.addEventListener("loyumi:action", (event) => {
  const { action, rewardId } = event.detail;

  // Optional: handle sign-in or "view all" yourself.
  // Call preventDefault() only when your handler completes navigation.
  if (action === "sign_in") {
    event.preventDefault();
    window.location.assign("/account/login?next=/account/rewards");
  }
});

widget.addEventListener("loyumi:error", (event) => {
  const { code } = event.detail;
});
loyumi:readyNo detail payload. The isolated runtime is ready to receive appearance and the one-time session code.
loyumi:loadedDetail contains personalized and version.
loyumi:actionDetail contains action (sign_in, view_all_rewards, reward_selected, or reward_redeemed) and an optional rewardId. Fulfillment and value movement remain server-authorized.
loyumi:errorDetail contains a typed code for load, session, origin, network, or render failures.

Choose compatible fixes or an exact loader artifact.

/widgets/v1/

Major-pinned channel. Backward-compatible accessibility, security, and performance fixes can ship within v1 without changing the integration contract.

/widgets/v1.0.0/

Immutable merchant-page loader for artifact review and SRI. It still loads the hosted Web Widget v1 iframe/runtime; it does not freeze that full runtime.

Breaking changes

A breaking element, attribute, event, event-detail, or documented theme-token change requires a new major loader path.

Deprecation

Before a supported major is retired, Loyumi publishes a notice, migration guide, and end-of-support date. Do not copy an unversioned loader URL.

Immutable loader with SRI
<script
  async
  src="https://docs.loyumi.com/widgets/v1.0.0/loyumi.js"
  integrity="sha384-Y7E2UevWddDH2ijQE+/3oDT1dlG6MHRZjX2BD6fmRz45hiGUXtMoJKGA78jtDa5k"
  crossorigin="anonymous"
  nonce="<per-request-csp-nonce>"
></script>

Modern by default. Useful when blocked.

Desktop target

Current and previous stable Chrome, Edge, Firefox, and Safari releases.

Mobile target

Currently supported iOS Safari and maintained Android System WebView releases.

Required platform features

Custom Elements, Shadow DOM, postMessage, Fetch, Web Crypto, and CSP nonces. ResizeObserver and random UUIDs have built-in fallbacks.

The fallback link inside the custom element remains ordinary HTML. If JavaScript, the frame, CSP, DNS, or an extension blocks the runtime, customers still have a merchant-controlled rewards destination and the loader emits a typed loyumi:error instead of waiting forever.