Reset password
AUTHENTICATED BY X-API-Key, not by a bearer token. auth.APIKeyMiddleware REPLACES the usual chain on this route, so there is no user context, no RBAC check and no licence check — and the refusals are common.api_key_required / common.invalid_api_key rather than common.unauthorized / common.rbac_no_rec_access. An OPTIONS request skips the key check entirely. Complete a password reset using the token from the reset email. Subject to IP rate limiting (max attempts per 15 minutes; invalid tokens increment the failed-attempt window per hour). Cache errors fail closed with 503.
Authorization
apiKeyAuth Service API key for the eight pre-authentication user routes. auth.APIKeyMiddleware REPLACES the bearer chain on those routes rather than wrapping it: there is no user context, no RBAC check and no licence check on them.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
CompletePasswordResetInput. There is no OTP on this route: the caller arrives with the token from the reset link. verification_token is carried alongside it and is marked in the Go struct as temporary.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/users/reset-password" \ -H "Content-Type: application/json" \ -d '{ "new_password": "string" }'{
"status": 200,
"message": "OK"
}{
"status": 400,
"message": "Invalid user input",
"code": "users_m.invalid_user_input",
"class": "validation"
}{
"status": 401,
"message": "common.api_key_required",
"code": "common.api_key_required",
"class": "business"
}{
"status": 403,
"message": "common.invalid_api_key",
"code": "common.invalid_api_key",
"class": "business"
}{
"status": 409,
"message": "Credential already exists",
"code": "users_m.duplicate_credential",
"class": "business"
}{
"status": 429,
"message": "Too many password reset attempts. Please try again in 842 seconds",
"code": "users_m.password_reset_rate_limit_exceeded",
"class": "temporary",
"retryable": true
}{
"status": 500,
"message": "Internal server error",
"code": "common.server_error",
"class": "business"
}{
"overall_status": "unhealthy",
"message": "Service is shutting down",
"timestamp": "2026-08-27T15:04:05Z"
}AUTHENTICATED BY X-API-Key, not by a bearer token. auth.APIKeyMiddleware REPLACES the usual chain on this route, so there is no user context, no RBAC check and no licence check — and the refusals are common.api_key_required / common.invalid_api_key rather than common.unauthorized / common.rbac_no_rec_access. An OPTIONS request skips the key check entirely. Request a password reset link for an email or phone credential. Subject to IP rate limiting (max attempts per 15 minutes; failed attempts tracked per hour). Always returns success when the credential is unknown to prevent enumeration.
Change user password Note {id} is NOT validated here: ChangePassword calls uuid.MustParse on it, so a malformed id panics and middleware.Recoverer answers a bare 500 with an EMPTY body rather than the Error envelope documented below.