> For the complete documentation index, see [llms.txt](https://docs.elitebundleapp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.elitebundleapp.com/developer-sdk/getting-started.md).

# Getting started with the SDK

Enable SDK access in Elite Bundle Builder, wait for the elite:byob:ready event, and read your first bundle state from window\.eliteBundle.sdk.byob.

## 1. Enable SDK access

SDK access is off by default.

{% stepper %}
{% step %}
Open the Elite Bundle Builder app in your Shopify admin.
{% endstep %}

{% step %}
Go to **Settings → Developer SDK**.
{% endstep %}

{% step %}
Click **Enable SDK access**, enter your developer email, and confirm.
{% endstep %}
{% endstepper %}

The SDK is immediately active. The team may also reach out with setup guidance.

## 2. Verify the global is present

Open any bundle product page in your browser, open DevTools, and run:

```js
window.eliteBundle
```

You should see an object like:

```js
{
  version: "1.0.0",
  app: {
    shopDomain: "your-store.myshopify.com",
    moneyFormat: "${{amount}}",
    currencyCode: "USD",
    currencyRate: 1,
    countryCode: "US",
    locale: "en",
    storefrontAccessToken: "...",
    sdkEnabled: true
    // plus internal setup fields
  },
  byob: { bundleConfig: {...}, shopifyVariantId: 123, productId: "789" },
  sdk: {
    byob: { steps: [...], cart: [...], addToCart: fn, selectOne: fn, checkout: fn, ... }
  },
  on: fn,
  off: fn
}
```

`byob` holds the bundle on this page whichever type it is — build-your-own or multi-option. Both use the same `sdk.byob`.

{% hint style="info" %}
`sdk` is only present when `sdkEnabled` is `true`. If it is missing, either SDK access has not been enabled in the app settings, or the deferred bundle script has not run yet. See step 4.
{% endhint %}

## 3. Wait for the ready event

`sdk.byob` is populated once the bundle has fetched its products and rendered. Wait for the ready event before reading it:

```js
document.addEventListener('elite:byob:ready', (event) => {
  const { steps, formatPrice } = window.eliteBundle.sdk.byob;
  console.log(`Bundle loaded with ${steps.length} steps`);
  steps.forEach(step => {
    console.log(step.products.map(p => p.title));
  });
});
```

A multi-option bundle fires the same event; its options arrive as `steps`.

## 4. Check if the SDK is available

{% hint style="warning" %}
`window.eliteBundle` is written by the theme app embed in `<head>`, but `sdk` is added later by the deferred bundle script. **Do not gate on `sdk` existing.** Theme code that runs early would bail out permanently even on stores where the SDK is enabled. Listen on `document` and check the SDK inside the handler.
{% endhint %}

```js
function onBundleReady(callback) {
  const ready = () => {
    const sdk = window.eliteBundle?.sdk?.byob;
    if (sdk) callback(sdk);   // absent means SDK access is off
  };

  // Handle the event having already fired before your script ran
  const sdk = window.eliteBundle?.sdk?.byob;
  if (sdk && !sdk.isLoading && !sdk.error) ready();
  else document.addEventListener('elite:byob:ready', ready, { once: true });
}

onBundleReady((sdk) => {
  console.log(sdk.steps.length);
});
```

## 5. Read shop and bundle basics

`app` is written by the theme app embed, so it is available synchronously:

```js
const { moneyFormat, currencyCode, locale, storefrontAccessToken } = window.eliteBundle.app;
```

So is `byob` — but only on a page that actually has a bundle, so guard it:

```js
const bundle = window.eliteBundle.byob;
if (bundle) console.log(bundle.productId);   // Shopify product ID
```

Its `bundleConfig` is an internal encoded form and is not a public API. For anything you want to read or render, use `sdk.byob` — see the [Bundle SDK](/reference/sdk.md).

## 6. Choose where your code runs

Your code can live in your theme, or in the app itself.

|                      | Theme code                                 | Custom JavaScript                                                |
| -------------------- | ------------------------------------------ | ---------------------------------------------------------------- |
| Where                | `theme.liquid`, a section or a theme asset | Bundle editor → **Settings → Custom JavaScript**                 |
| Runs on              | Wherever your theme loads it               | Every page where a bundle renders, once, before the bundle loads |
| After a theme change | Must be copied to the new theme            | Keeps working                                                    |

Custom JavaScript applies to every bundle in the store. To act on one bundle only, compare `window.eliteBundle.byob.productId`: **Settings → Custom JavaScript** shows the check for the open bundle, ready to copy, under **Only this bundle**. In CSS, the bundle's outer element carries the same ID as `data-eb-bundle`, so `[data-eb-bundle="…"] .eb_card { … }` styles one bundle alone. It runs before the bundle loads, so wait for `elite:byob:ready` as in step 3: the event hasn't fired yet when your code starts.

{% hint style="info" %}
A runtime error in your code is caught and logged as `[Elite Bundle] custom JavaScript`, and the bundle keeps working. Errors name your code `elite-bundle-custom.js` in DevTools. Custom JavaScript does not run in the editor's preview: check it on your store.
{% endhint %}

This example adds links between two bundles, such as a 50 ml and a 100 ml version, under the banner. It uses only the ready events and `byob.productId`, so it works with SDK access off:

```js
document.addEventListener('elite:byob:ready', () => {
  const tabs = [
    { id: '1111111111111', label: '50 ML', path: 'products/bundle-50-ml' },
    { id: '2222222222222', label: '100 ML', path: 'products/bundle-100-ml' },
  ];
  const current = String(window.eliteBundle.byob?.productId);
  if (!tabs.some((tab) => tab.id === current)) return;

  const nav = document.createElement('nav');
  nav.className = 'size-tabs';
  for (const tab of tabs) {
    const link = document.createElement('a');
    link.href = (window.Shopify?.routes?.root ?? '/') + tab.path;
    link.textContent = tab.label;
    if (tab.id === current) link.setAttribute('aria-current', 'page');
    nav.append(link);
  }

  // Put the links back if the bundle re-renders without them.
  const place = () => {
    const layout = document.querySelector('.eb_root .eb_layout');
    if (layout && nav.nextElementSibling !== layout) layout.before(nav);
  };
  place();
  document.addEventListener('elite:byob:cart-change', place);
});
```

Style the links in **Settings → Custom CSS**, for example `.size-tabs a[aria-current="page"] { font-weight: 700; }`.
