REST API - Documentación Completa
Tabla de Contenidos
- Getting Started / Primeros Pasos
- Documentación de Endpoints
- Diagramas de Flujo
- Manejo de Errores y Troubleshooting
- Buenas Prácticas
Getting Started / Primeros Pasos
¿Qué es Xipher?
Xipher es un BaaS (Backend as a Service) que cuenta con un motor completo para integrarse con el Servicio de Rentas Internas (SRI) de Ecuador de forma fácil e intuitiva.
Funcionalidades principales:
- ✅ Firma y envío de documentos XML al SRI
- ✅ Verificación de estado del documento
- ✅ Generación de RIDE (Representación Impresa de Documento Electrónico)
- ✅ Envío de notificaciones
- ✅ Integración mediante REST API, GraphQL, WSDL y SDK
Xipher simplifica la integración con el SRI, eliminando la complejidad de gestionar certificados digitales, firmas electrónicas y la comunicación directa con los servicios del SRI.
Opciones de integración disponibles:
- REST API (esta documentación): Endpoints HTTP estándar para todas las operaciones
- GraphQL API: Ver documentación GraphQL - Interfaz flexible con tipado fuerte
- WSDL: Ver documentación Web Services - Integración mediante SOAP
- SDK: Próximamente
Obtener API Key o Token
Para usar la API de Xipher, necesitas obtener tus credenciales de acceso:
- Crea una cuenta en www.xipher.app/sign-up o xipher.hex-tech.us/form
- Verifica tu correo electrónico (revisa también la carpeta de spam)
- Accede a tu dashboard y navega a la sección de configuración
- Obtén tu API Key y API Secret desde el panel de control
Estructura del Token:
El token de autenticación se construye codificando en Base64 la combinación de apiKey:apiSecret:
// Ejemplo en Node.js
const apiKey = 'tu_api_key';
const apiSecret = 'tu_api_secret';
const token = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');
// Resultado: "dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Headers de autenticación:
Authorization: Bearer {token_base64}
Base URL de Producción
La Base URL para todas las solicitudes a la API de Xipher es:
https://api.xipher.app/prd/api/documents
Nota: Todas las URLs de endpoints en esta documentación son relativas a esta base URL.
Ejemplo Mínimo Funcional
Este ejemplo muestra el flujo completo: firmar un XML, enviarlo al SRI y consultar su autorización.
1. Firmar un XML
cURL:
curl -X POST "https://api.xipher.app/prd/api/documents/Sign" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0" \
-H "Content-Type: application/json" \
-d '{
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGZhY3R1cmE+Li4uPC9mYWN0dXJhPg==",
"userId": "507f1f77bcf86cd799439011"
}'
Node.js (fetch):
const xmlBase64 = Buffer.from(xmlString).toString('base64');
const token = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');
const response = await fetch('https://api.xipher.app/prd/api/documents/Sign', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
xml: xmlBase64,
userId: '507f1f77bcf86cd799439011'
})
});
const data = await response.json();
console.log('XML Firmado:', data.signedXml);
Node.js (axios):
const axios = require('axios');
const xmlBase64 = Buffer.from(xmlString).toString('base64');
const token = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');
const response = await axios.post(
'https://api.xipher.app/prd/api/documents/Sign',
{
xml: xmlBase64,
userId: '507f1f77bcf86cd799439011'
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log('XML Firmado:', response.data.signedXml);
2. Enviar al SRI (Validar)
cURL:
curl -X POST "https://api.xipher.app/prd/api/documents/Validate" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0" \
-H "Content-Type: application/json" \
-d '{
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGZhY3R1cmE+Li4uPC9mYWN0dXJhPg==",
"userId": "507f1f77bcf86cd799439011"
}'
Node.js (fetch):
const response = await fetch('https://api.xipher.app/prd/api/documents/Validate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
xml: xmlBase64,
userId: '507f1f77bcf86cd799439011'
})
});
const data = await response.json();
console.log('Estado:', data.estado); // RECIBIDA, DEVUELTA, NO RECIBIDO
console.log('Clave de Acceso:', data.accessKey);
3. Consultar Autorización
cURL:
curl -X GET "https://api.xipher.app/prd/api/documents/Authorize/0101202410012345678901234567890123456789012345678" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Node.js (fetch):
const accessKey = '0101202410012345678901234567890123456789012345678';
const response = await fetch(
`https://api.xipher.app/prd/api/documents/Authorize/${accessKey}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
}
);
const data = await response.json();
console.log('Estado:', data.estado); // AUTORIZADO, NO AUTORIZADO, DEVUELTA
console.log('Número de Autorización:', data.numeroAutorizacion);
console.log('Fecha de Autorización:', data.fechaAutorizacion);
Ejemplo Completo: Flujo End-to-End
async function procesarDocumento(xmlString, userId, token) {
try {
// 1. Convertir XML a Base64
const xmlBase64 = Buffer.from(xmlString).toString('base64');
// 2. Firmar y enviar al SRI
const validateResponse = await fetch(
'https://api.xipher.app/prd/api/documents/Validate',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
xml: xmlBase64,
userId: userId
})
}
);
const validateData = await validateResponse.json();
if (validateData.estado !== 'RECIBIDA') {
throw new Error(`Documento no recibido: ${validateData.mensaje || validateData.estado}`);
}
const accessKey = validateData.accessKey;
console.log('Documento recibido. Clave de acceso:', accessKey);
// 3. Consultar autorización (con reintentos)
let estado = 'RECIBIDA';
let intentos = 0;
const maxIntentos = 10;
while (estado === 'RECIBIDA' && intentos < maxIntentos) {
await new Promise(resolve => setTimeout(resolve, 5000)); // Esperar 5 segundos
const authorizeResponse = await fetch(
`https://api.xipher.app/prd/api/documents/Authorize/${accessKey}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
}
);
const authorizeData = await authorizeResponse.json();
estado = authorizeData.estado;
intentos++;
console.log(`Intento ${intentos}: Estado = ${estado}`);
}
if (estado === 'AUTORIZADO') {
// 4. Obtener PDF
const pdfResponse = await fetch(
`https://api.xipher.app/prd/api/documents/PDF/${accessKey}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
}
);
const pdfData = await pdfResponse.json();
const pdfBuffer = Buffer.from(pdfData.pdf, 'base64');
console.log('PDF generado exitosamente');
return {
accessKey,
estado,
numeroAutorizacion: authorizeData.numeroAutorizacion,
pdf: pdfBuffer
};
} else {
throw new Error(`Documento no autorizado: ${estado}`);
}
} catch (error) {
console.error('Error al procesar documento:', error);
throw error;
}
}
Flujo Completo: XML → Base64 → Firma → SRI → Autorización → PDF
┌─────────────────────────────────────────────────────────────────┐
│ FLUJO COMPLETO │
└─────────────────────────────────────────────────────────────────┘
1. CLIENTE
└─> Genera XML del documento electrónico
└─> Convierte XML a Base64
└─> Envía a Xipher
2. XIPHER
└─> Recibe XML en Base64
└─> Firma digitalmente el XML con certificado del emisor
└─> Envía XML firmado al SRI
3. SRI
└─> Valida estructura y firma del documento
└─> Retorna estado: RECIBIDA, DEVUELTA, o NO RECIBIDO
└─> Si es RECIBIDA, procesa para autorización
4. CLIENTE (Polling)
└─> Consulta estado de autorización periódicamente
└─> Estados posibles: RECIBIDA → AUTORIZADO / NO AUTORIZADO
5. XIPHER
└─> Si AUTORIZADO: Genera PDF (RIDE)
└─> Retorna número de autorización y PDF
6. CLIENTE
└─> Recibe PDF en Base64
└─> Decodifica y almacena/descarga
Documentación de Endpoints
Documentos Emitidos
POST /Sign
Firma digitalmente un documento XML electrónico usando la firma electrónica de la compañía registrada.
Método: POST
URL: https://api.xipher.app/prd/api/documents/Sign
Explicación:
Este endpoint firma digitalmente un documento XML. El documento debe estar codificado en Base64. La firma se realiza usando el certificado digital de la compañía asociada a la cuenta.
Headers Obligatorios:
Authorization: Bearer {token}
Content-Type: application/json
Body Requerido:
{
"xml": "string (base64)",
"userId": "string"
}
Parámetros:
xml(string, requerido): Documento XML codificado en Base64userId(string, requerido): ID de la cuenta de usuario
Ejemplo de Request:
curl -X POST "https://api.xipher.app/prd/api/documents/Sign" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0" \
-H "Content-Type: application/json" \
-d '{
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGZhY3R1cmE+Li4uPC9mYWN0dXJhPg==",
"userId": "507f1f77bcf86cd799439011"
}'
Ejemplo de Response:
{
"signedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGZhY3R1cmE+Li4uPC9mYWN0dXJhPg=="
}
Errores Comunes:
400 Bad Request: XML inválido o mal formado401 Unauthorized: API key inválida o sin suscripción activa404 Not Found: Emisor no registrado o firma electrónica no actualizada500 Internal Server Error: Error al firmar el documento
Casos de Uso:
- Firmar facturas electrónicas antes de enviarlas al SRI
- Firmar documentos de retención
- Firmar notas de crédito o débito
POST /Validate
Firma digitalmente un documento XML y lo envía al SRI para validación en una sola operación.
Método: POST
URL: https://api.xipher.app/prd/api/documents/Validate
Explicación:
Este endpoint realiza dos operaciones: primero firma el documento XML y luego lo envía al SRI para validación. Es el método recomendado para enviar documentos al SRI.
Headers Obligatorios:
Authorization: Bearer {token}
Content-Type: application/json
Body Requerido:
{
"xml": "string (base64)",
"userId": "string"
}
Parámetros:
xml(string, requerido): Documento XML codificado en Base64userId(string, requerido): ID de la cuenta de usuario
Ejemplo de Request:
curl -X POST "https://api.xipher.app/prd/api/documents/Validate" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0" \
-H "Content-Type: application/json" \
-d '{
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGZhY3R1cmE+Li4uPC9mYWN0dXJhPg==",
"userId": "507f1f77bcf86cd799439011"
}'
Ejemplo de Response (Documento Recibido):
{
"signedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4K...",
"estado": "RECIBIDA",
"accessKey": "0101202410012345678901234567890123456789012345678"
}
Ejemplo de Response (Documento Devuelto):
{
"signedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4K...",
"estado": "DEVUELTA",
"accessKey": "0101202410012345678901234567890123456789012345678",
"identificador": "65",
"mensaje": "FECHA DE EMISIÓN EXTRANJERA",
"tipo": "ERROR",
"informacionAdicional": "La fecha de emisión es posterior a la fecha actual"
}
Errores Comunes:
400 Bad Request: XML inválido o datos incorrectos401 Unauthorized: API key inválida o sin suscripción activa404 Not Found: Emisor no registrado o firma electrónica no actualizada500 Internal Server Error: Error al procesar el documento
Casos de Uso:
- Enviar facturas electrónicas al SRI para validación
- Validar documentos de retención
- Enviar notas de crédito o débito
GET /Authorize/{accessKey}
Consulta el estado de autorización de un documento en el SRI usando su clave de acceso.
Método: GET
URL: https://api.xipher.app/prd/api/documents/Authorize/{accessKey}
Explicación:
Este endpoint consulta el estado de autorización de un documento. Si el documento ya está autorizado, retorna la información almacenada sin consultar el SRI nuevamente. Tiene rate limiting de 3 solicitudes por minuto por accessKey.
Headers Obligatorios:
Authorization: Bearer {token}
Parámetros de URL:
accessKey(string, requerido): Clave de acceso del documento (49 caracteres)
Query Parameters:
forceSriResponse(boolean, opcional): Si estrue, fuerza la consulta al SRI incluso si el documento ya está autorizado
Ejemplo de Request:
curl -X GET "https://api.xipher.app/prd/api/documents/Authorize/0101202410012345678901234567890123456789012345678" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Ejemplo de Response (Autorizado):
{
"estado": "AUTORIZADO",
"authXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4K...",
"numeroAutorizacion": "12345678901234567890123456789012345678901234567890",
"fechaAutorizacion": "2024-01-15T10:30:00.000Z",
"identificador": null,
"mensaje": null,
"tipo": null,
"informacionAdicional": null
}
Ejemplo de Response (No Autorizado):
{
"estado": "NO AUTORIZADO",
"authXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4K...",
"numeroAutorizacion": null,
"fechaAutorizacion": null,
"identificador": "58",
"mensaje": "Error en la estructura de clave acceso",
"tipo": "ERROR",
"informacionAdicional": "La clave de acceso no es válida"
}
Errores Comunes:
202 Accepted: Clave de acceso inválida401 Unauthorized: API key inválida o sin suscripción activa404 Not Found: Emisor no registrado429 Too Many Requests: Rate limit excedido (3 solicitudes/minuto por accessKey)500 Internal Server Error: Error al procesar la clave de acceso
Casos de Uso:
- Verificar si una factura electrónica ha sido autorizada por el SRI
- Consultar el estado de documentos enviados previamente
- Implementar polling para verificar autorización
GET /PDF/{accessKey}
Genera y retorna el PDF (RIDE) de un documento electrónico autorizado.
Método: GET
URL: https://api.xipher.app/prd/api/documents/PDF/{accessKey}
Explicación:
Este endpoint genera el PDF del documento electrónico autorizado. El PDF se retorna en formato Base64 y corresponde al RIDE (Representación Impresa de Documento Electrónico).
Headers Obligatorios:
Authorization: Bearer {token}
Parámetros de URL:
accessKey(string, requerido): Clave de acceso del documento (49 caracteres)
Ejemplo de Request:
curl -X GET "https://api.xipher.app/prd/api/documents/PDF/0101202410012345678901234567890123456789012345678" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Ejemplo de Response:
{
"pdf": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9MZW5ndGg..."
}
Errores Comunes:
401 Unauthorized: Clave de acceso inválida o no autorizado404 Not Found: Emisor no registrado o documento no encontrado500 Internal Server Error: Error al procesar la clave de acceso
Casos de Uso:
- Obtener la representación en PDF de una factura electrónica autorizada
- Generar RIDE para impresión o envío por email
- Descargar documentos autorizados
GET /XML/{accessKey}
Retorna el XML de un documento electrónico autorizado.
Método: GET
URL: https://api.xipher.app/prd/api/documents/XML/{accessKey}
Explicación:
Este endpoint retorna el XML original del documento electrónico. El XML se retorna en formato Base64.
Headers Obligatorios:
Authorization: Bearer {token}
Parámetros de URL:
accessKey(string, requerido): Clave de acceso del documento (49 caracteres)
Ejemplo de Request:
curl -X GET "https://api.xipher.app/prd/api/documents/XML/0101202410012345678901234567890123456789012345678" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Ejemplo de Response:
{
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4K..."
}
Errores Comunes:
401 Unauthorized: Clave de acceso inválida o no autorizado404 Not Found: Emisor no registrado o documento no encontrado500 Internal Server Error: Error al procesar la clave de acceso
Casos de Uso:
- Obtener el XML original de una factura electrónica autorizada
- Almacenar documentos en formato XML
- Realizar auditorías o análisis de documentos
GET /issued/{accountId}
Obtiene una lista paginada de documentos electrónicos emitidos por una cuenta.
Método: GET
URL: https://api.xipher.app/prd/api/documents/issued/{accountId}
Explicación:
Este endpoint retorna una lista de documentos emitidos con paginación. Permite filtrar por número de documento, tipo, estado y rango de fechas.
Headers Obligatorios:
Authorization: Bearer {token}
Parámetros de URL:
accountId(string, requerido): ID de la cuenta de usuario
Query Parameters:
fromDate(string, opcional): Fecha de inicio (formato: YYYY-MM-DD)toDate(string, opcional): Fecha de fin (formato: YYYY-MM-DD)docNumber(string, opcional): Número de documento (formato: 001-001-000000001 o sin guiones)docType(string, opcional): Tipo de documento según SRI01: Factura03: Liquidación de Compra04: Nota de Crédito05: Nota de Débito06: Guía de Remisión07: Comprobante de Retención
docStatus(string, opcional): Estado del documentoAUTORIZADONO AUTORIZADODEVUELTARECIBIDANO RECIBIDO
limit(integer, opcional): Número máximo de documentos (default: 20, max: 100)cursor(string, opcional): Cursor para paginación (ID del último documento de la página anterior)
Ejemplo de Request:
curl -X GET "https://api.xipher.app/prd/api/documents/issued/507f1f77bcf86cd799439011?fromDate=2024-01-01&toDate=2024-12-31&docType=01&limit=20" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Ejemplo de Response:
{
"issued": [
{
"type": "FACTURA",
"number": "001-001-000000001",
"accessKey": "0101202410012345678901234567890123456789012345678",
"authorization": "12345678901234567890123456789012345678901234567890",
"environment": "PRODUCCIÓN",
"authorizationDate": "2024-01-15T10:30:00.000Z",
"nif": "1234567890001",
"status": "AUTORIZADO",
"legalName": "EMPRESA EJEMPLO S.A.",
"nifPartner": "9876543210001",
"legalNamePartner": "CLIENTE EJEMPLO S.A.",
"issueDate": "2024-01-15T00:00:00.000Z"
}
],
"pagination": {
"hasNextPage": true,
"nextCursor": "507f1f77bcf86cd799439011",
"limit": 20,
"totalCount": 1500
}
}
Errores Comunes:
403 Forbidden: Cuenta no encontrada, sin compañías o sin suscripción activa500 Internal Server Error: Error al obtener documentos emitidos
Casos de Uso:
- Listar todas las facturas emitidas en un período
- Filtrar documentos por tipo o estado
- Implementar reportes de documentos emitidos
- Realizar consultas históricas
Documentos Recibidos
GET /received/{accountId}
Obtiene una lista paginada de documentos electrónicos recibidos (facturas de proveedores) para una cuenta.
Método: GET
URL: https://api.xipher.app/prd/api/documents/received/{accountId}
Explicación:
Este endpoint retorna documentos recibidos de proveedores. Si no se proporcionan fromDate/toDate, se usa la fecha de ayer hasta la fecha actual. Tiene rate limiting de 10 solicitudes por minuto por dirección IP.
Headers Obligatorios:
Authorization: Bearer {token}
Parámetros de URL:
accountId(string, requerido): ID de la cuenta de usuario
Query Parameters:
fromDate(string, opcional): Fecha de inicio (formato: YYYY-MM-DD, default: ayer)toDate(string, opcional): Fecha de fin (formato: YYYY-MM-DD, default: hoy)docNumber(string, opcional): Número de documento (formato: 001-001-000000001 o sin guiones)docType(string, opcional): Tipo de documento según SRI (01, 03, 04, 05, 06, 07)limit(integer, opcional): Número máximo de documentos (default: 200, max: 500)cursor(string, opcional): Cursor para paginación (ID del documento del último elemento de la página anterior)
Ejemplo de Request:
curl -X GET "https://api.xipher.app/prd/api/documents/received/507f1f77bcf86cd799439011?fromDate=2024-01-01&toDate=2024-12-31&docType=01&limit=200" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Ejemplo de Response:
{
"received": [
{
"type": "FACTURA",
"sriType": "01",
"number": "001-001-000000001",
"authorization": "12345678901234567890123456789012345678901234567890",
"accesskey": "0101202410012345678901234567890123456789012345678",
"environment": "PRODUCCIÓN",
"issueDate": "2024-01-15T00:00:00.000Z",
"authorizationDate": "2024-01-15T10:30:00.000Z",
"nif": "1234567890001",
"legalName": "PROVEEDOR EJEMPLO S.A.",
"nifPartner": "9876543210001",
"status": "AUTORIZADO",
"legalNamePartner": "MI EMPRESA S.A.",
"subtotal": "100.00",
"tax": "12.00",
"total": "112.00"
}
],
"pagination": {
"hasNextPage": false,
"nextCursor": null,
"limit": 200,
"totalCount": 1
}
}
Errores Comunes:
403 Forbidden: Cuenta no encontrada, sin compañías o sin suscripción activa429 Too Many Requests: Rate limit excedido (10 solicitudes/minuto por IP)500 Internal Server Error: Error al obtener documentos recibidos
Casos de Uso:
- Listar facturas recibidas de proveedores
- Filtrar documentos recibidos por fecha o tipo
- Implementar reportes de compras
- Realizar conciliaciones contables
GET /received/PDF/{accountId}/{accessKey}
Genera y retorna el PDF de un documento electrónico recibido (factura de proveedor).
Método: GET
URL: https://api.xipher.app/prd/api/documents/received/PDF/{accountId}/{accessKey}
Explicación:
Este endpoint genera el PDF de un documento recibido. El PDF se retorna en formato Base64. Tiene rate limiting de 10 solicitudes por minuto por dirección IP.
Headers Obligatorios:
Authorization: Bearer {token}
Parámetros de URL:
accountId(string, requerido): ID de la cuenta de usuarioaccessKey(string, requerido): Clave de acceso del documento (49 caracteres)
Ejemplo de Request:
curl -X GET "https://api.xipher.app/prd/api/documents/received/PDF/507f1f77bcf86cd799439011/0101202410012345678901234567890123456789012345678" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Ejemplo de Response:
{
"pdf": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9MZW5ndGg..."
}
Errores Comunes:
401 Unauthorized: Clave de acceso inválida o no autorizado404 Not Found: Emisor no registrado o documento no encontrado429 Too Many Requests: Rate limit excedido500 Internal Server Error: Error al procesar la clave de acceso
Casos de Uso:
- Obtener el PDF de facturas recibidas de proveedores
- Generar RIDE de documentos recibidos para impresión
- Archivar documentos recibidos en formato PDF
GET /received/XML/{accountId}/{accessKey}
Retorna el XML de un documento electrónico recibido (factura de proveedor).
Método: GET
URL: https://api.xipher.app/prd/api/documents/received/XML/{accountId}/{accessKey}
Explicación:
Este endpoint retorna el XML de un documento recibido. El XML se retorna en formato Base64. Tiene rate limiting de 10 solicitudes por minuto por dirección IP.
Headers Obligatorios:
Authorization: Bearer {token}
Parámetros de URL:
accountId(string, requerido): ID de la cuenta de usuarioaccessKey(string, requerido): Clave de acceso del documento (49 caracteres)
Ejemplo de Request:
curl -X GET "https://api.xipher.app/prd/api/documents/received/XML/507f1f77bcf86cd799439011/0101202410012345678901234567890123456789012345678" \
-H "Authorization: Bearer dHVfYXBpX2tleTp0dV9hcGlfc2VjcmV0"
Ejemplo de Response:
{
"xml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4K..."
}
Errores Comunes:
401 Unauthorized: Clave de acceso inválida o no autorizado404 Not Found: Emisor no registrado o documento no encontrado429 Too Many Requests: Rate limit excedido500 Internal Server Error: Error al procesar la clave de acceso
Casos de Uso:
- Obtener el XML de facturas recibidas de proveedores
- Almacenar documentos recibidos en formato XML
- Realizar análisis o validaciones de documentos recibidos
Autenticación
POST /Authenticate
Nota: Este endpoint no está disponible en la versión actual de la API. La autenticación se realiza mediante el header Authorization: Bearer {token} en todos los endpoints.
Para obtener tu token de autenticación, consulta la sección Obtener API Key o Token.
Diagramas de Flujo
Flujo General: Cliente → Xipher → SRI → Cliente
┌─────────┐ ┌─────────┐ ┌─────┐ ┌─────────┐
│ Cliente │────────>│ Xipher │────────>│ SRI │────────>│ Cliente │
└─────────┘ └─────────┘ └─────┘ └─────────┘
│ │ │ │
│ 1. XML Base64 │ │ │
│───────────────────>│ │ │
│ │ 2. Firma XML │ │
│ │─────────────────>│ │
│ │ │ 3. Valida │
│ │ │ y procesa │
│ │<─────────────────│ │
│ │ 4. Estado │ │
│<───────────────────│ │ │
│ 5. AccessKey │ │ │
│ │ │ │
│ 6. Consulta estado │ │ │
│───────────────────>│ │ │
│ │ 7. Consulta SRI │ │
│ │─────────────────>│ │
│ │<─────────────────│ │
│ │ 8. Estado │ │
│<───────────────────│ │ │
│ 9. Autorización │ │ │
│ │ │ │
│ 10. Solicita PDF │ │ │
│───────────────────>│ │ │
│ │ 11. Genera PDF │ │
│<───────────────────│ │ │
│ 12. PDF Base64 │ │ │
Flujo de Documentos Emitidos: Sign → Validate → Authorize → PDF/XML
┌─────────────────────────────────────────────────────────────────┐
│ FLUJO DE DOCUMENTOS EMITIDOS │
└─────────────────────────────────────────────────────────────────┘
1. SIGN
Cliente → Xipher
├─ Envía XML en Base64
├─ Xipher firma el documento
└─ Retorna XML firmado
2. VALIDATE (Recomendado)
Cliente → Xipher → SRI
├─ Envía XML en Base64
├─ Xipher firma el documento
├─ Xipher envía al SRI
├─ SRI valida estructura y firma
└─ Retorna: estado (RECIBIDA/DEVUELTA) + accessKey
3. AUTHORIZE (Polling)
Cliente → Xipher → SRI
├─ Consulta periódicamente con accessKey
├─ Estados posibles:
│ ├─ RECIBIDA: SRI aún procesando
│ ├─ AUTORIZADO: Documento autorizado ✅
│ ├─ NO AUTORIZADO: Documento rechazado ❌
│ └─ DEVUELTA: Documento con errores ⚠️
└─ Retorna: estado + número autorización + fecha
4. PDF/XML
Cliente → Xipher
├─ Solicita PDF o XML con accessKey
└─ Retorna: PDF/XML en Base64
Flujo de Documentos Recibidos: SRI → Xipher → Cliente
┌─────────────────────────────────────────────────────────────────┐
│ FLUJO DE DOCUMENTOS RECIBIDOS │
└─────────────────────────────────────────────────────────────────┘
1. SRI → Xipher
├─ SRI envía documentos de proveedores
├─ Xipher procesa y almacena
└─ Xipher notifica al cliente (opcional)
2. Cliente → Xipher
├─ Cliente consulta documentos recibidos
├─ Filtros disponibles:
│ ├─ Por fecha (fromDate/toDate)
│ ├─ Por tipo de documento
│ ├─ Por número de documento
│ └─ Paginación con cursor
└─ Retorna: Lista de documentos recibidos
3. Cliente → Xipher
├─ Cliente solicita PDF o XML
├─ Usa accountId + accessKey
└─ Retorna: PDF/XML en Base64
Manejo de Errores y Troubleshooting
Estados del SRI
El SRI puede retornar los siguientes estados para los documentos:
RECIBIDO / RECIBIDA
Significado: El documento fue recibido correctamente por el SRI y está en proceso de autorización.
Acción recomendada:
- Esperar entre 5-30 segundos antes de consultar nuevamente
- Implementar polling con intervalos de 5-10 segundos
- Máximo de intentos recomendado: 10-15 consultas
Ejemplo de implementación:
async function esperarAutorizacion(accessKey, token, maxIntentos = 10) {
for (let i = 0; i < maxIntentos; i++) {
await new Promise(resolve => setTimeout(resolve, 5000)); // Esperar 5 segundos
const response = await fetch(
`https://api.xipher.app/prd/api/documents/Authorize/${accessKey}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
const data = await response.json();
if (data.estado !== 'RECIBIDA') {
return data; // AUTORIZADO, NO AUTORIZADO, o DEVUELTA
}
}
throw new Error('Tiempo de espera agotado. El documento sigue en estado RECIBIDA');
}
DEVUELTO / DEVUELTA
Significado: El documento fue rechazado por el SRI debido a errores en su estructura o contenido.
Causas comunes:
- Fecha de emisión inválida o fuera de rango
- Datos del contribuyente incorrectos
- Estructura XML inválida
- Valores numéricos incorrectos
- Información faltante o duplicada
Acción recomendada:
- Revisar el campo
mensajeen la respuesta - Revisar el campo
informacionAdicionalpara detalles - Corregir el XML según los errores indicados
- Volver a enviar el documento corregido
Ejemplo de respuesta:
{
"estado": "DEVUELTA",
"identificador": "65",
"mensaje": "FECHA DE EMISIÓN EXTRANJERA",
"tipo": "ERROR",
"informacionAdicional": "La fecha de emisión es posterior a la fecha actual"
}
NO AUTORIZADO
Significado: El documento fue procesado pero no fue autorizado por el SRI.
Causas comunes:
- Problemas con la firma electrónica
- Certificado digital vencido o inválido
- Datos inconsistentes con el SRI
- Problemas de conectividad durante el procesamiento
Acción recomendada:
- Verificar que la firma electrónica esté actualizada
- Revisar los campos
mensajeeinformacionAdicional - Contactar al soporte de Xipher si el problema persiste
AUTORIZADO
Significado: El documento fue autorizado exitosamente por el SRI.
Acción recomendada:
- Guardar el
numeroAutorizacionyfechaAutorizacion - Obtener el PDF usando
/PDF/{accessKey} - Almacenar el documento autorizado
Errores HTTP
400 Bad Request
Causas:
- XML mal formado o inválido
- Parámetros faltantes o incorrectos
- Formato de fecha inválido
- Clave de acceso con formato incorrecto (debe tener 49 caracteres)
Solución:
- Validar el XML antes de enviarlo
- Verificar que todos los parámetros requeridos estén presentes
- Validar el formato de la clave de acceso
- Revisar la estructura del JSON en el body
Ejemplo:
// Validar XML antes de enviar
function validarXML(xmlString) {
try {
// Verificar que sea XML válido
const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, 'text/xml');
const error = doc.querySelector('parsererror');
if (error) {
throw new Error('XML inválido');
}
return true;
} catch (error) {
throw new Error(`Error al validar XML: ${error.message}`);
}
}
401 Unauthorized
Causas:
- Token de autenticación inválido o faltante
- API Key o API Secret incorrectos
- Token expirado (si aplica)
- Sin suscripción activa
Solución:
- Verificar que el token esté correctamente formado (Base64 de
apiKey:apiSecret) - Verificar que el header
Authorizationesté presente - Verificar que la cuenta tenga una suscripción activa
- Regenerar el token si es necesario
Ejemplo de verificación:
// Verificar token
function verificarToken(apiKey, apiSecret) {
const token = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');
console.log('Token generado:', token);
// El token debe tener al menos 20 caracteres
if (token.length < 20) {
throw new Error('Token inválido: muy corto');
}
return token;
}
404 Not Found
Causas:
- Emisor no registrado en Xipher
- Firma electrónica no actualizada
- Documento no encontrado (accessKey inválido)
- Endpoint incorrecto
Solución:
- Verificar que la compañía esté registrada en Xipher
- Verificar que la firma electrónica esté actualizada
- Verificar que el accessKey sea correcto (49 caracteres)
- Verificar la URL del endpoint
429 Too Many Requests
Causas:
- Rate limit excedido
- Demasiadas solicitudes en un período corto
Límites de rate limiting:
/Authorize/{accessKey}: 3 solicitudes por minuto por accessKey/received/{accountId}: 10 solicitudes por minuto por IP/received/PDF/{accountId}/{accessKey}: 10 solicitudes por minuto por IP/received/XML/{accountId}/{accessKey}: 10 solicitudes por minuto por IP
Solución:
- Implementar retry con backoff exponencial
- Cachear respuestas cuando sea posible
- Reducir la frecuencia de consultas
- Esperar antes de reintentar
Ejemplo de retry con backoff:
async function fetchConRetry(url, options, maxIntentos = 3) {
for (let i = 0; i < maxIntentos; i++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
console.log(`Rate limit excedido. Esperando ${retryAfter} segundos...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
return response;
} catch (error) {
if (i === maxIntentos - 1) throw error;
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
}
}
}
500 Internal Server Error
Causas:
- Error interno del servidor de Xipher
- Problemas temporales con el SRI
- Error al procesar el documento
Solución:
- Reintentar la solicitud después de unos segundos
- Verificar el estado del servicio
- Contactar al soporte de Xipher si el problema persiste
Ejemplo de manejo:
async function solicitudConReintento(url, options, maxIntentos = 3) {
for (let i = 0; i < maxIntentos; i++) {
try {
const response = await fetch(url, options);
if (response.status >= 500) {
if (i < maxIntentos - 1) {
const delay = Math.pow(2, i) * 1000; // Backoff exponencial
console.log(`Error del servidor. Reintentando en ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
return response;
} catch (error) {
if (i === maxIntentos - 1) throw error;
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
}
}
}
Recomendaciones de Reintentos
Para estado RECIBIDO:
- Intervalo inicial: 5 segundos
- Intervalo máximo: 30 segundos
- Máximo de intentos: 10-15
- Estrategia: Intervalo fijo o incremental
Para errores HTTP 5xx:
- Intervalo inicial: 1 segundo
- Estrategia: Backoff exponencial (1s, 2s, 4s, 8s...)
- Máximo de intentos: 3-5
Para errores HTTP 429 (Rate Limit):
- Leer header
Retry-Aftersi está disponible - Estrategia: Esperar el tiempo indicado o usar backoff exponencial
- Máximo de intentos: 3-5
Buenas Prácticas
Validar XML Antes de Enviarlo
Siempre valida la estructura y el contenido del XML antes de enviarlo a Xipher. Esto reduce errores y mejora la experiencia del usuario.
Ejemplo en Node.js:
const { DOMParser } = require('xmldom');
function validarXML(xmlString) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, 'text/xml');
// Verificar errores de parsing
const errors = doc.getElementsByTagName('parsererror');
if (errors.length > 0) {
throw new Error('XML mal formado');
}
// Validar estructura básica
const factura = doc.getElementsByTagName('factura');
if (factura.length === 0) {
throw new Error('No se encontró el elemento factura');
}
return true;
} catch (error) {
throw new Error(`Error al validar XML: ${error.message}`);
}
}
// Uso
try {
validarXML(xmlString);
const xmlBase64 = Buffer.from(xmlString).toString('base64');
// Enviar a Xipher
} catch (error) {
console.error('XML inválido:', error.message);
}
Correcto Manejo de Base64
Codificación:
- Asegúrate de que el XML esté en UTF-8 antes de codificar
- No agregues saltos de línea en el Base64
- Valida que el Base64 sea correcto antes de enviar
Ejemplo:
// Correcto
const xmlBase64 = Buffer.from(xmlString, 'utf8').toString('base64');
// Incorrecto - no uses atob/btoa en Node.js
// const xmlBase64 = btoa(xmlString); // ❌ No funciona bien con UTF-8
// Decodificación
const xmlString = Buffer.from(xmlBase64, 'base64').toString('utf8');
Validación de Base64:
function esBase64Valido(str) {
try {
return btoa(atob(str)) === str;
} catch (err) {
return false;
}
}
No Exponer Tokens en Frontend
❌ Incorrecto:
// NUNCA hagas esto en código frontend
const apiKey = 'tu_api_key';
const apiSecret = 'tu_api_secret';
const token = btoa(`${apiKey}:${apiSecret}`);
// El código JavaScript es visible para cualquiera
fetch('https://api.xipher.app/...', {
headers: { 'Authorization': `Bearer ${token}` }
});
✅ Correcto:
// Backend (Node.js, Python, etc.)
// El token se genera en el servidor
const token = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');
// Frontend solo llama a tu backend
fetch('/api/firmar-documento', {
method: 'POST',
body: JSON.stringify({ xml: xmlBase64 })
});
// Tu backend hace la llamada a Xipher
app.post('/api/firmar-documento', async (req, res) => {
const token = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');
const response = await fetch('https://api.xipher.app/...', {
headers: { 'Authorization': `Bearer ${token}` }
});
// ...
});
Reintentos Cuando Estado = RECIBIDO
Implementa un sistema de polling robusto para consultar la autorización cuando el estado es RECIBIDO.
Ejemplo completo:
async function consultarAutorizacion(accessKey, token, opciones = {}) {
const {
intervaloInicial = 5000, // 5 segundos
intervaloMaximo = 30000, // 30 segundos
maxIntentos = 15,
factorBackoff = 1.5
} = opciones;
let intervalo = intervaloInicial;
let intentos = 0;
while (intentos < maxIntentos) {
try {
const response = await fetch(
`https://api.xipher.app/prd/api/documents/Authorize/${accessKey}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
// Si no está en RECIBIDO, retornar resultado
if (data.estado !== 'RECIBIDA') {
return data;
}
// Esperar antes del siguiente intento
console.log(`Intento ${intentos + 1}/${maxIntentos}: Estado = RECIBIDA. Esperando ${intervalo}ms...`);
await new Promise(resolve => setTimeout(resolve, intervalo));
// Incrementar intervalo con backoff
intervalo = Math.min(intervalo * factorBackoff, intervaloMaximo);
intentos++;
} catch (error) {
console.error(`Error en intento ${intentos + 1}:`, error.message);
if (intentos === maxIntentos - 1) {
throw new Error(`No se pudo obtener autorización después de ${maxIntentos} intentos`);
}
await new Promise(resolve => setTimeout(resolve, intervalo));
intervalo = Math.min(intervalo * factorBackoff, intervaloMaximo);
intentos++;
}
}
throw new Error('Tiempo de espera agotado');
}
Recomendaciones de Seguridad
-
Almacenar credenciales de forma segura:
- Usa variables de entorno para API Key y API Secret
- No commitees credenciales en el repositorio
- Usa servicios de gestión de secretos (AWS Secrets Manager, Azure Key Vault, etc.)
-
Validar y sanitizar inputs:
- Valida todos los datos antes de enviarlos
- Sanitiza el XML para prevenir inyecciones
- Valida el formato de la clave de acceso
-
Implementar logging seguro:
- No registres tokens o credenciales en logs
- Registra solo información necesaria para debugging
- Usa niveles de log apropiados
-
Manejar errores de forma segura:
- No expongas información sensible en mensajes de error
- Registra errores detallados en el servidor
- Retorna mensajes genéricos al cliente
Ejemplo de configuración segura:
// .env (nunca commitees este archivo)
API_KEY=tu_api_key
API_SECRET=tu_api_secret
// config.js
require('dotenv').config();
const config = {
apiKey: process.env.API_KEY,
apiSecret: process.env.API_SECRET,
baseUrl: 'https://api.xipher.app/prd/api/documents'
};
if (!config.apiKey || !config.apiSecret) {
throw new Error('API_KEY y API_SECRET deben estar configurados');
}
module.exports = config;
Límites de Consumo
Rate Limits:
/Authorize/{accessKey}: 3 solicitudes/minuto por accessKey/received/{accountId}: 10 solicitudes/minuto por IP/received/PDF/{accountId}/{accessKey}: 10 solicitudes/minuto por IP/received/XML/{accountId}/{accessKey}: 10 solicitudes/minuto por IP- Otros endpoints: Sin límite específico, pero se recomienda no exceder 100 solicitudes/minuto
Recomendaciones:
- Implementa cache cuando sea posible
- Agrupa solicitudes cuando sea factible
- Respeta los rate limits para evitar errores 429
- Implementa circuit breakers para prevenir sobrecarga
Ejemplo de cache:
const cache = new Map();
async function obtenerPDFConCache(accessKey, token) {
// Verificar cache
if (cache.has(accessKey)) {
const cached = cache.get(accessKey);
if (Date.now() - cached.timestamp < 3600000) { // 1 hora
return cached.data;
}
}
// Obtener de la API
const response = await fetch(
`https://api.xipher.app/prd/api/documents/PDF/${accessKey}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
const data = await response.json();
// Guardar en cache
cache.set(accessKey, {
data,
timestamp: Date.now()
});
return data;
}
Otras Buenas Prácticas
-
Manejo de timeouts:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 segundos
try {
const response = await fetch(url, {
signal: controller.signal,
// ...
});
clearTimeout(timeoutId);
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Timeout: La solicitud tardó demasiado');
}
throw error;
} -
Validar respuestas:
async function validarRespuesta(response) {
if (!response.ok) {
const error = await response.json().catch(() => ({ mensaje: 'Error desconocido' }));
throw new Error(error.mensaje || `HTTP ${response.status}`);
}
return response.json();
} -
Manejo de errores centralizado:
class XipherError extends Error {
constructor(message, status, detalles) {
super(message);
this.status = status;
this.detalles = detalles;
}
}
async function llamarAPI(url, options) {
try {
const response = await fetch(url, options);
const data = await response.json();
if (!response.ok) {
throw new XipherError(
data.mensaje || 'Error desconocido',
response.status,
data
);
}
return data;
} catch (error) {
if (error instanceof XipherError) {
throw error;
}
throw new XipherError('Error de red', 0, error);
}
}
Recursos Adicionales
- Documentación OpenAPI: https://api.xipher.app/api-docs.json
- GraphQL API: Documentación GraphQL - API GraphQL disponible para integración
- WSDL: Documentación Web Services - Integración mediante SOAP
- Soporte: support@mail.hex-tech.us
- Sitio Web: www.xipher.app
- Dashboard: www.xipher.app/dashboard
Última actualización: 2024