# Keys

Source: https://docs.mirafive.io/keys

> The three MIRA FIVE keys, where to create each one, where it may appear, and how to rotate it.

MIRA FIVE has three kinds of key. A **website key** sends events from browsers, a **secret key** sends events from servers, and an **API key** reads your analytics through the REST API and the MCP server.

| Key | Looks like | Used by | May appear in |
| --- | --- | --- | --- |
| Website key | `mf_ab12cd34_…` | Script tag, browser and framework SDKs | Page source, URL paths, public env vars |
| Secret key | `mf_ab12cd34_…` | Server SDKs, the ingest API from a server | `Authorization: Bearer` header only |
| API key | `mf_pat_ab12cd34_…` | REST API, MCP server | `Authorization: Bearer` header only |

Website and secret keys have the same shape. What makes a key one or the other is the source it belongs to, so keep track of which is which when you store them.

## Sources

Events arrive through a **source**, and each source belongs to a project. A project usually has two:

- a **Website** source for the browser, with the list of domains allowed to send to it, and
- a **Server** source for your backend.

Each source has its own keys, its own status, and its own [collection mode](https://docs.mirafive.io/guides/consent): **Consentless** or **Full**. The mode is a ceiling. A consentless source refuses batches sent in full mode.

## Create a website key

1. In the dashboard, open the project and go to **Data → Sources → Add source**.
2. Choose **Website**. Enter a **Name**, the **Allowed origins** and the **Collection mode**.
3. Create the source. It comes with a first key named **Default key**, shown on the **Install** tab together with a ready snippet.

Put the key in your framework's public env var:

```sh title=".env"
# Pick the one your framework reads
NEXT_PUBLIC_MIRAFIVE_KEY=mf_…
VITE_MIRAFIVE_KEY=mf_…
PUBLIC_MIRAFIVE_KEY=mf_…
NUXT_PUBLIC_MIRAFIVE_KEY=mf_…
```

A website key is public by design: every visitor can read it in the page. What protects it is the list of allowed origins. You can show and copy it again from the source's **Ingest keys** tab at any time.

### Allowed origins

Browsers may only send with a website key from an origin on its source's list.

- Enter exact origins: `https://shop.example.com`, or `http://host:port`. No paths, queries or wildcards.
- `example.com` and `www.example.com` count as one site.
- `localhost` and `127.0.0.1` on any port are always allowed, so you can test without adding them.
- Up to 50 origins per source.

A request from any other origin is refused with `403 origin_not_allowed`.

## Create a secret key

1. Go to **Data → Sources → Add source** and choose **Server**.
2. Name the source and create it.
3. Copy the key now. A secret key is **shown once**. MIRA FIVE stores only a hash of it.

Put it in your server's environment:

```sh title=".env"
MIRAFIVE_SECRET_KEY=mf_…
```

> **Warning:** Never ship a secret key to a browser. If one is sent from a browser, MIRA FIVE marks it as exposed: events still arrive, but the feature-flag endpoints refuse it with `403 secret_key_exposed`. Issue a new key and revoke the exposed one.

## Create an API key

1. Open the account menu and go to **API & MCP**.
2. Under **API keys**, choose **New API key**. Enter a **Name** (for example "Nightly report script") and pick when it **Expires after**: 30 days, 90 days, 1 year or never.
3. Choose **Create key** and copy it. It is **shown once**.

```sh
curl https://app.mirafive.io/api/v1/projects \
  -H "Authorization: Bearer $MIRAFIVE_API_KEY"
```

An API key acts as you: it sees every project you can see, in every organization you belong to. The REST API is read-only whatever the key allows. For the MCP server, each key has two switches, **May change setup** and **May turn flags on and off**. Both are off by default, and the key can then only read. See [MCP server](https://docs.mirafive.io/mcp#permissions).

You can hold up to 20 API keys. Agents that support OAuth, like Claude and Cursor, do not need an API key. They sign in through the browser instead. See [MCP server](https://docs.mirafive.io/mcp).

## Environment variables

Every SDK reads the same names:

| Variable | Holds | Where |
| --- | --- | --- |
| `MIRAFIVE_WEBSITE_KEY` | Website key | Server-rendered frameworks that output the script tag |
| `NEXT_PUBLIC_MIRAFIVE_KEY`, `VITE_MIRAFIVE_KEY`, `PUBLIC_MIRAFIVE_KEY`, `NUXT_PUBLIC_MIRAFIVE_KEY` | Website key | Browser bundles, by framework |
| `MIRAFIVE_SECRET_KEY` | Secret key | Server only |
| `MIRAFIVE_HOST` | Events host, default `https://events.mirafive.io` | Only when you send somewhere else |

The API key has no standard name; the examples here use `MIRAFIVE_API_KEY`.

## Rotate or revoke a key

- **Website or secret key:** on the source's **Ingest keys** tab, choose **Issue key**, deploy the new key, then revoke the old one. A revoked key is refused with `401 unauthorized` at once.
- **API key:** create a new key on **API & MCP**, switch your scripts over, then **Revoke** the old one.
- **OAuth apps** (connected agents): **Disconnect** the app on **API & MCP**. That revokes all its tokens.

Only an organization owner or admin can issue and revoke source keys.

> **Note:** On a **Full** website source, a new key gives every browser a new anonymous id. While a browser-split experiment is running on that source, MIRA FIVE refuses to issue or revoke its keys, so the experiment's groups stay intact.

## Key refusals

| Answer | Cause | Fix |
| --- | --- | --- |
| `401 unauthorized` | The key is unknown or revoked, or its source or project is archived. | Copy the key again from the dashboard. |
| `403 website_key_as_bearer` | A website key was sent as a bearer token. | Servers use the secret key. |
| `403 secret_key_in_path` | A secret key was put in a URL. | Browsers use the website key. |
| `403 origin_not_allowed` | The page's origin is not on the source's list. | Add it under the source's allowed origins. |
| `403 secret_key_exposed` | A secret key was used from a browser (flag endpoints). | Issue a new key and keep it on the server. |

Every refusal is recorded on the key, and the source page shows the most recent one.
