Request lifecycle
Every checkpoint that runs between a user’s click and your handler returning.
Detailed checkpoint reference
1. Request ID
Generated from X-Request-Id (preferred) or fresh ULID. Propagated to:
c.var.requestId- OTel trace span
- Structured logs
- Audit row
2. Logger context
Logger is enriched with: service, route, request_id, client_id (post-auth), brand_id (post-brand-resolve), user_id (post-introspection).
3. CORS
For browser-originated requests, validate Origin against the OAuth client’s allowed_origins list. Reject if mismatch.
4. Rate limit
Sliding window in Redis. Key: (client_id, route-class). Returns 429 + Retry-After if cap exceeded. See Rate limits and circuit breakers.
5. Token introspection
Calls services/identity/v1/oauth/introspect. Result cached in Redis for min(token_exp, 60s). Returns 401 if token invalid or expired.
6. requireScope
Checks the route’s required scopes against auth.scopes. Returns 403 + WWW-Authenticate: Bearer scope="required:scope" if insufficient.
7. Brand-scope resolution
Reads brand_id from the access token (preferred) or X-Brand-Id header (M2M). Validates against platform.brands. Attaches to context.
8. Audit row
After the handler returns, the audit middleware writes a row. If the handler didn’t c.set("audit_event_type", ...) AND the request was a mutation, the convention check audit-required would fail CI — so this should always be populated.
9. Response headers
X-Request-Idechoed backX-RateLimit-Remaining,X-RateLimit-Reset(from rate-limit middleware)- CORS headers if applicable
Cache-Controlper route
10. Structured log
Final log line: { request_id, route, method, status, duration_ms, client_id, user_id?, brand_id }. PHI never appears here — safeView() redacts anything sensitive before logging.
What can fail at each checkpoint
| Checkpoint | Failure mode | Response |
|---|---|---|
| 4 (rate limit) | Over cap | 429 |
| 5 (introspection) | Invalid / expired token | 401 |
| 5 (introspection) | identity service down | 503 (fail-closed) |
| 6 (scope) | Missing scope | 403 |
| 7 (brand) | Unknown brand_id | 400 |
| 7 (brand) | brand_scope_violation | 403 |
| Handler | Validation error | 400 |
| Handler | Not found | 404 |
| Handler | DB error | 500 |
| 8 (audit) | Audit write fails | Request rolled back (audit + mutation in same tx) |
What never happens
- A 200 response without an audit row for a mutation.
- A 200 response with a
brand_idmismatch between token and rows. - A 200 response containing PHI fields if the token’s scopes don’t include them.
- A response that omits
X-Request-Id— required for cross-service correlation.