# Open a screen: the one-time link (/docs/embed/open-a-screen)

Add **one endpoint** to your server. Your page calls it every time it shows a screen; it checks your user is signed in, then asks us for a link with your embed key.

## The call [#the-call]

`POST /v3/embed/sessions`, with `Authorization: Bearer $HL_EMBED_KEY`.

<ApiPlayground method="post" path="/v3/embed/sessions" tool="" args={`{ "websiteId": "64b7f0a1c2d3e4f5a6b7c8d9", "view": "auto" }`} />

The `url` works **once, within 60 seconds**. Put it straight into the iframe; never store it, never log it.

| Field | Required | What it is |
| --- | --- | --- |
| `websiteId` | Yes (unless `scope` is `account`) | The signed-in user's company website. Always from your database, never from the request. |
| `view` | No, default `auto` | The screen to open (below). |
| `scope` | No, default `website` | `account` opens the admin view of every website of your account: **your own staff only**. |
| `user.email` | No | Only if you switched on CSV export: the export's file link is emailed there. Without it the Export button is hidden. |

<Callout title="No personal data needed">
Send only the website and the screen. We never need your users' names or emails.
</Callout>

## Which screen: `view` [#which-screen-view]

| `view` | Opens |
| --- | --- |
| `auto` | The install screen until their pixel works, then their leads. **Use this by default.** |
| `leads` | Their leads |
| `install` | The tracking-pixel install screen: their snippet and a Verify button |
| `automations` · `integrations` · `settings` · `qualify` · `engage` | An extra screen, only if you ticked it in Embed settings |

One iframe per screen, each behind its own menu item in your app.

## Your endpoint [#your-endpoint]

```js title="server.js"
app.get('/api/leads-embed', requireLogin, async (req, res) => {
  const staff = req.user.isStaff; // your rule: who may see every website
  const view = ['auto', 'leads', 'install'].includes(req.query.view) ? req.query.view : 'auto';
  const r = await fetch(`${process.env.HL_API_BASE}/v3/embed/sessions`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.HL_EMBED_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify(staff ? { scope: 'account' } : { websiteId: req.user.company.happierleadsWebsiteId, view }),
  });
  if (!r.ok) return res.status(502).json({ error: 'Could not open this screen' });
  res.set('Cache-Control', 'no-store').json({ url: (await r.json()).url });
});
```

- **Not signed in:** `requireLogin` answers `401` before anything reaches us.
- **A company with no website yet** (signed up while our API was down): create it on the spot ([Create a website](/docs/embed/create-website)), then continue.
- Keep our name out of your route: `/api/leads-embed`, not `/api/happierleads`.

## Errors [#errors]

| Status | Why | What to do |
| --- | --- | --- |
| `400` | Unknown `view`, a screen you didn't tick, or a website outside your account | Check the `status` message |
| `401` | Wrong or revoked embed key, or embedding is switched off | Check `HL_EMBED_KEY` and Embed settings |

**Next:** [show it in an iframe](/docs/embed/iframe).
