🔌 Introducción y Autenticación
Los endpoints son las URL que se utilizan para acceder a los recursos de la API. Cada endpoint es un punto de acceso que devuelve datos o realiza operaciones en el servidor.
📮 Ver Colección Oficial de Postman
Sin autenticación
Requieren token
CRUD y consultas
v3.0.0
🎯 Flujo Completo de una Factura Electrónica
1. AUTENTICACIÓN 2. CONSULTA 3. ENVÍO 4. ESTADO
/auth/login /document-type /invoice /status
↓ ↓ ↓ ↓
[Token] [Tabla DIAN] [Factura JSON] [Validado]
↓ ↓ ↓ ↓
Públicos Públicos Privados Privados
🏷️ Tipos de Endpoints
| Tipo | Autenticación | Uso | Headers |
|---|---|---|---|
| 🟢 Públicos | ❌ No | Tablas DIAN, autenticación | Ninguno |
| 🔐 Privados | ✅ Sí | Documentos, estado, eventos | Authorization: Bearer {token} |
Los endpoints privados requieren el header Authorization: Bearer {token} en todas las solicitudes.
🔑 Autenticación
Las cuentas principales creadas con POST /register quedan con el correo sin verificar. Hasta abrir el enlace del correo de verificación, POST /auth/login responde HTTP 404 con el mensaje El correo electrónico no ha sido verificado. Comuníquese con el administrador. Si el correo no llegó o el enlace venció, pide uno nuevo con POST /email/verification-notification. Las sub-cuentas que una Casa de Software crea con POST /company/{uuid}/customer ya quedan verificadas.
POST /auth/login — Iniciar Sesión
POST {{url}}/auth/login
Content-Type: application/json
Accept: application/json
Parámetros del Body:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
email | string | ✅ Sí | Correo electrónico registrado (formato de correo válido) |
password | string | ✅ Sí | Contraseña de la cuenta |
remember_me | boolean | No | Opcional. Se acepta por compatibilidad, pero no cambia la vigencia del token |
- cURL
- JavaScript (Axios)
- PHP (Guzzle)
curl -X POST "{{url}}/auth/login" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"email": "usuario@miempresa.com",
"password": "MiPassword123!"
}'
const response = await axios.post(`${url}/auth/login`, {
email: 'usuario@miempresa.com',
password: 'MiPassword123!'
}, {
headers: { Accept: 'application/json' }
});
const token = response.data.access_token;
$response = $client->post("{$url}/auth/login", [
'headers' => ['Accept' => 'application/json'],
'json' => [
'email' => 'usuario@miempresa.com',
'password' => 'MiPassword123!',
],
]);
$token = json_decode($response->getBody())->access_token;
✅ Respuesta Exitosa (HTTP 200)
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImYyZTFlYjgyZDUyMjEz...",
"token_id": "f2e1eb82d52213a6b1c0d3e4f5a60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9",
"user": {
"id": 1,
"type_id": 2,
"first_name": "JUAN",
"last_name": "PEREZ",
"email": "usuario@miempresa.com",
"avatar": null,
"active": 1,
"name": "JUAN PEREZ",
"avatarUrl": null,
"user_type": {
"id": 2,
"user_type_name": "DESARROLLADOR",
"type": 2,
"active": 1
}
},
"expires_at": "2026-05-15 20:00:00",
"message": "Bienvenido. Su sesión ha sido iniciada con éxito.",
"success": true
}
access_token: el token que se envía enAuthorization: Bearer {token}.token_id: identificador del token (el mismoidque aparece enGET /tokens).expires_at: fecha de vencimiento (YYYY-MM-DD HH:MM:SS). Por defecto, 90 días después del login.- La respuesta no incluye
token_typeni roles: el tipo de usuario está enuser.type_idyuser.user_type(verGET /profile/types).
❌ Inicio de Sesión Rechazado (HTTP 404)
Todos los rechazos del login responden HTTP 404 (no 401) con el mismo formato. Se comprueban en el orden de la tabla:
{
"message": "Credenciales inválidas.",
"success": false
}
message | Causa |
|---|---|
Credenciales inválidas. | El correo o la contraseña no coinciden. |
El usuario se encuentra inactivo. Comuníquese con el administrador. | El usuario está inactivo. |
El correo electrónico no ha sido verificado. Comuníquese con el administrador. | Cuenta de POST /register sin verificar, o correo cambiado con PUT /profile/update-email y aún sin verificar. Ver verificación de correo. |
No se ha encontrado el perfil de usuario. | El usuario no está asociado a ninguna empresa. |
No se ha encontrado la empresa asociada al usuario. | La empresa del usuario no existe. |
La empresa se encuentra inactiva. Comuníquese con el administrador. | La empresa está inactiva. |
❌ Error de Validación (HTTP 422)
Falta email o password, o el correo no tiene un formato válido. Envía Accept: application/json para recibir el error en JSON (sin esa cabecera, la respuesta es una redirección HTTP 302).
{
"message": "El campo email es obligatorio.",
"errors": {
"email": [
"El campo email es obligatorio."
]
}
}
GET /auth/user — Obtener Usuario Actual
GET {{url}}/auth/user
Authorization: Bearer {token}
Accept: application/json
Devuelve el usuario dueño del token. Es equivalente a GET /profile.
✅ Respuesta Exitosa (HTTP 200)
{
"dataRecords": {
"data": [
{
"id": 1,
"type_id": 2,
"first_name": "JUAN",
"last_name": "PEREZ",
"email": "usuario@miempresa.com",
"avatar": null,
"active": 1,
"name": "JUAN PEREZ",
"avatarUrl": null,
"user_type": {
"id": 2,
"user_type_name": "DESARROLLADOR",
"type": 2,
"active": 1
}
}
]
},
"success": true
}
❌ No Autenticado (HTTP 401)
Sin token, o con un token inválido, vencido o revocado. Este formato solo se recibe si la petición envía Accept: application/json; sin esa cabecera, la respuesta es una redirección (HTTP 302).
{
"message": "Unauthenticated."
}
GET /auth/logout — Cerrar Sesión
GET {{url}}/auth/logout
Authorization: Bearer {token}
Accept: application/json
Revoca el token con el que se hace la petición y cierra la sesión asociada. Los demás tokens del usuario siguen vigentes (para revocarlos, usa POST /tokens/revoke-all).
✅ Respuesta Exitosa (HTTP 200)
{
"message": "Successfully logged out",
"success": true
}
❌ No Autenticado (HTTP 401)
Sin token, o con un token inválido o ya revocado. Requiere Accept: application/json; sin esa cabecera, la respuesta es una redirección (HTTP 302).
{
"message": "Unauthenticated."
}
📧 Verificación de Correo
Al registrarse con POST /register, la API envía un correo con un enlace de verificación firmado y con vencimiento. Hay que abrirlo antes de iniciar sesión. Estos endpoints son públicos: no llevan token.
POST /email/verification-notification — Reenviar Correo de Verificación
POST {{url}}/email/verification-notification
Content-Type: application/json
Accept: application/json
Envía de nuevo el correo de verificación. Úsalo cuando el correo no llegó o cuando el enlace venció.
Parámetros del Body:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
email | string | ✅ Sí | Correo de la cuenta pendiente de verificar |
- cURL
- JavaScript (Axios)
- PHP (Guzzle)
curl -X POST "{{url}}/email/verification-notification" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email": "usuario@miempresa.com"}'
await axios.post(`${url}/email/verification-notification`, {
email: 'usuario@miempresa.com'
}, {
headers: { Accept: 'application/json' }
});
$response = $client->post("{$url}/email/verification-notification", [
'headers' => ['Accept' => 'application/json'],
'json' => ['email' => 'usuario@miempresa.com'],
]);
✅ Correo Enviado (HTTP 200)
{
"message": "Se ha enviado un correo electrónico de verificación"
}
❌ Correo no Registrado (HTTP 404)
También se recibe si no se envía email.
{
"message": "El usuario no existe.",
"success": false
}
❌ Correo ya Verificado (HTTP 400)
{
"message": "El correo electrónico ya fue verificado.",
"success": false
}
GET Enlace de verificación — Confirmar el Correo
El correo contiene un enlace con esta forma. Lo genera la API: no se construye a mano.
GET {{url}}/verify-email/{id}/{hash}?expires={timestamp}&signature={firma}
| Resultado | Comportamiento |
|---|---|
| Enlace válido | Marca el correo como verificado, activa el usuario y redirige al portal web, a la pantalla de inicio de sesión. Si el correo ya estaba verificado, también redirige al login. |
| Enlace vencido o alterado | HTTP 403 (Invalid signature.). Pide un enlace nuevo con POST /email/verification-notification. |
| Usuario inexistente | HTTP 404. |
Se admiten hasta 6 intentos por minuto sobre este enlace.
🔁 Recuperar Contraseña
Endpoints públicos (sin token) para restablecer la contraseña por correo.
POST /forgot-password — Solicitar Enlace de Restablecimiento
POST {{url}}/forgot-password
Content-Type: application/json
Accept: application/json
Parámetros del Body:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
email | string | ✅ Sí | Correo de la cuenta (formato válido) |
Envía al correo un enlace con el token de restablecimiento. El enlace vence a los 60 minutos.
curl -X POST "{{url}}/forgot-password" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email": "usuario@miempresa.com"}'
✅ Enlace Enviado (HTTP 200)
{
"success": true,
"message": "¡Recordatorio de contraseña enviado!"
}
El correo contiene el enlace con el token que se usa en POST /reset-password.
❌ No se Pudo Enviar (HTTP 422)
El correo no está registrado o ya se pidió un enlace hace muy poco (en ese caso, Por favor espere antes de volver a intentarlo.). Si falta email o no tiene formato válido, el error es el de validación (El campo email es obligatorio.).
{
"message": "No se ha encontrado un usuario con esa dirección de correo.",
"errors": {
"email": [
"No se ha encontrado un usuario con esa dirección de correo."
]
}
}
POST /reset-password — Restablecer Contraseña
POST {{url}}/reset-password
Content-Type: application/json
Accept: application/json
Parámetros del Body:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
token | string | ✅ Sí | Token recibido en el enlace del correo |
email | string | ✅ Sí | Correo de la cuenta |
password | string | ✅ Sí | Nueva contraseña (mínimo 8 caracteres) |
password_confirmation | string | ✅ Sí | Debe coincidir con password |
curl -X POST "{{url}}/reset-password" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"token": "token-recibido-en-el-correo",
"email": "usuario@miempresa.com",
"password": "NuevaPassword123!",
"password_confirmation": "NuevaPassword123!"
}'
✅ Contraseña Restablecida (HTTP 200)
{
"success": true,
"message": "¡Su contraseña ha sido restablecida!"
}
❌ Token Inválido o Vencido (HTTP 422)
El token no corresponde al correo enviado o ya venció (60 minutos). Si el correo no está registrado, el mensaje es No se ha encontrado un usuario con esa dirección de correo. Los errores de la contraseña (longitud, confirmación) llegan en errors.password.
{
"message": "Este token de restablecimiento de contraseña es inválido.",
"errors": {
"email": [
"Este token de restablecimiento de contraseña es inválido."
]
}
}
🔐 Gestión de Tokens (Personal Access Tokens)
| PAT | OAuth2 (login) | |
|---|---|---|
| Caso de uso | Integraciones servidor-a-servidor, scripts automatizados, ERP | Apps web con usuarios que inician sesión |
| Creación | Self-service desde la API | Login con email/contraseña |
| Expiración | Configurable: 1–365 días (por defecto, 90) | 90 días |
| Revocación | Selectiva por ID | Logout del token usado |
| Recomendado para | ✅ Integraciones B2B | ✅ Portales web |
client_uuidTodos los endpoints de /tokens aceptan ?client_uuid={{client_uuid}}. Una Casa de Software lo usa para gestionar los tokens del usuario de una de sus sub-cuentas (con relación activa). Sin el parámetro, se gestionan los tokens del propio usuario.
GET /tokens — Listar Tokens
GET {{url}}/tokens?client_uuid={{client_uuid}}
Authorization: Bearer {token}
Accept: application/json
Obtiene la lista de los tokens vigentes del usuario (no incluye los revocados ni los vencidos), del más reciente al más antiguo. También aparecen los tokens de las sesiones abiertas con POST /auth/login, con name igual a Login - {correo}.
✅ Respuesta Exitosa (HTTP 200)
{
"dataRecords": {
"data": [
{
"id": "cb6282ed9f5cb116416928518f9dd93b53aafc2c0273722bbe5eb7eb96d7ccfac10178f43b8fe617",
"name": "Token ERP",
"description": "Token de integración para facturación",
"ip_address": "203.0.113.10",
"user_agent": "curl/8.4.0",
"scopes": [],
"revoked": false,
"expires_at": "2026-05-02 12:00:00",
"created_at": "2026-02-01 12:00:00",
"last_used_at": "2026-02-10 08:30:00",
"is_expired": false
}
],
"total": 1
},
"success": true
}
GET /tokens/{token_id} — Obtener Token por ID
GET {{url}}/tokens/{token_id}?client_uuid={{client_uuid}}
Authorization: Bearer {token}
Accept: application/json
| Parámetro | Ubicación | Descripción |
|---|---|---|
token_id | path | ID del token (id en GET /tokens, token_id al crearlo) |
client_uuid | query | Opcional. UUID de la sub-cuenta (Casa de Software) |
✅ Respuesta Exitosa (HTTP 200)
{
"dataRecords": {
"data": {
"id": "cb6282ed9f5cb116416928518f9dd93b53aafc2c0273722bbe5eb7eb96d7ccfac10178f43b8fe617",
"name": "Token ERP",
"description": "Token de integración para facturación",
"ip_address": "203.0.113.10",
"user_agent": "curl/8.4.0",
"scopes": [],
"revoked": false,
"expires_at": "2026-05-02 12:00:00",
"created_at": "2026-02-01 12:00:00",
"last_used_at": "2026-02-10 08:30:00",
"is_expired": false
}
},
"success": true
}
❌ Token no Encontrado (HTTP 404)
El token no existe o ya fue revocado.
{
"success": false,
"message": "Token not found."
}
❌ Token de Otro Usuario (HTTP 403)
{
"success": false,
"message": "Token does not belong to the authenticated user"
}
POST /tokens — Crear Nuevo Token
POST {{url}}/tokens?client_uuid={{client_uuid}}
Authorization: Bearer {token}
Content-Type: application/json
Accept: application/json
Parámetros del Body:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | ✅ Sí | Nombre descriptivo (máx. 100). Solo letras sin tilde, números, espacios, guiones (-), guiones bajos (_) y puntos |
description | string | No | Descripción del uso del token (máx. 500) |
expires_in_days | integer | No | Días de vigencia, de 1 a 365. Por defecto, 90 |
Cada usuario puede crear hasta 10 tokens por día. Al superarlo, la API responde HTTP 429.
- cURL
- JavaScript (Axios)
- PHP (Guzzle)
curl -X POST "{{url}}/tokens" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "Token Produccion",
"description": "Token para integración ERP",
"expires_in_days": 60
}'
const response = await axios.post(`${url}/tokens`, {
name: 'Token Produccion',
description: 'Token para integración ERP',
expires_in_days: 60
}, {
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }
});
// Guardar: response.data.dataRecords.data.token (solo se muestra una vez)
const pat = response.data.dataRecords.data.token;
$response = $client->post("{$url}/tokens", [
'headers' => [
'Authorization' => "Bearer {$token}",
'Accept' => 'application/json',
],
'json' => [
'name' => 'Token Produccion',
'description' => 'Token para integración ERP',
'expires_in_days' => 60,
],
]);
// Guardar: dataRecords.data.token (solo se muestra una vez)
$pat = json_decode($response->getBody())->dataRecords->data->token;
✅ Respuesta Exitosa (HTTP 201)
{
"dataRecords": {
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI...",
"token_id": "cb6282ed9f5cb116416928518f9dd93b53aafc2c0273722bbe5eb7eb96d7ccfac10178f43b8fe617",
"name": "Token Produccion",
"description": "Token para integración ERP",
"expires_at": "2026-05-02 12:00:00",
"created_at": "2026-03-03 12:00:00"
}
},
"message": "Token creado exitosamente. Asegúrese de guardar el token, ya que no podrá verlo nuevamente.",
"success": true
}
El valor dataRecords.data.token solo se muestra una vez al crearlo. No se puede recuperar después.
❌ Límite Diario Alcanzado (HTTP 429)
{
"success": false,
"message": "Límite de creación de tokens alcanzado. Máximo 10 por día.",
"error": "Rate limit exceeded",
"retry_after": 3600
}
retry_after indica los segundos que faltan para poder crear otro token.
❌ Error de Validación (HTTP 422)
{
"message": "The token name may only contain letters, numbers, spaces, hyphens, underscores, and periods.",
"errors": {
"name": [
"The token name may only contain letters, numbers, spaces, hyphens, underscores, and periods."
]
}
}
DELETE /tokens/{token_id} — Revocar Token
DELETE {{url}}/tokens/{token_id}?client_uuid={{client_uuid}}
Authorization: Bearer {token}
Accept: application/json
| Parámetro | Ubicación | Descripción |
|---|---|---|
token_id | path | ID del token a revocar |
client_uuid | query | Opcional. UUID de la sub-cuenta (Casa de Software) |
reason | body | Opcional. Motivo de la revocación |
✅ Respuesta Exitosa (HTTP 200)
{
"message": "Token revoked successfully",
"success": true
}
Revocar un token que ya estaba revocado también responde 200.
❌ Token no Encontrado o de Otro Usuario (HTTP 404 / 403)
{
"success": false,
"message": "Token not found: cb6282ed9f5cb116416928518f9dd93b53aafc2c0273722bbe5eb7eb96d7ccfac10178f43b8fe617"
}
Si el token pertenece a otro usuario, la respuesta es HTTP 403 con Token does not belong to user.
POST /tokens/revoke-all — Revocar Todos los Tokens
POST {{url}}/tokens/revoke-all?client_uuid={{client_uuid}}
Authorization: Bearer {token}
Accept: application/json
Revoca todos tus tokens excepto el que estás usando actualmente.
✅ Respuesta Exitosa (HTTP 200)
{
"dataRecords": {
"data": {
"revoked_count": 0
}
},
"message": "Tokens revoked successfully",
"success": true
}