Iniciar sesión
Autentica con email y contraseña. Devuelve el `access_token` (JWT) junto con el perfil del usuario y del tenant. El `refresh_token` NO viene en el body: QAPI lo setea en una cookie HttpOnly `qapi_rt` (inaccesible a JavaScript). 💡 **Guardá el `access_token`**: es el que vas a usar como `Authorization: Bearer <token>` para probar los endpoints de **Sandbox** desde esta misma documentación. Dura **15 minutos**; renovalo con `POST /v1/auth/refresh` (que usa la cookie). 🔐 **MFA**: si la cuenta tiene verificación en dos pasos activa (obligatoria para superadmin), este endpoint NO devuelve tokens — responde `{ "mfa_required": true, "mfa_token": "..." }` (o `mfa_enrollment_required` si el superadmin aún no lo configuró). Completá el login con `POST /v1/auth/mfa/verify`. > Para **integrar tu software** con QAPI usá una **API key**, no este flujo JWT (pensado para el dashboard en el navegador). Protegido contra fuerza bruta: máximo 10 intentos por email cada 15 minutos.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/auth/login" \ -H "Content-Type: application/json" \ -d '{ "email": "dev@miempresa.com", "password": "S3cret!" }'{
"access_token": "string",
"user": {
"id": "string",
"email": "user@example.com",
"nombre": "string",
"role": "owner",
"tenant_id": "string"
},
"tenant": {
"id": "string",
"nombre": "string",
"nit_emisor": "12345678",
"plan": "starter"
}
}{
"type": "https://docs.dtevia.com.gt/errors/validation-error",
"title": "VALIDATION_ERROR",
"status": 422,
"detail": "Error de validación",
"instance": "/v1/invoices",
"invalid_params": [
{
"field": "items.0.impuestos.0.monto_impuesto",
"message": "MontoImpuesto IVA inválido. Esperado ~12 (MontoGravable 100 × 0.12), recibido 10"
}
]
}{
"type": "https://docs.dtevia.com.gt/errors/validation-error",
"title": "VALIDATION_ERROR",
"status": 422,
"detail": "Error de validación",
"instance": "/v1/invoices",
"invalid_params": [
{
"field": "items.0.impuestos.0.monto_impuesto",
"message": "MontoImpuesto IVA inválido. Esperado ~12 (MontoGravable 100 × 0.12), recibido 10"
}
]
}{
"type": "https://docs.dtevia.com.gt/errors/validation-error",
"title": "VALIDATION_ERROR",
"status": 422,
"detail": "Error de validación",
"instance": "/v1/invoices",
"invalid_params": [
{
"field": "items.0.impuestos.0.monto_impuesto",
"message": "MontoImpuesto IVA inválido. Esperado ~12 (MontoGravable 100 × 0.12), recibido 10"
}
]
}Estados de Factura
Ciclo de vida completo de una factura QAPI — de PENDING a CERTIFIED, FAILED o CANCELLED.
Refrescar tokens
Renueva el access token usando el refresh token de la cookie HttpOnly `qapi_rt` (no se envía en el body). Devuelve un nuevo access token y **rota la cookie** del refresh. 🛡️ **CSRF**: como la cookie se adjunta automáticamente, este endpoint exige protección double-submit. Enviá el header `X-CSRF-Token` con el valor de la cookie no-HttpOnly `qapi_csrf`. ⚠️ **Rotación**: el refresh anterior queda revocado. Si se reutiliza un refresh ya rotado, QAPI lo detecta como posible robo y **revoca todas las sesiones del usuario**.