# How to handle API keys securely

> Best practices for SendPing API keys: keep them server-side, scope them to sending, rotate them, and revoke leaked keys.

A SendPing API key (it starts with `mb_`) authenticates every request as your account. Treat it like a password: anyone who has it can send email as you and, for a full-access key, manage your domains, audiences, contacts, and campaigns. What no key can do is touch the keys themselves — creating, re-scoping, and revoking keys happen only in the dashboard, so a stolen key can never mint a replacement for itself. The full key is **shown only once** at creation and is stored only as a **hash** afterward — SendPing can never show it to you again.

## Keep keys server-side only

- Call the API **from your server**, never from a browser, mobile app, or any other client the user controls — see [How do I fix CORS issues?](https://www.sendping.co/docs/kb/cors).
- Never commit keys to source control, embed them in client-side code, or paste them into logs, screenshots, or support tickets.
- Store keys in a **secret manager** or environment variable, not in your codebase.

> **Warning:** If a key ever appears in client-side code, a public repo, or a shared log, treat it as compromised and revoke it immediately.

## Use the least-privileged scope

API keys carry a permission level. Pick the **narrowest** one that does the job:

| Permission | Can do | Use for |
| --- | --- | --- |
| **sending_access** | Send email only. | Any server that only needs to send transactional or marketing email. |
| **full_access** | Send email **and** manage domains, audiences, contacts, and campaigns. It can *list* your API keys, but not create, re-scope, or revoke one. | Administrative tooling that genuinely manages resources. |

A `sending_access` key is the safe default: even if it leaks, it cannot delete your domains. Neither level can mint new keys — that is dashboard-only for every caller. See [Authentication](https://www.sendping.co/docs/authentication) and [Create an API key](https://www.sendping.co/docs/create-an-api-key).

## Rotate and revoke

SendPing API keys **do not expire automatically** — a key stays valid until you revoke it, with no built-in expiry or auto-rotation. Rotating them yourself on a schedule is what keeps a forgotten or leaked key from being a long-lived liability. Rotate **at least every 90 days**, and immediately if you suspect a key is compromised.

- **Rotate periodically.** In the dashboard, create a new key, deploy it, then revoke the old one — keys are cheap, so rotate on a schedule and whenever someone with access leaves.
- **Revoke leaked keys immediately.** Revoke from the **API Keys** page; it takes effect right away, and subsequent requests with the old key return `403 invalid_api_key`.
- **Use separate keys per service** so you can revoke one without disrupting the others.
- **Delete stale keys.** If a key has not been used recently, delete it rather than leaving it active — an unused key is just attack surface. Keys idle for 30+ days are flagged in the dashboard to help you find them.

### How to rotate without downtime

Both the old and new key work at the same time, so rotate by overlapping them — never delete the old key first. The two key-lifecycle steps happen in the dashboard; the deploy and verify steps in the middle are yours to automate:

1. **Create a new key (dashboard)** — On the **API Keys** page, create a replacement with the **same permission level** and domain restriction as the key you are retiring, and copy the token — it is shown once. See [Create an API key](https://www.sendping.co/docs/create-an-api-key).
2. **Update every service** — Deploy the new key to all environments that reference the old one.
3. **Verify it is working** — Confirm the new key is in use — for example by filtering the [logs](https://www.sendping.co/docs/logs/overview) by API key and checking for recent requests, or by listing your keys with `GET /api-keys` and reading each `last_used_at` — before going further.
4. **Revoke the old key (dashboard)** — Only once the new key is confirmed live everywhere, revoke the old key from the **API Keys** page. Revocation is immediate and irreversible.

> **Warning:** Do not delete the old key before the new one is deployed everywhere. Because both keys work simultaneously, verifying the new key first means zero downtime during the transition.

> **Warning:** You cannot script the create and revoke steps. `POST /api-keys`, `PATCH /api-keys/:id`, and `DELETE /api-keys/:id` refuse every API-key caller with `403 dashboard_only` — a key may never mint, re-scope, or revoke a key. If you previously automated rotation end to end, split it: a signed-in person creates and revokes in the dashboard, and your automation handles the deploy and verification in between. `GET /api-keys` still works with a full-access key, so the "which keys exist and when were they last used" half of the runbook can stay automated.

> **Note:** Manage keys in the dashboard under **API Keys**. Because the full key is shown only once, store it safely at creation time — if you lose it, revoke it and create a new one there.
