---
title: "Keys and IP allowlist"
description: "Server-to-server only. How the private key works, what the IP allowlist accepts, and how to keep the key on your server."
canonical: "https://docs.nimbusmint.bot/authentication/"
---
> Index: https://docs.nimbusmint.bot/llms.txt

# Keys and IP allowlist

The Partner API is **server-to-server only**. Your servers call NimbusMint with a private key; browsers and apps
never do.

## Keep the key on your server

If your product has a website or app, put your own server between it and NimbusMint (a "backend for
frontend"):

```text
browser or app  ──>  your server (holds the private key)  ──>  api.nimbusmint.bot/partner/v1
```

- Your frontend calls **your** server, with your own user sessions and checks.
- Your server calls NimbusMint with the private key and returns only what your frontend needs.
- Nothing NimbusMint-issued ever reaches a browser, so there is nothing to copy out of your page source.

Your **app's identity is attached server-side**: every call made with your private key is attributed to your app.
You never send an app identifier or any NimbusMint key from a browser.

> **Note:** Browser-direct calls (short-lived tokens minted by your server) are not offered yet. Contact us if you need
> browser-direct calls.

## The private key

Send it on every request in the `Authorization` header:

```http
Authorization: Bearer nmk_live_<id>_<secret>
```

For example (a fake key):

```http
Authorization: Bearer nmk_live_exampleid000_0000000000000000000000000000000000000000000000000000000000000000
```

- **Prefix.** `nmk_live_` marks a production key. The fixed prefix lets secret scanners (GitHub and others)
  spot a leaked key.
- **Key ID.** `<id>` is 12 characters; the key ID shown to you is `nmk_<id>`. It is not secret: it appears in
  `/health`, our logs and support tickets. `<secret>` is 64 hex characters.
- **Shown once.** The `secret` is shown once, when the key is issued. NimbusMint stores only a hash of it and
  cannot show it again. If you lose it, ask for a new key.
- **Expiry.** Every key expires after at most **one year**. Plan a [rotation](https://docs.nimbusmint.bot/authentication/key-rotation.md)
  before then.
- **Scopes.** Each key has scopes. v1 has one: `read:hits`, needed for
  [`GET /hits`](https://docs.nimbusmint.bot/api/operations/gethits.md).
- **Rate limits** are set per key. See [Rate limits](https://docs.nimbusmint.bot/rate-limits.md).

> **Never ship the private key to a client:** Do not put the private key in browser code, a mobile app, a Discord message, a public repository, a URL or a
> log line. Keep it in your server's secret store and send it only to `api.nimbusmint.bot`.

### curl

```sh
curl https://api.nimbusmint.bot/partner/v1/health \
  -H "Authorization: Bearer $NIMBUS_PRIVATE_KEY"
```

### Node

```js
const headers = { Authorization: `Bearer ${process.env.NIMBUS_PRIVATE_KEY}` };
const res = await fetch('https://api.nimbusmint.bot/partner/v1/health', { headers });
```

### Python

```python
headers = {"Authorization": f"Bearer {os.environ['NIMBUS_PRIVATE_KEY']}"}
res = requests.get("https://api.nimbusmint.bot/partner/v1/health", headers=headers, timeout=10)
```

### How a request is checked

In order: the key is parsed and looked up, the secret is compared, then we check that the key is not revoked
or expired, that the request comes from an allowlisted IP, that the route exists, that the key has the route's
scope, and finally the [rate limit](https://docs.nimbusmint.bot/rate-limits.md). Because the key is checked first, a request without a valid
key learns nothing about which paths exist.

Every key problem (missing, malformed, unknown, wrong secret, revoked, expired, partner disabled) returns the same
`401 invalid_key`, on purpose: the response never says which part failed. An allowlist or scope problem returns
a `403` with a specific code. See [Errors](https://docs.nimbusmint.bot/errors.md).

## IP allowlist

Every private key has an allowlist of source addresses. Requests from anywhere else get
`403 ip_not_allowed`, even with the right key.

- The allowlist is **required**: a key cannot be issued without one.
- Send us the **public egress** addresses of every server that calls the API: the address the internet sees,
  not a private `10.x` or `192.168.x` address.
- Up to **16** entries, as single addresses or CIDR ranges, public addresses only. IPv4 ranges can be at most a
  `/24` and IPv6 ranges at most a `/48` (for example `203.0.113.0/24`, a documentation range).
- "Allow everything" (`0.0.0.0/0`, `::/0`) is never accepted.

> **Changing hosts or providers:** If your server moves to a new host, region or cloud provider, its egress IP usually changes. Send us the new
> address **before** you switch, or every call will fail with `ip_not_allowed`.

## Request signing (coming soon)

Next, keys gain **Ed25519 request signing**: you sign each request with a private key that never leaves your
server, and we hold only the public half. See [Request signing](https://docs.nimbusmint.bot/authentication/request-signing.md).

## Revocation

We can revoke a key or disable a partner at any time; it takes effect **within 10 seconds**. Ask us to revoke a
key straight away if you think it has leaked.
