# Account security

> Two-factor authentication, sessions and device alerts for dashboard users; scopes, IP allowlists, expiry and rotation for API keys.

## Email verification

Every account confirms its email address with a link that is valid for 24 hours. Unverified accounts can use the sandbox freely, but creating a live key or submitting a go-live application fails with `403 email_unverified` until the address is confirmed. Resend the link from the banner in the dashboard.

## Two-factor authentication (TOTP)

Enable it under **Settings → Security**. Scan the QR code with any authenticator app (1Password, Google Authenticator, Authy, Microsoft Authenticator or anything implementing RFC 6238), confirm a code, and save the ten backup codes shown once. Each backup code signs you in one time; you can generate a new set with your password.

With two-factor on, sign-in is two steps: password, then `POST /auth/mfa/verify` with the 6-digit code (or a backup code). Five wrong codes end the attempt. Disabling needs both your password and a current code.

Accounts **without** an authenticator app get a 6-digit code by email when signing in from a browser Speedmap has not seen before; it expires in 10 minutes.

### Requiring it for a workspace

Owners and admins can switch on **Require two-factor authentication** under **Settings → Organization**. Members who have not enabled it can still sign in and read, but every write in the dashboard answers `403 mfa_required_by_org` until they do. Whoever turns the policy on must already have it enabled.

## Sessions and devices

**Settings → Security** lists every active session with its browser, IP address and last activity. Sign out a single session, or all other sessions at once. Changing your password (or resetting it by email) signs out every other session. You are emailed when a new device signs in, when your password changes, and when an API key is created or revoked in a workspace you manage.

## API key scopes, allowlists and expiry

Keys are created in **API keys** with least privilege:

| Setting | Effect | Error when violated |
| --- | --- | --- |
| Scopes | All products (`*`), a list of product ids, or `read` (list and fetch only, no new checks). | `403 key_scope` |
| IP allowlist | IPv4/IPv6 addresses or CIDR blocks the key may be used from. Empty allows any address. | `403 ip_not_allowed` |
| Expiry | An optional date after which the key stops working. | `401 key_expired` |

Every key records when and from which IP it was last used, visible in the dashboard.

### Rotation

**Rotate** issues a replacement with the same name, mode, scopes, allowlist and expiry, and keeps the old key working for a grace period (default 60 minutes, from immediately to 24 hours) so you can deploy the new secret without downtime. The old key shows as *retiring* until it is revoked automatically. If a key may have leaked, rotate with no grace period or revoke it outright.

### Handling these errors in code

Treat `401 key_expired`, `403 key_scope` and `403 ip_not_allowed` as configuration errors: alert, do not retry. They never create a verification and are never billed.

**403 key_scope**

```json
{
  "error": {
    "code": "key_scope",
    "message": "This key is not scoped to bvn",
    "details": {
      "scopes": [
        "nin",
        "account"
      ]
    }
  }
}
```

> **Roles:** Dashboard access is role-based: owner, admin, developer, analyst, billing and viewer. The matrix is enforced by the API on every request and shown under **Team**. Only an owner can transfer ownership, which needs their password.

---

Source: https://speedmap-delta.vercel.app/docs/security
