# Embed widget

Drop your Bynefit group into any website with one `<script>` tag. No iframes, no CSS conflicts — the widget renders in Shadow DOM so your site's styles stay put.

## Quick start

Paste this anywhere on your page — usually right before `</body>`:

**HTML (your site)**

```html
<script
  src="https://bynefit.com/widget.v2.js"
  data-key="bapi_tk_your_key_here"
  async></script>
```

The widget adds a small floating card in the corner of your site showing your group name, a live stat (members, campaign progress, next event), and a primary action button. Clicking it opens a contained side drawer (desktop) or bottom sheet (mobile) with everything else.

> **Two versions are live.** `widget.v2.js` is the current widget — floating card + drawer, with auto-derived primary action. `widget.js` is the original — bubble + full-screen overlay, still maintained, still supported. Existing embeds keep working; new embeds should use `v2.js`.

> **Older widget keys were switched off on 20 October 2026.** If your embed uses a widget key instead of a `bapi_tk_…` token, create a team key below and replace the `data-key` value now: embeds still using one get a 410 error. See [Migrating from the legacy endpoint](https://bynefit.com/docs/legacy-sunset).

## Get your key

1. Go to [/dash/integrations/api-tokens](https://bynefit.com/dash/integrations/api-tokens).
2. Click **Create a token**.
3. Give it a label like "Website embed".
4. Check **`embed:widget`** in the scopes list. This is the only scope the widget needs.
5. Optionally list the domains allowed to use it (recommended — see below).
6. Click create. Copy the `bapi_tk_…` value *immediately* — it's shown once.

### Or make a browser key

A browser key is made for a web page. It belongs to your Space, carries only the widget permission, and is refused on any website that is not listed under Embeds, so it is safe to show in your page source.

1. Go to [/dash/integrations/api-tokens](https://bynefit.com/dash/integrations/api-tokens) and choose **Create key**.
2. Choose **Run in a visitor's browser** and name the key.
3. Copy the `bapi_pk_…` value *immediately*. It is shown once.
4. Use it as `data-key`, and list your website under [Embeds](https://bynefit.com/dash/integrations/embeds).

A browser key follows the allowed sites on your Embeds page, not a list of its own. A Space key with the widget permission keeps working exactly as before, including its own allowed-websites list.

> **Already have a token?** You can add `embed:widget` to an existing token from the same page — click **Edit**, check the scope, save. No need to rotate or recreate.

## Domain locking

If you listed allowed domains on the token, only those domains can load the widget; anything else is rejected with `domain_mismatch`, so a copy of your embed snippet on someone else's site won't work. To move embeds to a new domain, edit the token's allowed-domains list at [/dash/integrations/api-tokens](https://bynefit.com/dash/integrations/api-tokens).

If the token has no list, the widget uses the allowed sites for your Space. Space admins add and remove sites themselves under Embeds in the dashboard, and every admin is emailed when the list changes. A site that is not on the list is rejected with `domain_mismatch`.

## The Embeds hub

Space admins manage the snippet, the allowed websites and the widget's settings in one place: [/dash/integrations/embeds](https://bynefit.com/dash/integrations/embeds). It also shows which websites tried to load your widget and were not on the list, with a one-click *Allow*. Every admin of the Space is emailed when a site is added or removed, the key is replaced, or the widget is switched off.

| What loads | What happens |
| --- | --- |
| A page on a website on the allowed list | The widget loads. |
| A page on a website that is not on the list | `403` `domain_mismatch`. The hub counts the attempt. |
| A sandboxed page or a local file (`Origin: null`) | `403` `origin_opaque` |
| A request whose origin cannot be read | `403` `origin_invalid` |
| The widget is switched off in the hub | `403` `Widget disabled` |

- An allowed site is an exact origin: scheme, host and port. `https://example.com` does not allow `https://www.example.com` or `https://example.com:8443`; add each one you use.
- Allowed sites are `https`, with no wildcards, paths, logins or IP addresses. You can allow up to 25. `localhost` is for non-production setups only.
- A key that has its own allowed-domains list uses that list instead of the Space's.
- During the move to enforcement, the hub shows the date after which sites not on the list stop loading, and lists the ones that would be blocked until then. Sites that were using the widget over plain `http` are allowed for a limited time and show as insecure: move them to `https`.

**The key in the snippet.** New snippets from the hub carry a browser key that starts with `bapi_pk_`. The hub shows the whole key once, when you press *Generate a new snippet*, and masks it afterwards; lost it? Generate a new one, and the old one stops working. A snippet that still carries a key starting with `wk_` was switched off on 20 October 2026. Until then it keeps working, and the hub offers *Copy the new snippet* with a before and after. See [Migrating from the legacy endpoint](https://bynefit.com/docs/legacy-sunset).

## Customize

Pass options as extra `data-` attributes on the script tag:

| Attribute | Values | Purpose |
| --- | --- | --- |
| `data-key` | *required* | Your API token with the `embed:widget` scope. |
| `data-mode` | `float` (default)  
`inline` | How the widget appears. See *Inline mode* below. |
| `data-position` | `bottom-right` (default)  
`bottom-left` | Where the floating card sits. Float mode only. |
| `data-accent` | Any CSS color (e.g. `#7a4cc3`) | Override your group's accent color for this embed. |
| `data-target` | Element id (e.g. `my-widget`) | Required for `data-mode="inline"`. The widget mounts inside the element with this id. |

## Inline mode

Inline mode renders the widget directly into a section of your page — no floating trigger, no overlay. Often converts better when the widget *is* the page's main content (e.g. a donate landing page or a community sign-up section).

**HTML (your site)**

```html
<div id="my-widget"></div>

<script
  src="https://bynefit.com/widget.v2.js"
  data-key="bapi_tk_your_key_here"
  data-mode="inline"
  data-target="my-widget"
  async></script>
```

Same hero card and section composition as the drawer — just rendered in document flow. The widget keeps its dark surface treatment; it doesn't inherit your host page's typography or colors.

## Deep linking

Link directly to a part of the widget from anywhere on your page using URL hashes:

| Hash | Opens to |
| --- | --- |
| `#bynli-open` | The widget drawer (default tab) |
| `#bynli-donate` | Donate hero |
| `#bynli-events` | Events section |
| `#bynli-feed` | Latest posts |
| `#bynli-resources` | Resources |
| `#bynli-join` | Join / become-a-member |

**HTML (your site)**

```html
<a href="#bynli-donate">Support our work</a>
```

## What's inside

The widget surfaces your team's identity (name, logo, accent color) and auto-picks a primary call-to-action based on what your team currently has live — donations, an active relief campaign, paid membership, or upcoming events.

The primary action is auto-picked based on your team's setup:

- Active relief campaign → **Donate to the campaign**
- Donations enabled → **Donate**
- Paid membership → **Become a member**
- Upcoming events → **See events**
- Otherwise → **Visit page**

## Accessibility

- The drawer is a proper modal with keyboard focus management and `Escape` to close.
- The mobile sheet has the standard drag-handle and snap-to-dismiss.
- The trigger card respects `prefers-reduced-motion` — the rotating live state and entry animations are disabled for users who request it.
- All interactive elements have visible focus rings.

## Troubleshooting

### Widget doesn't appear at all

Open your browser's developer console (F12). Look for messages prefixed with `[Bynefit]` or `[Bynefit v2]`.

- `data-key is required` — the attribute name is `data-key`, not `data-bynli-key` or anything else.
- `config request failed: HTTP 403 — Token missing
        embed:widget scope` — edit your token at [/dash/integrations/api-tokens](https://bynefit.com/dash/integrations/api-tokens) and check the `embed:widget` scope.
- `config request failed: HTTP 403 — Invalid API token` — the token doesn't exist or has been revoked. Create a new one.
- `config request failed: HTTP 403 — Public-tier tokens
        cannot embed the widget` — you used a developer key (`bapi_pk_…` from [/dash/developers/keys](https://bynefit.com/dash/developers/keys)) by mistake. The widget needs a Space key (`bapi_tk_…`) with the widget permission, or a browser key made on [/dash/integrations/api-tokens](https://bynefit.com/dash/integrations/api-tokens).
- `config request failed: HTTP 403 — domain_mismatch` — the widget is loading from a domain that isn't registered for your token. Check your token's allowed-domains list.

### Donate button leads to the wrong group

If a logged-in visitor clicks Donate and lands on their *own* group's checkout instead of yours, you're on an older version of the widget — update to `widget.v2.js` (or the latest `widget.js`).

### Team logo broken / wrong URL

The widget always loads images from bynefit.com regardless of where it's embedded. If your logo shows a broken-image icon, the URL stored on your group page is probably wrong. Re-upload the logo from your group settings.

## Open it programmatically

The deep-link hashes work programmatically too — just set `location.hash`:

**JavaScript (your site)**

```javascript
document.getElementById('donate-btn')
    .addEventListener('click', () => {
        location.hash = 'bynli-donate';
    });
```

---

Source: https://bynefit.com/docs/widget
Checked against the live API on October 10, 2026.
