# License page on your website

A "My license" page inside your own website, where customers sign in with an email link, see their licenses and free a computer. Two lines to paste.

The [customer portal](/docs/customer-portal/) is a separate page. If you would rather keep your customers on your own website, under your own header, menu and footer, put a **My license** page there instead. Your customers type their email, receive a sign-in link that brings them back to that page, and see:

- each license: product, plan, status in plain words (“Renews on …”, “Lifetime license, version 1”, “Payment pending”), and the license key with **Show** and **Copy**;
- the computers using it, out of the limit, with each computer's name, system and last activity;
- **Free this machine**, with a confirmation step, to move the license to a new computer;
- **Manage my subscription and invoices**, when you set a billing portal (for example Paddle's) in **Account & usage › Customer portal**.

Keyrail is never shown. The page is in English and French and follows your visitor's light or dark mode (you can force one, see below).

## Set it up

1. In the dashboard, open **Account & usage › “My license” page on your website**, type the address of the page (for example `https://www.yourdomain.com/my-license`) and click **Save**. Its website is added to the allowed website addresses if needed.
2. Paste the code shown there into that page, where the license area should appear:

```html
<div id="keyrail-license" data-product="prd_..."></div>
<script src="https://api.keyrail.tech/v1/public/license-page.js" defer></script>
```

3. Optional: click **Link key e-mails to this page**, so that the emails carrying the license key point to your page instead of the hosted portal.

The dashboard shows a live preview with your colour and name, signed out and signed in, in light and dark mode. The page uses your portal colour and name: change them in **Account & usage › Customer portal**.

| Attribute | Meaning |
|---|---|
| `data-product` | Any product ID of your account. It identifies your account: the page shows every license of the customer, all products included. |
| `data-lang` | `en` or `fr` to force the language. Default: the language of the sign-in link, else your page's `<html lang>`, else your account's default language. |
| `data-theme` | `light` or `dark` to force a theme. Default: the visitor's setting. If your website has no dark mode, set `data-theme="light"`. |
| `data-api` | The API address. Default: the address the script was loaded from. Set it to `https://api.keyrail.tech` if you serve the script from your own website. Only that address, the script's own address or `localhost` are accepted. |

**Content-Security-Policy.** If your website sends one, allow `https://api.keyrail.tech` in `script-src` and `connect-src`. The page needs no `unsafe-inline`: it draws itself in a Shadow DOM, so your stylesheet does not change it and it does not change your page. You can also download `license-page.js` and serve it from your own website (then add `data-api`, see above), but your copy does not get our updates: the script at our address does, within an hour.

## How sign-in works

1. The customer types their email. Keyrail sends a sign-in email, with your sender identity and language, **only if** this address has licenses with you; the page shows the same message either way.
2. The link opens your page with a one-time token after `#keyrail-token=`. Browsers never send this part of an address to a server: it does not reach your web server, its logs or your analytics. The page removes it from the address bar at once.
3. The token works once and expires after 15 minutes. It opens a session of 12 hours, kept in the tab (`sessionStorage`): closing the tab signs out, and so does **Sign out**.

## Routes, if you build your own page

The script only calls these routes; you can call them from your own code instead (from the browser, on an allowed website). Every request must come from one of your allowed website addresses: calls from another website, or from a server, are refused with `403`.

| Route | Purpose |
|---|---|
| `POST /v1/public/license-page/sign-in` | `{ productId, email, locale? }` → always `202`: sends the sign-in email if the address is known. |
| `POST /v1/public/license-page/session` | `{ token, productId }` → `{ session, expiresAt }`. `401 invalid_token` if the token was used, has expired or belongs to another vendor. |
| `GET /v1/public/license-page/licenses` | With `Authorization: Bearer <session>`: the customer's licenses and machines. |
| `POST /v1/public/license-page/activations/{id}/release` | Frees one of the customer's machines. Written to your audit log (**Events**) as `activation.deleted` from `license_page`. |
| `POST /v1/public/license-page/sign-out` | Ends the session. |
| `GET /v1/public/license-page/config?productId=` | Your brand name and colours for the page. |

Limits: 3 sign-in emails per hour per address, 20 per hour per visitor, 300 requests per hour per session, plus your plan's hourly email budget. An expired session answers `401 invalid_session`.

## Portal or page?

- **The hosted [customer portal](/docs/customer-portal/)**, on a subdomain such as `account.yourdomain.com`: nothing to integrate; it needs a verified sending domain. Best when your website is hard to change.
- **The license page**: your customers never leave your website, under your own menu; two lines to paste, no DNS change; your Content-Security-Policy must allow the API.

Both show the same licenses with the same rules. Pick one and link the key emails to it. The key emails follow **Account & usage › Customer e-mails › Customer portal linked from the e-mails** only: the license page changes it only when you click **Link key e-mails to this page**, and the dashboard keeps it in step when you move or turn off the page.

## Good to know

- **Same rules as the portal.** A license key alone never shows machines or frees them: the customer proves the email address first.
- **Works with any portal setting.** You can keep the hosted portal, use your billing provider's portal, or none at all.
- **Pair it with “Lost your key?”** On the same page, a [“Lost your key?” form](/docs/customer-portal/#lost-your-key-on-your-website) lets customers get their key by email without signing in.
- **Turn it off** by clicking **Turn off**: links already sent stop working. If the key emails linked to the page, they link to the hosted portal again.
