# Route Spec

## Route ID
`auth-otp-request`

## Endpoint
`POST /api/v1/auth/otp/request`

## Human Description
Starts phone sign-in by sending a 4-digit OTP code. Used on the "enter phone" step and for resend after 60 seconds.

## Authentication
- Required: `no`
- Auth type: `none`
- Required roles/scopes: `none`

## Request
### Headers
- `Content-Type: application/json`

### Body
```json
{
  "channel": "phone",
  "phoneCountryCode": "+20",
  "phoneNumber": "1001234567",
  "purpose": "login"
}
```

### Validation Rules
- `channel`: required, enum `phone`.
- For `channel=phone`, `phoneCountryCode` and `phoneNumber` are required and E.164-compatible after merge.
- `purpose`: required, enum `login|signup`.

## Responses
### Success: `200 OK`
When returned:
- OTP accepted for delivery.

Body:
```json
{
  "success": true,
  "message": "OTP sent",
  "data": {
    "challengeId": "otp_ch_8f2a1b",
    "otpLength": 4,
    "resendAllowedAt": "2026-02-18T12:34:56Z",
    "expiresAt": "2026-02-18T12:38:56Z",
    "maskedDestination": "+20******567"
  }
}
```

### Error: `422 Unprocessable Entity`
When returned:
- `channel` is missing or unsupported.
- Required phone fields are missing or invalid.
- `purpose` is missing or invalid.

Body:
```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Unsupported channel.",
    "details": {}
  }
}
```

### Error: `429 Too Many Requests`
When returned:
- Resend requested before cooldown or rate limit exceeded.

Body:
```json
{
  "success": false,
  "error": {
    "code": "OTP_RATE_LIMITED",
    "message": "Please wait before requesting another code.",
    "details": {
      "retryAfterSeconds": 37
    }
  }
}
```

## Data & Caching Dependencies
- **Spanner Tables:** `None`
- **Redis Cache:** `otp_challenges (Write)`
- **GCS Storage:** `None`
- **Edge Cache (CDN):** `No`

## Side Effects
- Creates/updates an OTP challenge.
- Triggers SMS provider delivery.

## Idempotency and Retries
- Idempotent: `no`
- Retry guidance: only retry on network failure; UI should enforce cooldown.

## Security and Abuse Controls
- Rate limit: `5 requests / 15 minutes / destination + IP`.
- Sensitive data rules: never return the full phone number.
