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

# JavaScript SDK

> Launch Clink Checkout from browser applications with redirect or embedded flows.

The Clink JavaScript SDK helps you launch Clink Checkout in browser-based applications. It supports both full-page redirects and embedded checkout, and initializes with a publishable key.

<Warning>
  **Security Note:** Use a **publishable key** in browser code. Never expose your **Secret API keys** on the client side.
</Warning>

<Card title="NPM Package" icon="npm" href="https://www.npmjs.com/package/@clink-ai/clink-js">
  View the package on npmjs.com
</Card>

<CardGroup cols={2}>
  <Card title="Create Checkout Session" icon="square-plus" href="/api-reference/endpoint/create-checkout-session">
    Create a checkout session on your backend before launching checkout.
  </Card>

  <Card title="Checkout Session Guide" icon="banknote" href="/guides/payments/checkout_session">
    Learn how hosted checkout sessions work end to end.
  </Card>
</CardGroup>

## Installation

Install the SDK from npm with your preferred package manager:

<CodeGroup>
  ```bash npm theme={null}
  npm install @clink-ai/clink-js
  ```

  ```bash yarn theme={null}
  yarn add @clink-ai/clink-js
  ```

  ```bash pnpm theme={null}
  pnpm add @clink-ai/clink-js
  ```
</CodeGroup>

If you need a browser global build, the package also ships a UMD bundle. After self-hosting `dist/index.umd.js`, the SDK is exposed as `Clink.loadClink(...)` on `window`.

## Initialization

Initialize the SDK with your publishable key:

```ts theme={null}
import { loadClink } from '@clink-ai/clink-js';

const clink = await loadClink('pk_uat_xxxxxxxxx', {
  checkoutEnvironment: 'sandbox',
  locale: 'en-US',
});
```

Supported publishable key formats are `pk_test_*`, `pk_uat_*`, and `pk_prod_*`.

If you already know the final checkout host, you can skip bootstrap by passing `checkoutBaseUrl` directly:

```ts theme={null}
import { loadClink } from '@clink-ai/clink-js';

const clink = await loadClink('pk_prod_xxxxxxxxx', {
  checkoutBaseUrl: 'https://checkout.clinkbill.com',
});
```

### Init Options

* `checkoutEnvironment`: `sandbox` or `production`. Used when `checkoutBaseUrl` is not provided.
* `checkoutBaseUrl`: Checkout host used directly by the SDK. When set, bootstrap is skipped.
* `locale`: Forwarded during bootstrap.
* `origin`: Override the current site origin.
* `fetchImpl`: Custom `fetch` implementation for non-browser runtimes.

When `checkoutBaseUrl` is omitted, the SDK resolves bootstrap in this order:

1. `checkoutEnvironment`
2. `CLINK_ENV`

`CLINK_ENV=sandbox` maps to `https://uat-api.clinkbill.com/api/sdk/bootstrap`, and `CLINK_ENV=production` maps to `https://api.clinkbill.com/api/sdk/bootstrap`.

## Redirect Checkout

Use redirect checkout when you want Clink to take over the full page flow.

```ts theme={null}
import { loadClink } from '@clink-ai/clink-js';

const clink = await loadClink('pk_uat_xxxxxxxxx', {
  checkoutEnvironment: 'sandbox',
});

document
  .getElementById('checkout-button')
  ?.addEventListener('click', async () => {
    await clink.redirectToCheckout({
      // Preferred when your backend returns an opaque session token
      sessionParam: 'sess_xxx#token_xxx',
      replace: false,
    });
  });
```

`redirectToCheckout` accepts:

* `sessionParam`: Preferred when available.
* `sessionId`: Used when you only have the session ID.
* `replace`: Uses `window.location.replace(...)` instead of `assign(...)`.

When both `sessionParam` and `sessionId` are provided, `sessionParam` wins.

## Embedded Checkout

Use embedded checkout when you want the payment flow to stay inside your page.

```ts theme={null}
import { loadClink } from '@clink-ai/clink-js';

const clink = await loadClink('pk_uat_xxxxxxxxx', {
  checkoutEnvironment: 'sandbox',
});

const embedded = await clink.initEmbeddedCheckout({
  async fetchSession() {
    const response = await fetch('/api/clink/checkout-session', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        uiMode: 'elements',
        returnUrl: `${window.location.origin}/complete.html?session_id={ELEMENTS_SESSION_ID}`,
      }),
    });

    return await response.json();
    // {
    //   sessionId: 'sess_xxx',
    //   checkoutUrl: 'https://checkout.clinkbill.com/pay/sess_xxx%23token_xxx',
    //   orderId: 'ord_xxx'
    // }
  },
  onEvent(event) {
    console.log(event.type, event.payload);
  },
  async pollStatus({ sessionId, orderId, attempt }) {
    const response = await fetch(
      `/api/clink/checkout-status?sessionId=${sessionId}`,
    );
    const result = await response.json();

    if (result.state === 'pending' || result.state === 'payment') {
      return null;
    }

    return {
      state: result.state,
      payload: {
        orderId,
        attempt,
      },
    };
  },
});

embedded.mount('#clink-checkout');
```

