Error handling
GlobalExceptionHandler returns a JSON body with a stable code and traceId. Status codes follow the backend rule: 201 create, 204 delete, 401 and 403 auth, 409 conflict. Validation errors are 400.
Do not return stack traces or filesystem paths. Do not put OTP, refresh tokens, or presigned URL query strings into logs or error bodies.
Use cases throw domain exceptions. Controllers do not catch them one by one unless a single endpoint needs a different status.
External calls use timeouts. Communication-service adds a Resilience4j circuit breaker per email provider so one vendor does not stall OTP for everyone once the breaker is open. The user still needs a clear error, not a hung request.