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

# Embedded SDK

> Embed Bindbee's connector link flow inside your own product, with React or plain JavaScript.

[Magic Link](/get-started/how-to-connect) hands your customer a URL and needs no frontend code. The Embedded SDK does the same job inside your product: your user clicks a button in your UI, authorizes their HR system in an overlay, and never leaves your app.

<Frame caption="The full round trip: your server creates a link token, the user authorizes in the overlay, and the temporary token that comes back is exchanged — server-side again — for the connector token you store.">
  <img src="https://mintcdn.com/unifyx-56/NU46D4mywOWu_9HZ/images/guides/sdk_workflow.webp?fit=max&auto=format&n=NU46D4mywOWu_9HZ&q=85&s=b817beba0428afa4118458a05ac1819a" alt="A four-step diagram. Step 1, initiate the SDK: your server POSTs to Bindbee for a link_token, and your app's integrations page shows a Connect button on BambooHR. Step 2, authentication flow: the user approves access to each data model and enters their BambooHR subdomain. Step 3, get temporary token: a Connection Established confirmation returns a temporary_token. Step 4, get connector token: your server sends the temporary_token in a GET and receives the connector_token." width="5670" height="2667" data-path="images/guides/sdk_workflow.webp" />
</Frame>

Steps 1 and 3 run on your server. Both use your API key, which must never reach the browser.

## Step 1: Create a link token

A link token authorizes one linking session for one end user. Full reference: [Create Link Token](/sdk/create-link-token).

```bash theme={null}
POST /api/embedded/v1/link/create-link-token
Authorization: Bearer <BINDBEE_API_KEY>
```

<CodeGroup>
  ```json Request theme={null}
  {
    "end_user_data": {
      "org_name": "Acme Inc",
      "origin_id": "acme-4471",
      "email": "hr@acme.com"
    },
    "category": "HRIS",
    "integration": "bamboohr"
  }
  ```

  ```json Response theme={null}
  {
    "link_token": "KJDXZk8zC8Tdr-suQCy7zkALZVLjhKXyMd83leqheEGQpBx_evXrHA"
  }
  ```
</CodeGroup>

| Field | Required | Notes |
| - | - | - |
| `end_user_data.org_name` | Yes | The customer organization this connector belongs to. Minimum 2 characters. |
| `end_user_data.origin_id` | Yes | Your own unique identifier for the end user. This is how you match the connector back to a record in your system. |
| `end_user_data.email` | No | Contact email for the end user. |
| `category` | Yes | One of `HRIS`, `ATS`, `LMS`, `CUSTOM`. |
| `integration` | Yes | The integration slug, for example `bamboohr`. See [Integrations](/get-started/integrations). |

<Warning>
  Call this from your **server**, never the browser. It takes your Bindbee API key, and exposing that key would let anyone read every connector in your account.

  The key also decides the environment: a Development key creates a Development connector, a Production key a Production one. See [Environments](/get-started/environments).
</Warning>

## Step 2: Open the link flow

Pass the token to the SDK. Opening the flow mounts a full-screen overlay on top of your app, where the user signs in to their HR system and authorizes access.

<Tabs>
  <Tab title="React" icon="react" iconType="brands">
    ```bash theme={null}
    npm install --save @bindbee/react-link
    ```

    React 18 or later. The package ships its own TypeScript types.

    ```jsx App.jsx theme={null}
    import { useBindbeeMagiclink } from "@bindbee/react-link";

    const App = ({ linkToken }) => {
      const { open } = useBindbeeMagiclink({
        linkToken,
        onSuccess: (temporaryToken) => {
          // Send this to your backend to exchange for a connector token
          fetch("/api/bindbee/exchange", {
            method: "POST",
            body: JSON.stringify({ temporaryToken }),
          });
        },
        onClose: () => console.log("Linking flow closed"),
      });

      return <button onClick={open}>Connect your HR system</button>;
    };
    ```

    The hook returns `{ open }`. Call it from a click handler.
  </Tab>

  <Tab title="JavaScript" icon="js" iconType="brands">
    For any frontend that is not React. Add the script, then initialize it.

    ```html theme={null}
    <script
      src="https://cdn.bindbee.dev/initialize.min.js"
      integrity="sha512-1L1XqHiVgenLFd95eE8fqJ7o6kje+5+3IN7K0cuwNP6feTrYAeNTdpeqR1e2HgThSb5Bj+USqJeNiyEE9KBtfA=="
      crossorigin="anonymous"
    ></script>
    ```

    ```js theme={null}
    window.BindbeeEmbed.initialize({
      linkToken: "LINK_TOKEN",
      onSuccess: (temporaryToken) => {
        // Send this to your backend to exchange for a connector token
      },
      onClose: () => console.log("Linking flow closed"),
    });

    // Then, from a click handler
    window.BindbeeEmbed.open();
    ```

    `initialize()` throws if `linkToken` is missing, and `open()` throws if called before `initialize()`. Call `window.BindbeeEmbed.destroy()` to remove the overlay and its message listener.

    `integrity` verifies the script has not been tampered with, and `crossorigin="anonymous"` is required for that check to run. An unminified build is at `https://cdn.bindbee.dev/initialize.js`, with integrity `sha512-2Rzjv7TfBrXNCgB1wLTnpZupTZh6/JS0tQkKTGRu9fC7IAK4ahbPzcu29VUGHOmRPquLNU6RkLyt6yVuKR60DQ==`.
  </Tab>
