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

# Browser SDK

> Control Darwin's chat from your website's code: open, close, hide and ask; identify signed-in visitors with a signed userHash; pass context; and listen for leads and booked meetings to fire analytics conversions.

The Darwin chat on your website comes with a small JavaScript interface, `Darwin(command, …)`. Use it to open the chat from your own buttons, tell it who a signed-in visitor is, and react when a visitor leaves their email or books a meeting. There is nothing extra to install: the [Darwin embed](/installation/install-widget) adds it.

```javascript theme={null}
Darwin("open");
Darwin("ask", "Can I see a demo?");
Darwin("on", "meeting_booked", (event) => console.log("Booked in", event.conversationId));
```

## Call it before the chat loads

The embed loads asynchronously, so `Darwin` may not exist yet when your code runs. Add this one line **before** the Darwin embed. It queues your calls, and the chat runs them in order as soon as it loads:

```html theme={null}
<script>
  window.Darwin=window.Darwin||function(){(window.Darwin.q=window.Darwin.q||[]).push(arguments)};
</script>
<script async src="https://app.meetdarwin.ai/widget.js"
  data-site-key="YOUR_WORKSPACE_SITE_KEY"
  data-api-url="https://app.meetdarwin.ai/api/chat"></script>
```

Use your own embed from **Integrations → Site tag**; the second tag above is only an example. Once the embed has loaded, every call works immediately, and calls made before the chat finished starting are queued, not lost.

## Commands

