Skip to main content

REST API - Documentación Completa

Tabla de Contenidos

  1. Getting Started / Primeros Pasos
  2. Documentación de Endpoints
  3. Diagramas de Flujo
  4. Manejo de Errores y Troubleshooting
  5. 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:

Obtener API Key o Token

Para usar la API de Xipher, necesitas obtener tus credenciales de acceso:

  1. Crea una cuenta en www.xipher.app/sign-up o xipher.hex-tech.us/form
  2. Verifica tu correo electrónico (revisa también la carpeta de spam)
  3. Accede a tu dashboard y navega a la sección de configuración
  4. 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 Base64
  • userId (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 formado
  • 401 Unauthorized: API key inválida o sin suscripción activa
  • 404 Not Found: Emisor no registrado o firma electrónica no actualizada
  • 500 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 Base64
  • userId (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 incorrectos
  • 401 Unauthorized: API key inválida o sin suscripción activa
  • 404 Not Found: Emisor no registrado o firma electrónica no actualizada
  • 500 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 es true, 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álida
  • 401 Unauthorized: API key inválida o sin suscripción activa
  • 404 Not Found: Emisor no registrado
  • 429 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 autorizado
  • 404 Not Found: Emisor no registrado o documento no encontrado
  • 500 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 autorizado
  • 404 Not Found: Emisor no registrado o documento no encontrado
  • 500 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 SRI
    • 01: Factura
    • 03: Liquidación de Compra
    • 04: Nota de Crédito
    • 05: Nota de Débito
    • 06: Guía de Remisión
    • 07: Comprobante de Retención
  • docStatus (string, opcional): Estado del documento
    • AUTORIZADO
    • NO AUTORIZADO
    • DEVUELTA
    • RECIBIDA
    • NO 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 activa
  • 500 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 activa
  • 429 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 usuario
  • accessKey (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 autorizado
  • 404 Not Found: Emisor no registrado o documento no encontrado
  • 429 Too Many Requests: Rate limit excedido
  • 500 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 usuario
  • accessKey (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 autorizado
  • 404 Not Found: Emisor no registrado o documento no encontrado
  • 429 Too Many Requests: Rate limit excedido
  • 500 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:

  1. Revisar el campo mensaje en la respuesta
  2. Revisar el campo informacionAdicional para detalles
  3. Corregir el XML según los errores indicados
  4. 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:

  1. Verificar que la firma electrónica esté actualizada
  2. Revisar los campos mensaje e informacionAdicional
  3. Contactar al soporte de Xipher si el problema persiste

AUTORIZADO

Significado: El documento fue autorizado exitosamente por el SRI.

Acción recomendada:

  • Guardar el numeroAutorizacion y fechaAutorizacion
  • 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:

  1. Verificar que el token esté correctamente formado (Base64 de apiKey:apiSecret)
  2. Verificar que el header Authorization esté presente
  3. Verificar que la cuenta tenga una suscripción activa
  4. 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-After si 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

  1. 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.)
  2. 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
  3. Implementar logging seguro:

    • No registres tokens o credenciales en logs
    • Registra solo información necesaria para debugging
    • Usa niveles de log apropiados
  4. 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

  1. 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;
    }
  2. 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();
    }
  3. 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


Última actualización: 2024