QAPI está en beta — la API puede cambiar sin previo aviso. Usa sandbox para pruebas.
QAPIQAPIDocs · by DTEVIA
Empezar

Autenticación

API keys (qapi_test_ / qapi_live_), JWT, sistema dual-auth y scopes de permisos.

Autenticación

QAPI soporta dos mecanismos de autenticación — API keys para integraciones de backend y JWT para sesiones de usuario desde el dashboard.

API Keys

Las API keys son la forma recomendada para integrar QAPI en tu software. Cada key pertenece a un proyecto y su entorno lo define el proyecto. Tu Proyecto Sandbox y su key de prueba se crean automáticamente al registrar tu cuenta.

Formato

PrefijoEntornoDescripción
qapi_test_...TestKey del Proyecto Sandbox. Solo certificador MOCK. No consume cuota. No toca la SAT.
qapi_live_...LiveKey de un proyecto live. Usa el certificador real. Consume la cuota del proyecto. Factura real ante la SAT.

La API key es recuperable

No la pierdes si no la copiaste al crearla: podés volver a ver y copiar la key completa desde el dashboard, dentro del proyecto (ícono del ojo). Cada proyecto tiene una key activa — para rotarla, regenerala (eso revoca la anterior). Cada vez que se revela, queda registrado en el log de auditoría de tu cuenta.

Uso en requests

Envía la API key en el header Authorization con el prefijo Bearer:

Authorization: Bearer qapi_test_abc123xyz...

Acceso de la key (scopes)

Al crear una key elegís su nivel de acceso, y el API lo hace cumplir:

AccesoScopesPermite
Completoinvoices:write + invoices:readEmitir, anular y consultar facturas
Solo lecturainvoices:readSolo consultar (GET) — no puede emitir ni anular

Una key solo lectura que intente emitir (POST /v1/invoices) o anular (POST /v1/invoices/:id/cancel) recibe 403 FORBIDDEN indicando el scope que le falta. Útil, por ejemplo, para una key de un panel de monitoreo que solo lee y nunca debe facturar.

Cómo elegirlo

Lo elegís al generar la key en el dashboard (dentro de cada proyecto). La key del Sandbox y la inicial se crean con acceso completo. Al regenerar una key se conserva su mismo acceso.


JWT (JSON Web Tokens)

Los JWT autentican sesiones de usuario desde el navegador (el dashboard y el playground en /v1/dashboard/* y /v1/sandbox/*). Es el mecanismo que usa la propia app web de QAPI.

Para integrar, usá API keys — no JWT

Si estás conectando tu software a QAPI (servidor a servidor), usá una API key: es un solo header, sin expiración corta ni cookies. El flujo JWT de abajo está pensado para el dashboard en el navegador (incluye una cookie HttpOnly y protección CSRF que un cliente de servidor no necesita manejar).

Obtener tokens

POST /v1/auth/login
Content-Type: application/json

{
  "email": "tu@email.com",
  "password": "tupassword"
}

Respuesta:

{
  "access_token": "eyJhbGci...",
  "user": { "id": "...", "email": "...", "nombre": "...", "role": "owner", "mfa_enabled": false },
  "tenant": { "id": "...", "nombre": "...", "plan": "starter", "certificador": "MOCK" }
}

El refresh_token no viene en el body: QAPI lo setea en una cookie HttpOnly (qapi_rt) inaccesible a JavaScript. El cliente solo guarda el access_token.

Login con verificación en dos pasos (MFA)

Si la cuenta tiene MFA activo (obligatorio para superadmin), /v1/auth/login no devuelve los tokens: responde { "mfa_required": true, "mfa_token": "..." } (o mfa_enrollment_required si es un superadmin que aún no lo configuró). Completá el login enviando el mfa_token y el código TOTP (o un backup code) a POST /v1/auth/mfa/verify, que recién ahí devuelve access_token + user + tenant y setea la cookie del refresh.

Ciclo de vida

TokenDuraciónDónde vive
access_token15 minutosEl cliente lo guarda y lo manda como Bearer en cada request
refresh_token7 díasCookie HttpOnly qapi_rt que setea el backend (el cliente no la maneja)

Renovar el access token

El access token es corto (15 min). Para renovarlo, el navegador llama a POST /v1/auth/refresh: el refresh viaja solo en la cookie qapi_rt (no en el body). Como la cookie se adjunta sola, el endpoint exige protección CSRF double-submit: enviá el header X-CSRF-Token con el valor de la cookie no-HttpOnly qapi_csrf (la setea el backend junto al refresh).

Rotación de refresh tokens

Cada vez que renuevas, el refresh anterior queda revocado y se rota la cookie. Si se reusa un refresh ya rotado, QAPI detecta la reutilización y revoca todas las sesiones del usuario.

POST /v1/auth/refresh
X-CSRF-Token: <valor de la cookie qapi_csrf>
# sin body — el refresh viaja en la cookie HttpOnly qapi_rt

# Respuesta: { "access_token": "eyJhbGci..." }  (y rota la cookie qapi_rt)

El logout (POST /v1/auth/logout) funciona igual: lee la cookie y exige el mismo header X-CSRF-Token.


Dual-Auth: cómo QAPI detecta el método

QAPI detecta automáticamente el tipo de credencial según el prefijo del header Authorization:

Valor del headerMecanismo activado
Bearer qapi_test_... o Bearer qapi_live_...API Key
Bearer eyJ... (comienza con eyJ)JWT

No hay un endpoint diferente — el mismo header, dos caminos de validación.


Rate limits de autenticación

Prop

Type


Ejemplos de autenticación

curl -X GET https://api.dtevia.com.gt/v1/invoices \
  -H "Authorization: Bearer qapi_test_TU_API_KEY"
# 1. Login
TOKEN=$(curl -s -X POST https://api.dtevia.com.gt/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"tu@email.com","password":"tupassword"}' \
  | jq -r '.access_token')

# 2. Usar el token
curl -X GET https://api.dtevia.com.gt/v1/dashboard/api-keys \
  -H "Authorization: Bearer $TOKEN"
const BASE = 'https://api.dtevia.com.gt/v1';
const API_KEY = 'qapi_test_TU_API_KEY';

const res = await fetch(`${BASE}/invoices`, {
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
});

const data = await res.json();
import requests

BASE = 'https://api.dtevia.com.gt/v1'
API_KEY = 'qapi_test_TU_API_KEY'

headers = {
    'Authorization': f'Bearer {API_KEY}',
    'Content-Type': 'application/json',
}

response = requests.get(f'{BASE}/invoices', headers=headers)
data = response.json()

On this page