Headless / JS API

Everything the components do is available programmatically. Import Auth and getUnidyClient from the SDK module to read auth state, call the profile/newsletter/ticket/subscription APIs directly, and combine the results with any library — no u-* markup required.

View this guide as Markdown

Auth state from JavaScript

authState is the same reactive store the components use: read authState.authenticated for the current value and subscribe with onAuthChange for updates. Session restore runs asynchronously on page load, so reacting to the store — instead of a one-shot check — is the reliable pattern. Auth.Errors exposes stable error codes for handling specific sign-in failures.

  • authState store — current auth state, shared with the components
  • onAuthChange() — subscribe to sign-in/sign-out transitions
  • Auth.Errors — stable error-code constants
<div class="rounded-lg border border-border bg-background-light p-4">
  <p class="text-sm">
    Authentication status:
    <strong id="auth-status" class="font-mono">checking…</strong>
  </p>
</div>

<script type="module">
  // The same module the components use — import it for headless access
  import {
    authState,
    onAuthChange,
    Auth,
  } from "https://cdn.jsdelivr.net/npm/@unidy.io/sdk@1.8.1/dist/sdk/index.esm.js";

  const render = (authenticated) => {
    document.getElementById("auth-status").textContent = authenticated
      ? "authenticated"
      : "not authenticated";
  };

  // authState is a reactive store: read it now, subscribe for changes.
  // (Session restore is async on page load — never rely on a one-shot check.)
  render(authState.authenticated);
  onAuthChange("authenticated", render);

  // Auth.Errors holds stable error codes for the sign-in process,
  // e.g. Auth.Errors.email.NOT_FOUND === "account_not_found"
  console.log("Known email errors:", Auth.Errors.email);
</script>
Live demo

Authentication status: checking…

API calls with getUnidyClient

getUnidyClient() exposes typed services — profile, newsletters, tickets, subscriptions, auth. Calls return a [error, data] tuple, so error handling is a destructure instead of try/catch.

  • client.profile.get() / .update() — read and write profile data
  • [error, data] tuples — predictable error handling
  • All feature areas — newsletters, tickets, subscriptions, auth
<div class="flex flex-col gap-3">
  <button id="load-profile" class="btn btn-primary !min-h-0 !px-4 !py-2 w-fit text-sm">
    Load my profile via the API
  </button>
  <pre
    id="profile-output"
    class="overflow-x-auto rounded-lg bg-background-light p-4 font-mono text-xs text-text-light">(click the button — requires being signed in)</pre>
</div>

<script type="module">
  import { getUnidyClient } from "https://cdn.jsdelivr.net/npm/@unidy.io/sdk@1.8.1/dist/sdk/index.esm.js";

  document.getElementById("load-profile").addEventListener("click", async () => {
    const output = document.getElementById("profile-output");

    // getUnidyClient() reads its config from <u-config> on the page.
    // Every service call returns a [error, data] tuple — no try/catch needed.
    const client = getUnidyClient();
    const [error, profile] = await client.profile.get();

    output.textContent = error
      ? `Error: ${JSON.stringify(error, null, 2)}`
      : JSON.stringify(profile, null, 2);

    // Also available: client.newsletters, client.tickets, client.subscriptions, client.auth
  });
</script>
Live demo
(click the button — requires being signed in)

Mix with any library: profile QR code

SDK data is just data — here the signed-in user's name is fetched via the client and rendered as a QR code with a third-party library. The same pattern powers loyalty cards, wallet passes, or personalized widgets.

  • SDK + npm ecosystem — combine with any client-side library
  • u-signed-in gating — components and JS API work together
<u-signed-in>
  <!-- Combine SDK data with any third-party library — here: a QR code
       encoding the user's name, rendered fully client-side -->
  <canvas id="profile-qr-canvas" width="200" height="200"></canvas>
</u-signed-in>

<u-signed-in not>
  <p class="text-text-light">
    Sign in on the <a href="/auth" class="text-primary underline">Auth page</a> to see your profile QR
    code.
  </p>
</u-signed-in>

<script type="module">
  import QRCode from "https://esm.sh/qrcode@1.5.4";
  import {
    authState,
    onAuthChange,
    getUnidyClient,
  } from "https://cdn.jsdelivr.net/npm/@unidy.io/sdk@1.8.1/dist/sdk/index.esm.js";

  async function renderQr() {
    const [error, profile] = await getUnidyClient().profile.get();
    if (error) return;
    const name = [profile.first_name?.value, profile.last_name?.value].filter(Boolean).join(" ");
    QRCode.toCanvas(
      document.getElementById("profile-qr-canvas"),
      `Hello, ${name || "Unidy user"}!`
    );
  }

  // Session restore is async on page load, so react to auth state instead
  // of checking once
  if (authState.authenticated) renderQr();
  onAuthChange("authenticated", (authenticated) => authenticated && renderQr());
</script>
Live demo

Sign in on the Auth page to see your profile QR code.