# Security: server vs browser (/docs/embed/security)

A key in a web page can be read by anyone who opens it. So keys only ever live on your server, and the browser only ever gets a link that works once.

## Your server only [#your-server-only]

- **Master key** (`master-priv-…`): acts as the account owner.
- **Embed key** (`hle_…`): can only open embedded sessions.
- Creating, changing and deleting websites, setting credits and trials.
- Asking for the one-time link, **after your own sign-in check**.

## Safe in the browser [#safe-in-the-browser]

- The tracking pixel snippet: it only carries the website's public id.
- The one-time link: it works once, within 60 seconds.
- The messages the frame sends your page (`ready`, `session-ended`…).

## Never [#never]

- A key in front-end code, a mobile app or a public repository.
- `DELETE /admin/websites/{id}`, or any write, from the browser: proxy it through your server after your own permission check.
- A `websiteId` taken from the request: always from the signed-in user's company in your database.
- One login shared between clients: one website and one client view each.
- `scope: "account"` for anyone but your own staff.

## If a key leaks [#if-a-key-leaks]

- **Embed key:** revoke it under **Embed in your app → Server keys**. Its open sessions end on their next request. Create a new one first to avoid downtime (up to 5 can be active).
- **Master key:** rotate it, see [Authentication](/docs/authentication#if-a-key-leaks-rotate-it).

## Before you go live [#before-you-go-live]

- [ ] `grep` your front-end build output for `master-priv-` and `hle_`: no hits.
- [ ] Signed out, your endpoint answers `401` without calling us.
- [ ] Sending another company's `websiteId` still opens only the user's own.
- [ ] The link response has `Cache-Control: no-store` and is never logged.

**Next:** [build it with your AI coding assistant](/docs/embed/build-with-ai).
