Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CodexaCP/SERVICIOS-BACK/llms.txt

Use this file to discover all available pages before exploring further.

ServiciosYa provides three password-management endpoints. Authenticated users change their password directly through POST /api/auth/change-password. Users who have forgotten their credentials follow a two-step OTP flow: they request a one-time code via POST /api/auth/forgot-password and then submit that code alongside their new password via POST /api/auth/reset-password. All three endpoints enforce the same password policy before persisting any changes.

Password policy

All new passwords must satisfy every one of the following rules enforced by PasswordPolicy.IsValid:
RuleRequirement
Minimum lengthAt least 8 characters
UppercaseAt least 1 uppercase letter
LowercaseAt least 1 lowercase letter
DigitAt least 1 numeric digit
Special characterAt least 1 character that is not a letter or digit
Validation error message (returned verbatim):
“La contraseña debe tener mínimo 8 caracteres, una mayúscula, una minúscula, un número y un carácter especial.”

POST /api/auth/change-password

MethodURL
POST/api/auth/change-password
POST/api/v1/auth/change-password
Authentication: Bearer token required. Any authenticated role. Allows an authenticated user to change their own password by providing their current password alongside the desired new password. The server validates the new password against the policy before delegating the hash comparison and update to the database via the ChangeUserPassword stored procedure. On success, the MustChangePassword flag is cleared in the database, granting full access.
Every account created through POST /api/auth/register or POST /api/auth/register/internal is issued the default password 123456 with mustChangePassword = true. This password must be changed before the account is used in any environment. Failing to do so leaves the account exposed to trivial credential-guessing attacks.

Request body

currentPassword
string
required
The user’s current password. Must match the hash stored in the database; the stored procedure will throw if it does not match.
newPassword
string
required
The desired new password. Must pass the password policy checked by the API before the database call is made.

Response fields

message
string
"Password actualizado" on success.

Example request

curl -X POST https://localhost:7177/api/auth/change-password \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <token>' \
  -d '{
    "currentPassword": "123456",
    "newPassword": "NuevaC0ntraseña!"
  }'

Example response

{
  "message": "Password actualizado"
}

Error responses

HTTP statusBodyCause
400 Bad Request{"error": "La contraseña debe tener mínimo 8 caracteres..."}New password does not meet the policy
401 UnauthorizedMissing or invalid Bearer token
500 / DB errorSQL-thrown messageCurrent password does not match ("Password actual inválido.")
An audit event with Action = "CHANGE_PASSWORD" and Result = "Success" is written on every successful call.

POST /api/auth/forgot-password

MethodURL
POST/api/auth/forgot-password
POST/api/v1/auth/forgot-password
Authentication: None — public endpoint. Initiates the OTP-based password reset flow. When the submitted email matches an active account, the API generates a numeric OTP code, hashes it with SHA-256, stores the hash with an expiry timestamp, and dispatches the plain-text code to the user via the configured notification service (email). The endpoint always returns 200 OK regardless of whether the email address exists in the system. This design prevents user enumeration attacks — callers cannot determine whether an account exists by observing the response.

Rate limiting

The API enforces a rate limit per email address: a maximum of 3 requests are allowed within any rolling 15-minute window. Requests that exceed this limit are silently dropped (the response is still 200 OK).

OTP details

PropertyDefault valueConfiguration key
Digit count6 (range: 6–8)PasswordReset:OtpDigits
Expiry10 minutesPasswordReset:ExpirationMinutes (clamped to 10–15)
Max requests per window3PasswordReset:MaxRequestsPerWindow
Window duration15 minutesPasswordReset:RequestWindowMinutes

Request body

email
string
required
Email address of the account for which the password reset is requested. Must not be blank; returns 400 if empty.
companyId
integer
Company scope for the lookup. Defaults to 1 (ServiciosYa base company) when the value is 0 or negative.

Response fields

message
string
Always "Si el correo existe, te enviaremos instrucciones para restablecer tu contrasena." — the message is the same whether or not the account was found.

Example request

curl -X POST https://localhost:7177/api/auth/forgot-password \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "maria.perez@example.com",
    "companyId": 1
  }'

Example response

{
  "message": "Si el correo existe, te enviaremos instrucciones para restablecer tu contrasena."
}

Error responses

HTTP statusBodyCause
400 Bad Request{"error": "El correo es obligatorio."}email field is blank or missing
The OTP code is transmitted to the user’s email in plain text. The API stores only the SHA-256 hash — the plain text is never persisted. When the user submits the code to /api/auth/reset-password, the API hashes the submitted value before comparing it to the stored hash.

POST /api/auth/reset-password

MethodURL
POST/api/auth/reset-password
POST/api/v1/auth/reset-password
Authentication: None — public endpoint. Completes the OTP-based password reset flow. The caller must provide the email address, the OTP code received from the notification, and the desired new password. The API hashes the submitted otpCode with SHA-256 and compares it against the stored hash. Each OTP record allows a maximum of 5 failed attempts before it is permanently invalidated, even if it has not yet expired. This prevents brute-forcing of the 6-digit space.

Request body

email
string
required
Email address of the account being reset.
companyId
integer
Company scope for the lookup. Defaults to 1 when 0 or negative.
otpCode
string
required
The plain-text OTP code received in the reset email (e.g. "482910"). Must not be blank.
newPassword
string
required
The new password. Must pass the password policy before the OTP is validated.

Response fields

message
string
"Tu contrasena fue actualizada." on success.

Example request

curl -X POST https://localhost:7177/api/auth/reset-password \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "maria.perez@example.com",
    "companyId": 1,
    "otpCode": "482910",
    "newPassword": "NuevaC0ntraseña!"
  }'

Example response

{
  "message": "Tu contrasena fue actualizada."
}

Error responses

HTTP statusBodyCause
400 Bad Request{"error": "Correo, codigo y nueva contrasena son obligatorios."}One or more required fields are blank
400 Bad Request{"error": "La contraseña debe tener mínimo 8 caracteres..."}New password fails the policy check
400 / DB errorSQL-thrown messageOTP expired, maximum attempts exceeded, or OTP not found
Direct users to request a new OTP via /api/auth/forgot-password if they receive an error indicating the code has expired or the maximum attempts have been reached. The previous OTP record is invalidated and a fresh one is issued.

Build docs developers (and LLMs) love