> ## 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.

# Lifecycle and readiness

> Understand how the Cashback Hive SDK initialises, the three ways to detect when it is ready, and how to avoid race conditions in your integration.

## Initialisation sequence

When `sdk.js` executes it:

1. Creates the SDK instance and assigns it to `window.CashbackHive`. If an instance already exists (the script was loaded twice), the second load does nothing.
2. Reads `data-*` attributes from its own `<script>` tag. If `data-key` is present it calls `init()` immediately — this is **auto-initialisation**. If `data-key` is absent, nothing happens until you call [`CashbackHive.init()`](https://developer.cashbackhive.com/reference/configuration#manual-initialisation) yourself.
3. During `init()` it sends an `sdk_loaded` analytics beacon, and if a member token is already in `localStorage` from a previous visit it fetches the member in the background.
4. Resolves the `ready` promise, then dispatches `cbh:ready` on `window` and calls `window.onCashbackHiveReady` if you defined one.

> [!NOTE]
>
> `init()` only runs once. Calling it a second time is ignored (and logged in debug mode).

## Knowing when the SDK is ready

You **must not** call SDK methods before initialisation. Methods throw `[CashbackHive] call init({ key }) first, or set data-key on the script tag` if you do. Pick whichever readiness signal fits your code.

### `cbh:ready` event (recommended)

A `CustomEvent` on `window`. The SDK is on `event.detail.sdk`.

```js theme={null}
window.addEventListener("cbh:ready", (event) => {
  const sdk = event.detail.sdk;
  // safe to use sdk here
});
```

Events only reach listeners that are registered **before** the event fires. If your listener might attach late, combine it with the `initialized` check below.

### `initialized` property

A synchronous boolean on the instance. Use it to handle the "already ready" case:

```js theme={null}
function whenReady(callback) {
  const sdk = window.CashbackHive;
  if (sdk?.initialized) {
    callback(sdk);
    return () => {};
  }
  const handler = (event) => callback(event.detail.sdk);
  window.addEventListener("cbh:ready", handler, { once: true });
  return () => window.removeEventListener("cbh:ready", handler);
}
```

This pattern is what you want in components that mount after page load, such as a route that's navigated to client-side.

### `ready` promise

`CashbackHive.ready` is a promise that resolves after `init()` completes. It's convenient in `async` code, but it only exists once `sdk.js` has executed, so guard against the script not having loaded yet:

```js theme={null}
await window.CashbackHive?.ready;
```

### `onCashbackHiveReady` callback

Define a global function before the script loads and the SDK will call it with the instance. Useful in environments where you can't add event listeners easily (for example some tag managers).

```html theme={null}
<script>
  window.onCashbackHiveReady = function (sdk) {
    sdk.offers.list().then(console.log);
  };
</script>
<script src="https://sdk.cashbackhive.com/sdk.js" data-key="cbh_pk_…" async></script>
```

## Script loading and `async`

Always load `sdk.js` with `async` (or your framework's equivalent, such as Next.js's `strategy="afterInteractive"`). The SDK doesn't block rendering and has no dependency on DOM content being parsed.

Because the script is `async`, its execution order relative to your own scripts is not guaranteed — which is exactly why you should use `cbh:ready` rather than assuming `window.CashbackHive` exists.

## Single-page applications

Load the SDK **once**, at the application root (root layout, `index.html`, `_app`), not inside individual routes or components. Client-side navigation doesn't reload the page, so the SDK stays initialised and `cbh:ready` doesn't fire again. Components that mount later should use the `initialized` check shown above.

## Caching

`sdk.js` is served with `Cache-Control: public, max-age=300, stale-while-revalidate=86400`. Browsers cache it for five minutes and may serve a stale copy for up to a day while revalidating in the background, so SDK updates roll out to visitors within minutes without any change on your side.


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