### Sign a manager in

`POST /api/v1/auth/login`

Exchanges an email or username and a password for a tenant-scoped session token. The tenant is taken from the host when you call a tenant subdomain, and a selector in the body is discarded in that case.

- Authentication: none
- Rate limit bucket: `login`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `email` | body | string | yes | Email or username. |
| `password` | body | string | yes | The manager's password. |
| `totp_code` | body | string | no | Six-digit code or a recovery code. Sent on the second attempt, after the first returns mfa_required. |
| `tenant_slug` | body | string | no | Honoured only when the host carries no tenant. Ignored outright on a tenant subdomain. |
| `refresh` | body | boolean | no | Opt in to the refresh-token flow. Omitting it returns one long-lived token and no refresh fields at all. |

#### Request

```json
{
  "email": "ops@acme.example",
  "password": "a-strong-password"
}
```

#### Response — 200 OK

```json
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "user_id": 41,
    "tenant_id": 12,
    "tenant_slug": "acme",
    "roles": ["support"],
    "expires_at": 1758358800
  }
}
```

#### Errors

| Code | Status | When |
| --- | --- | --- |
| `ERR_VALIDATION` | 400 | the host carries no tenant and no selector was supplied — details.reason tenant_required |
| `ERR_UNAUTHORIZED` | 401 | wrong credentials (invalid_credentials), two-factor needed (mfa_required), or a bad code (mfa_invalid) |
| `ERR_RATE_LIMITED` | 429 | too many consecutive failures on this account — details.reason login_locked |
| `ERR_UNAVAILABLE` | 503 | the tenant's portal has been switched off — details.reason site_offline |

#### Note

mfa_required is a 401 with the same code as a wrong password. Branch on details.reason, never on the status or the message: six different login outcomes share ERR_UNAUTHORIZED and only the reason tells them apart. expires_at is unix SECONDS, not milliseconds and not a formatted timestamp.