| Command | What it does |
| - | - |
| `Darwin("open")` | Opens the chat |
| `Darwin("close")` | Closes the chat. The launcher stays. |
| `Darwin("hide")` | Closes the chat and hides the launcher for this page view |
| `Darwin("show")` | Shows the launcher again after `hide` |
| `Darwin("ask", text)` | Opens the chat and sends `text` as the visitor's message (up to 2,000 characters). If the agent is still answering, it waits and then sends. |
| `Darwin("ask", "btn_…")` | Runs one of your [chat buttons](#start-a-chat-from-your-own-button) by its id, with the words you set for it in Darwin |
| `Darwin("identify", { email, name, userHash })` | Tells the chat who a signed-in visitor is. [Details](#identify-signed-in-visitors). |
| `Darwin("context", { … })` | Gives the agent facts about the visitor, such as plan or seats. [Details](#pass-context). |
| `Darwin("on", event, fn)` | Calls `fn` when an [event](#events) happens |
| `Darwin("off", event, fn)` | Stops calling `fn` for that event. Pass the same function you passed to `on`. |
| `Darwin("shutdown")` | Forgets the identity and context for this browser tab and closes the chat. Call it when a visitor logs out. |

### Start a chat from your own button

```html theme={null}
<button onclick="Darwin('ask', 'I would like to talk about pricing for my team.')">Talk to us about pricing</button>
```

`ask` sends the text as if the visitor typed it, so write it in their voice.

To use a styled button you made in **Agent Studio → Buttons**, paste its HTML from there, or call it by id: `Darwin("ask", "btn_k3j9x2m4p7")`. Its id is in the button's HTML and in `GET /v1/chat_buttons`.

## Identify signed-in visitors

If visitors log in to your site or app, you can tell the chat who they are. A verified visitor:

* becomes a **contact** in Darwin (with `identity_verified: true`), linked to the conversation;
* finds the booking form **already filled in** with their name and email, so the agent never asks for them;
* may be **greeted by name** by the agent.

Darwin only trusts an identity your **server** has signed. Anyone can type `Darwin("identify", …)` into a browser console, so an identity without a valid `userHash` is ignored entirely.

<Steps>
  <Step title="Create the identity secret">
    Open **Settings → API → Website identity** and select **Create secret**. Copy the secret (it starts with `dis_`). Darwin shows it once. Keep it on your server with your other secrets; it must never reach the browser.
  </Step>

  <Step title="Sign the email on your server">
    For each signed-in visitor, compute `userHash`: the **HMAC-SHA256** of their email address, **trimmed and lowercased**, keyed with the identity secret exactly as Darwin showed it, as a **hex** string.

    <CodeGroup>
      ```javascript Node theme={null}
      import crypto from "node:crypto";

      export function darwinUserHash(email) {
        return crypto
          .createHmac("sha256", process.env.DARWIN_IDENTITY_SECRET) // dis_...
          .update(email.trim().toLowerCase())
          .digest("hex");
      }
      ```

      ```python Python theme={null}
      import hashlib, hmac, os

      def darwin_user_hash(email: str) -> str:
          return hmac.new(
              os.environ["DARWIN_IDENTITY_SECRET"].encode(),  # dis_...
              email.strip().lower().encode(),
              hashlib.sha256,
          ).hexdigest()
      ```
    </CodeGroup>
  </Step>

  <Step title="Pass it to the page">
    Render the visitor's email, name and hash into the page (or return them from an endpoint your page calls), then identify:

    ```javascript theme={null}
    Darwin("identify", {
      email: "jordan@example.com",
      name: "Jordan Lee",
      userHash: "3f1c…e9a0", // from your server, 64 hex characters
    });
    ```
  </Step>

  <Step title="Forget them on logout">
    Call `Darwin("shutdown")` in your logout flow. The identity is kept for the browser tab otherwise, so the next person on a shared computer would inherit it.
  </Step>
</Steps>

**It worked if**, after the signed-in visitor sends a message, `GET /v1/contacts?email=…` returns a contact with `"identity_verified": true`. When the agent offers a meeting, the form already holds their name and email.

The identity travels with each chat message the visitor sends, so a verified visitor appears in Darwin once they chat, not when the page loads. **Replace secret** in **Website identity** creates a new secret; hashes made with the old one stop verifying at once, so update your server at the same moment.

## Pass context

Give the agent facts your page already knows, so it can answer in context:

```javascript theme={null}
Darwin("context", { plan: "growth", seats: 24, trial_ends_in_days: 3, is_admin: true });
```

* Each call adds to or overwrites keys from earlier calls. Context lasts for the browser tab, like identity, and `shutdown` clears it.
* Keys start with a letter and use letters, digits and `_`, up to 40 characters. Values are strings (up to 200 characters), numbers or `true`/`false`. Up to 20 keys are used.
* The agent treats context as **unverified hints**. A visitor can change it in their browser, so it never unlocks anything, changes prices or terms, or overrides the agent's instructions. Never put secrets or anything you wouldn't show the visitor in it.

## Events

| Event | When |
| - | - |
| `ready` | The chat has loaded on the page |
| `open` | The chat opened |
| `close` | The chat closed |
| `conversation_started` | The visitor sent their first message in a new conversation |
| `lead_captured` | The visitor gave their email in the chat |
| `meeting_booked` | The visitor finished booking a meeting in the chat |
| `handoff_requested` | The visitor's request to talk to a person was recorded |

Your function receives one object: `{ name, conversationId }`. `conversationId` is present on the conversation, lead, booking and handoff events. The API's id for the same conversation is `"conv_" + conversationId`. Events never include the visitor's name or email, so they are safe to send to analytics tools.

```javascript theme={null}
function onBooked(event) {
  console.log(event.name, event.conversationId);
}
Darwin("on", "meeting_booked", onBooked);
// later
Darwin("off", "meeting_booked", onBooked);
```

Register listeners with the pre-load line above so you don't miss `ready`. A listener that throws an error doesn't stop the chat.

Browser events tell your page what the visitor just did. For a record your systems can rely on (the calendar provider's confirmation, a lead in your CRM), use [webhooks](/api-reference/webhooks) such as `meeting.booked`, which Darwin's server sends.

### Listen without calling Darwin: darwin:event

Every event is also dispatched on `window` as a `darwin:event` custom event whose `detail` is the same object. Tag managers can listen for it without depending on `Darwin` having loaded:

```javascript theme={null}
window.addEventListener("darwin:event", (e) => {
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({ event: "darwin_" + e.detail.name, darwin_conversation_id: e.detail.conversationId });
});
```

In Google Tag Manager, put this in a **Custom HTML** tag that fires on **All Pages**, then create **Custom Event** triggers for `darwin_meeting_booked` or `darwin_lead_captured`.

## Example: count booked meetings as a GA4 conversion

With the Google tag (`gtag.js`) on your site:

```html theme={null}
<script>
  window.Darwin=window.Darwin||function(){(window.Darwin.q=window.Darwin.q||[]).push(arguments)};
  Darwin("on", "meeting_booked", function (event) {
    gtag("event", "darwin_meeting_booked", { conversation_id: event.conversationId });
  });
  Darwin("on", "lead_captured", function (event) {
    gtag("event", "generate_lead", { lead_source: "darwin_chat", conversation_id: event.conversationId });
  });
</script>
```

Then, in Google Analytics, find `darwin_meeting_booked` in your events list once it has been received and mark it as a key event.

**It worked if** GA4's **Realtime** report shows `darwin_meeting_booked` after you book a test meeting through the chat on your live site. Bookings in Darwin's own previews don't fire it.

## Content security policy

If your site sends a `Content-Security-Policy`, the pre-load line is an inline script: allow it with a nonce or hash, or put it in a file you already serve. The chat itself needs its script and network connections allowed, as described in [Platforms](/installation/platforms).


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