</Tabs>

Both take the same options:

| Option | Type | Notes |
| - | - | - |
| `linkToken` | `string` | Required. The token from Step 1. One token, one linking session. |
| `serverUrl` | `string` | Set to `https://api-eu.bindbee.dev` for the EU region. Defaults to US. |
| `onSuccess` | `(temporary_token) => void` | Fires when the user completes the flow. You get a temporary token, not the connector token. |
| `onClose` | `() => void` | Fires when the overlay is dismissed, whether or not linking succeeded. |

<Note>
  Register **one instance** per page. A second one logs an error and will not behave predictably. In React, lift the hook to a shared parent if several places need to trigger it.
</Note>

## Step 3: Exchange for a connector token

The temporary token from `onSuccess` is not an API credential. Send it to your backend and exchange it for the real one. Full reference: [Get Connector Token](/sdk/get-connector-token).

```bash theme={null}
GET /api/embedded/v1/connectors/connector_token/{temporary_token}
Authorization: Bearer <BINDBEE_API_KEY>
```

```json Response theme={null}
{
  "connector_token": "jk23h4jk23hk4j2h3kj4h23kjh4k23jh4k2j3h4",
  "connector_id": "123e4567-e89b-12d3-a456-426614174000"
}
```

Store the `connector_token` against the customer record you identified with `origin_id`. It is the credential every later call uses, as the `X-Connector-Token` header on unified endpoints, [Passthrough](/guides/extending/passthrough), and [custom fields](/guides/extending/custom-fields).

This exchange uses your API key, so it belongs on your server too. Treat the returned `connector_token` as a secret: it grants access to that customer's HR data.

## Behavior worth knowing

| | |
| - | - |
| Overlay | A fixed, full-screen iframe with id `magic-link-flow`, prepended to `document.body`. |
| Page scroll | Locked while the overlay is open, restored on close. |
| Dismissal | Closing from inside the flow removes the iframe and fires `onClose`. |
| Repeat opens | Calling `open()` while the overlay is already mounted does nothing. |
| Styling | The flow is Bindbee-hosted. Branding is configured in the dashboard, not through the SDK. |

## Errors

| Status | Meaning | Fix |
| - | - | - |
| `401` | API key missing or invalid. | Check the `Authorization` header on your server call. |
| `403` | Credentials valid but not permitted for this resource. | Confirm the category and integration are enabled for your account. |
| `404` | No connector found for that temporary token. | Tokens are single use and short-lived. Re-run the flow. |
| `422` | Validation failed. | Check `org_name`, `origin_id`, `category` and `integration`. |
| `429` | Rate limit exceeded. | Retry after `Retry-After`. See [Rate limits](/api-reference/basics/rate-limits). |

## Related

<CardGroup cols={2}>
  <Card title="npm package" icon="box" href="https://www.npmjs.com/package/@bindbee/react-link">
    `@bindbee/react-link` on npm.
  </Card>

  <Card title="Authentication" icon="key" href="/api-reference/basics/authentication">
    API keys and connector tokens.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks/overview">
    Get notified when a new connector finishes its first sync.
  </Card>

  <Card title="Docs MCP" icon="bot" href="/guides/sdk/mcp">
    Point your AI assistant at these docs while you build.
  </Card>
</CardGroup>


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