`fetchSession` must create a checkout session on your backend and return the final checkout URL. For embedded checkout, create the session with `uiMode: 'elements'`. When `uiMode` is `elements`, `returnUrl` is required, and Clink replaces `{ELEMENTS_SESSION_ID}` with the created session ID before returning control to your site. The SDK mounts the returned URL as-is and does not rewrite its query parameters.

### Embedded Options

* `fetchSession`: Required. Must resolve `{ sessionId, checkoutUrl, orderId? }`.
* `onEvent`: Receives all checkout lifecycle events.
* `autoResize`: Automatically applies iframe height updates. Default: `true`.
* `autoDestroyOnComplete`: Automatically destroys the embedded instance after a successful payment. Default: `true`.
* `pollStatus`: Optional polling hook for terminal state detection.
* `pollIntervalMs`: Poll interval in milliseconds. Default: `2000`.

### Embedded Instance API

* `mount(container)`: Mount into a CSS selector or `HTMLElement`.
* `unmount()`: Remove the iframe but keep the instance reusable.
* `destroy()`: Fully dispose the instance.
* `on(type, handler)`: Subscribe to a specific event type.
* `getState()`: Returns `{ mounted, destroyed }`.

## Events and States

Embedded checkout can emit the following events:

| Event           | Description                                              |
| --------------- | -------------------------------------------------------- |
| `ready`         | The checkout iframe is ready or has finished loading.    |
| `resize`        | The iframe requests a height update.                     |
| `state_change`  | Checkout state changed.                                  |
| `complete`      | A terminal state was reached.                            |
| `hosted_return` | The hosted checkout returned control to the parent page. |
| `error`         | SDK or polling error.                                    |

Possible embedded states are:

* `payment`
* `pending`
* `success`
* `cancelled`
* `error`
* `expired`

Semantics to keep in mind:

* `complete`: The checkout reached a terminal payment state, either from the checkout page itself or via `pollStatus`.
* `hosted_return`: A hosted success or cancel page returned control to the parent page. Use this for UI cleanup or navigation.
* `error`: An SDK or polling failure, not necessarily a payment terminal state.

## Bootstrap Environment

The SDK supports fixing the remote bootstrap environment with `CLINK_ENV`:

* `CLINK_ENV=sandbox` → `https://uat-api.clinkbill.com/api/sdk/bootstrap`
* `CLINK_ENV=production` → `https://api.clinkbill.com/api/sdk/bootstrap`

`checkoutEnvironment` uses the same values: `sandbox` and `production`.

Priority order:

1. `loadClink(..., { checkoutBaseUrl })`
2. `loadClink(..., { checkoutEnvironment })`
3. `CLINK_ENV`

## Error Handling

The SDK throws `ClinkError` for validation, bootstrap, and embedded checkout failures.

```ts theme={null}
import {
  CLINK_ERROR_CODES,
  ClinkError,
  loadClink,
} from '@clink-ai/clink-js';

try {
  const clink = await loadClink('pk_uat_xxxxxxxxx', {
    checkoutEnvironment: 'sandbox',
  });

  await clink.redirectToCheckout({
    sessionId: 'sess_xxx',
  });
} catch (error) {
  if (error instanceof ClinkError) {
    if (error.code === CLINK_ERROR_CODES.INVALID_PUBLIC_KEY) {
      console.error('Invalid publishable key');
    }
  }
}
```

Common error codes include:

* `INVALID_PUBLIC_KEY`
* `INVALID_CHECKOUT_ENV`
* `BOOTSTRAP_REQUEST_FAILED`
* `INVALID_BOOTSTRAP_RESPONSE`
* `INVALID_REDIRECT_PARAMS`
* `INVALID_EMBEDDED_OPTIONS`
* `INVALID_SESSION_ID`
* `SESSION_ID_FETCH_FAILED`
* `EMBEDDED_CHECKOUT_DISABLED`
* `CONTAINER_NOT_FOUND`
* `NOT_IN_BROWSER`

## References

* [Create Checkout Session](/api-reference/endpoint/create-checkout-session)
* [Checkout Session Guide](/guides/payments/checkout_session)
