> ## Documentation Index
> Fetch the complete documentation index at: https://developer.cashbackhive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Activate widget

> Build an Activate widget with the Cashback Hive SDK so visitors can sign in as members, earn cashback on purchases, and see their pending and paid balances.

The Activate widget is the member-facing entry point to cashback. A visitor who isn't signed in sees a call to action; once they sign in, the widget shows who they are and what they've earned.

Until a visitor signs in, clicks still go through tracked links and are attributed to **you** as the publisher — but the member earns nothing. Signing in is what "activates" cashback for *them*.

## What it does

* Shows a sign-in call to action when no member is signed in.
* Calls [`auth.signIn()`](https://developer.cashbackhive.com/reference/auth#auth-signin), which opens the Cashback Hive sign-in window and resolves with the member.
* Shows the member's name and pending / confirmed / paid balances.
* Subscribes to [`auth:change`](https://developer.cashbackhive.com/reference/events#auth-change) so it stays correct if sign-in happens elsewhere (another widget, a token restored on load, or sign-in completing in a popup).
* Offers sign-out via [`auth.signOut()`](https://developer.cashbackhive.com/reference/auth#auth-signout).

## Vanilla JavaScript

```html theme={null}
<section id="cbh-activate"></section>

<script>
  (function () {
    function money(amount, currency) {
      return new Intl.NumberFormat(undefined, { style: "currency", currency }).format(amount);
    }

    function mount(sdk) {
      var root = document.getElementById("cbh-activate");

      function renderSignedOut(error) {
        root.textContent = "";
        var p = document.createElement("p");
        p.textContent = "Sign in to activate cashback and track your earnings.";
        var btn = document.createElement("button");
        btn.type = "button";
        btn.textContent = "Activate cashback";
        btn.onclick = async function () {
          btn.disabled = true;
          btn.textContent = "Opening sign-in…";
          try {
            await sdk.auth.signIn(); // auth:change will re-render
          } catch (err) {
            renderSignedOut(err.message);
          }
        };
        root.append(p, btn);
        if (error) {
          var e = document.createElement("p");
          e.setAttribute("role", "alert");
          e.textContent = error;
          root.append(e);
        }
      }

      function renderSignedIn(member) {
        root.textContent = "";
        var b = member.balances;
        var p = document.createElement("p");
        p.textContent = "Signed in as " + (member.displayName || member.email) + ". Cashback is active.";

        var dl = document.createElement("dl");
        [["Pending", b.pending], ["Confirmed", b.confirmed], ["Paid", b.paid]].forEach(function (row) {
          var dt = document.createElement("dt");
          dt.textContent = row[0];
          var dd = document.createElement("dd");
          dd.textContent = money(row[1], b.currency);
          dl.append(dt, dd);
        });

        var out = document.createElement("button");
        out.type = "button";
        out.textContent = "Sign out";
        out.onclick = function () { sdk.auth.signOut(); }; // auth:change will re-render

        root.append(p, dl, out);
      }

      function render(member) {
        if (member) renderSignedIn(member);
        else renderSignedOut();
      }

      sdk.on("auth:change", function (e) { render(e.member); });
      render(sdk.member.current());
    }

    var sdk = window.CashbackHive;
    if (sdk && sdk.initialized) mount(sdk);
    else window.addEventListener("cbh:ready", function (e) { mount(e.detail.sdk); });
  })();
</script>
```

## React / Next.js

Requires the [`useCashbackHive()`](https://developer.cashbackhive.com/installation/nextjs) hook and the [type declarations](https://developer.cashbackhive.com/reference/types.md#typescript-declarations).

```tsx components/activate-widget.tsx theme={null}
"use client";

import { useCallback, useEffect, useState } from "react";
import { useCashbackHive } from "@/hooks/use-cashback-hive";
import type { CashbackHiveSdk, CbhMember } from "@/types/cashback-hive";

function money(amount: number, currency: string) {
  return new Intl.NumberFormat(undefined, { style: "currency", currency }).format(amount);
}

function Activate({ sdk }: { sdk: CashbackHiveSdk }) {
  const [member, setMember] = useState<CbhMember | null>(() => sdk.member.current());
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Keep in sync with sign-in/out triggered anywhere on the page.
  useEffect(() => sdk.on("auth:change", ({ member }) => setMember(member)), [sdk]);

  const signIn = useCallback(async () => {
    setBusy(true);
    setError(null);
    try {
      setMember(await sdk.auth.signIn());
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy(false);
    }
  }, [sdk]);

  if (!member) {
    return (
      <section aria-label="Activate cashback">
        <p>Sign in to activate cashback and track your earnings.</p>
        <button type="button" disabled={busy} onClick={signIn}>
          {busy ? "Opening sign-in…" : "Activate cashback"}
        </button>
        {error && <p role="alert">{error}</p>}
      </section>
    );
  }

  const { balances } = member;
  return (
    <section aria-label="Your cashback">
      <p>
        Signed in as <strong>{member.displayName ?? member.email}</strong>. Cashback is active.
      </p>
      <dl>
        <dt>Pending</dt>
        <dd>{money(balances.pending, balances.currency)}</dd>
        <dt>Confirmed</dt>
        <dd>{money(balances.confirmed, balances.currency)}</dd>
        <dt>Paid</dt>
        <dd>{money(balances.paid, balances.currency)}</dd>
      </dl>
      <button type="button" onClick={() => sdk.auth.signOut()}>
        Sign out
      </button>
    </section>
  );
}

export default function ActivateWidget() {
  const sdk = useCashbackHive();
  if (!sdk) return <p>Loading…</p>;
  return <Activate sdk={sdk} />;
}
```

## Showing recent activity

Signed-in members can see their recent cashback transactions with [`member.activity()`](https://developer.cashbackhive.com/reference/member#member-activity):

```ts theme={null}
const { activity, total } = await sdk.member.activity({ limit: 5 });
// activity[0] → { brandName: "Nike", status: "confirmed", cashback: 4.48, currency: "USD", … }
```

Statuses are `pending`, `confirmed`, `paid` and `declined`. `member.activity()` throws if no member is signed in, so call it only when `auth.isSignedIn()` is true.

## Design notes

**The sign-in window is a popup.** `auth.signIn()` opens a 480×640 popup. Call it from a **direct user gesture** (a click handler) or browsers will block it. If the popup is blocked anyway, the SDK falls back to a full-page redirect and brings the member back to the current URL afterwards — your widget will then see the member via `auth:change` on the next page load.

**Handle rejection.** `auth.signIn()` rejects with `Sign-in window was closed` if the member closes the popup, or with an error message from the sign-in flow. Reset your button state in a `catch`/`finally`.

**Don't assume "signed in" means "member loaded".** `auth.isSignedIn()` only checks for a stored token. `member.current()` is `null` until the SDK has fetched the member (which it does in the background on load if a token exists). Subscribe to `auth:change` rather than polling.

**Sessions persist across visits.** The token lives in `localStorage`, so a member who signed in last week is still signed in today. If the stored token turns out to be invalid, the SDK signs them out automatically on load.

See [Members and authentication](https://developer.cashbackhive.com/guides/members-and-auth.md) for the full flow.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.