# Authentication

JARVIS API endpoints accept two kinds of credential.

| Credential | Header | Works with |
| --- | --- | --- |
| Personal API key | `Authorization: Bearer jrv_…` | Chat, conversations, tasks |
| Browser session | Session cookie | Everything |

## Personal API keys

Create a key on the [Developer page](https://jarvis.spacekeep.dev/developer). The full key is
displayed **once**; only a hash is stored, so it cannot be retrieved again.

```bash
export JARVIS_KEY="jrv_…"

curl -s https://jarvis.spacekeep.dev/api/jarvis/chat \
  -H "Authorization: Bearer $JARVIS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"Hello"}'
```

Key properties:

- Prefix is `jrv_`.
- Revocation takes effect on the key's next request.
- Unknown, revoked, and malformed keys all return the same `401 Invalid API key.` — they cannot
  be distinguished by probing.
- A key grants only what its owner can do. It is not scoped more narrowly than the account is.

## Sessions

A signed-in browser carries a session cookie. Requests made from the JARVIS app itself are
authenticated that way, and no header is needed.

For scripted use, a key is simpler and safer than replaying a cookie.

## Which endpoints accept what

| Endpoint group | Session | API key |
| --- | --- | --- |
| `POST /api/jarvis/chat` | Yes | Yes |
| `/api/conversations…` | Yes | Yes |
| `/api/tasks…` | Yes | Yes |
| `/api/memories…` | Yes | **No** |
| `/api/web` | Yes | **No** |
| `/api/keys…` | Yes | **No** |
| `PATCH /api/auth/profile` | Yes | **No** |
| `POST /api/auth/register`, `/login`, `/forgot`, `/reset` | Not applicable | Not applicable |
| `GET /api/health` | Not needed | Not needed |

Key management and memory are session-only on purpose: they are account administration, and
administering an account from a token stored in a script is not a good default.

## Failure behaviour

| Status | Meaning | What to do |
| --- | --- | --- |
| `401` | Missing, invalid, or revoked credential | Check the header and that the key is not revoked |
| `403` | The account is restricted | See [Global Enforcement](/jarvis/security/global-enforcement/) |
| `404` | No such resource **for this account** | Check the identifier; foreign and missing ids are indistinguishable by design |
| `429` | Rate limit reached | Back off and retry — see [Rate limits](#rate-limits) |
| `503` | Access could not be verified right now | Retry shortly — see [Global Enforcement](/jarvis/security/global-enforcement/) |
| `504` | The reply took too long | Retry; consider a shorter prompt |

> [!TIP]
> An invalid key is always a hard `401`. It is never treated as an anonymous request, so a broken
> key fails loudly instead of silently degrading to unauthenticated behaviour.

## Signing in programmatically

Two endpoints issue a session from credentials. Both are rate limited per client address.

| Method | Path | Body |
| --- | --- | --- |
| `POST` | `/api/auth/register` | `email`, `password`, `confirmPassword`, optional `displayName` |
| `POST` | `/api/auth/login` | `email`, `password` |

Passwords must be 8–128 characters. Registration returns `409` if the address already has an
account.

Successful login sets a session cookie. Send it on subsequent requests, exactly as a browser would.

> [!NOTE]
> Prefer a personal API key over scripting password logins. It avoids handling credentials in your
> integration and survives a password change.

## Password recovery

| Method | Path | Body |
| --- | --- | --- |
| `POST` | `/api/auth/forgot` | `email` |
| `POST` | `/api/auth/reset` | `token`, `password`, `confirmPassword` |

`/api/auth/forgot` returns the same response whether or not the address is registered. A reset
token is valid for 30 minutes and can be redeemed once; redeeming it ends every session on the
account.

## Rate limits {#rate-limits}

| Operation | Limit |
| --- | --- |
| `POST /api/jarvis/chat` | 40 requests per minute |
| Task create / update / delete | 60 requests per minute |
| Memory update / delete | 60 requests per minute |
| API key creation | 10 requests per minute |
| Sign-in attempts | 30 requests per minute per address |
| Registration | 15 requests per minute per address |
| Password reset requests | 3 per 15 minutes per address |

Exceeding a limit returns `429`. Back off and retry rather than retrying immediately.

## Keeping credentials safe

- Read keys from environment variables or a secret manager, never from source.
- Use one key per integration so revocation is surgical.
- Never send a key in a query string.
- JARVIS never returns a key after creation, and never includes keys in a reply.

## Related

- [API Reference](/jarvis/api/reference/) — full endpoint documentation
- [Sessions & API Keys](/jarvis/account/sessions/#api-keys) — key lifecycle
- [Health](/jarvis/api/health/) — the one endpoint that needs no credential
