# Errors and limits

## Error format

Every API error is a machine code plus one plain sentence.

```json
{
  "error": "invalid_credentials",
  "message": "Username or password is incorrect. Next attempt in 4 seconds.",
  "retry_after": 4
}
```

`retry_after` is present when the caller should back off. The same value is
sent in the `Retry-After` header.

## Error codes

| code | HTTP | meaning |
|---|---|---|
| `invalid_request` | 400 | a required field is missing or malformed |
| `invalid_client` | 401 | unknown app, disabled app, or bad app secret |
| `invalid_credentials` | 401 | wrong username or password |
| `invalid_grant` | 400 | code invalid, expired or used, or the user is inactive |
| `invalid_token` | 401 | missing, expired or revoked token |
| `too_many_attempts` | 429 | in cooldown, honour `retry_after` |
| `weak_password` | 400 | below the minimum length |
| `forbidden` | 403 | the token cannot do that |
| `not_found` | 404 | no such object in your scope |
| `conflict` | 409 | the username is already taken |
| `unsupported_grant_type` | 400 | unknown `grant_type` |
| `bad_origin` | 403 | the request came from an unexpected origin |
| `server_error` | 500 | unexpected failure |

## Password rules

- Minimum length is 10 characters.
- Usernames are 3-32 characters: letters, digits, dot, underscore or hyphen.
- App usernames follow the same rule and are unique within an app.

## Client account sign-ins

Client sign-ins are strictly rate-limited. The wait doubles after each failed
attempt: 1, 2, 4, 8, 16, 32, 64 seconds and so on. There is no practical cap.

If a client account is locked out, contact your administrator to clear it.

## App user sign-ins

App user sign-ins are lenient by default. The first few failures are free, then
the wait doubles up to the app's cap.

| field | default | bounds |
|---|---|---|
| `start_after` | 5 | 0-20 |
| `cap` | 60 | 5-300 seconds |
| `burst_gap` | 300 | 60-3600 seconds |

You can tune these per app with `PATCH /manage/settings`.

## Retry behaviour

- Read `retry_after` or the `Retry-After` header.
- Do not retry a 4xx blindly. Fix the request first.
- Retry a 5xx with exponential backoff and jitter.
- Treat `invalid_token` as a signal to re-authenticate, not to retry the same call.

## Content types

The token and management endpoints accept `application/json` or form-encoded
bodies. Send `Content-Type: application/json` when you send JSON.

## Request size

JSON and form bodies are read as a flat dictionary. Send one object per
request. Do not send nested arrays where a string is expected.
