FactuplanSDK
v0.15.0
Postman

Factuplan SDK

Librería oficial de JavaScript y TypeScript para crear facturas electrónicas autorizadas por el SRI de Ecuador. Zero dependencies.

Node.js 18+TypeScriptESM & CJSSin dependencias

Instalación

npm
npm install factuplan
pnpm
pnpm add factuplan
yarn
yarn add factuplan

Autenticación

Crea una API key desde Developer → Crear API key en tu cuenta de Factuplan. Los API keys son a nivel de workspace — debes enviar el header x-taxpayer-ruc en cada petición para identificar el contribuyente a operar.

Inicializar el cliente
import { Factuplan } from 'factuplan';

// Inicializar con RUC del contribuyente (requerido para la mayoría de operaciones)
const factuplan = new Factuplan('ak_test_tu_api_key_aqui', {
  ruc: '0950194407001',   // RUC del contribuyente a operar
});

// Sin RUC (solo para queryExternalByAccessKey u operaciones que no requieren contribuyente)
const factuplan = new Factuplan('ak_test_tu_api_key_aqui');

Nunca expongas tu API key en el frontend. Úsala solo desde tu servidor backend.

Entorno de pruebas (ak_test_...)

Los documentos emitidos con una API key de prueba se eliminan automáticamente cada hora. Esto incluye facturas, notas de crédito, guías de remisión y cualquier otro comprobante generado en modo TESTING. No uses el entorno de pruebas para documentos que necesites conservar.

Clientes

Crear

const cliente = await factuplan.customers.create({
  identificationType: 'RUC',          // RUC | CEDULA | PASSPORT | FINAL_CONSUMER | EXTERIOR
  identification: '0000000000001',    // RUC=13 dígitos | CEDULA=10 dígitos
  legalName: 'Empresa Demo S.A.',
  email: 'contacto@empresa.com',      // opcional
  address: 'Guayaquil, Ecuador',      // opcional
  phone: '+593000000000',             // opcional — ej. +593 EC, +1 US, +44 UK
});

Listar

const { data: clientes, meta } = await factuplan.customers.list({
  page: 1,
  limit: 20,
  search: 'empresa',
});
console.log(meta.total, 'clientes encontrados');

Obtener / Actualizar / Eliminar

const cliente = await factuplan.customers.get('id');

await factuplan.customers.update('id', { email: 'nuevo@email.com' });

await factuplan.customers.delete('id');

Productos

Crear

const producto = await factuplan.products.create({
  code: 'SERV-001',
  auxiliaryCode: 'SERV-001',  // opcional — por defecto toma el mismo valor que code
  name: 'Servicio de consultoría',
  unitPrice: 150.00,
  type: 'SERVICE',          // PRODUCT | SERVICE
  taxType: 'IVA_RATE',      // IVA_0 | IVA_RATE | NOT_TAXABLE | EXEMPT
  description: 'Hora de consultoría técnica',
});

Listar / Actualizar / Eliminar

const { data: productos } = await factuplan.products.list({ search: 'consultoría' });

await factuplan.products.update('id', { unitPrice: 175.00 });

await factuplan.products.delete('id');

Facturas

Método SDKDescripción
invoices.create(input)Crear factura electrónica completa
invoices.list(params)Buscar y listar facturas con filtros
invoices.get(id)Obtener detalle del comprobante
invoices.getStatus(id)Consultar estado de procesamiento
invoices.downloadXml(id)Descargar XML autorizado (URL pre-firmada)
invoices.downloadPdf(id)Descargar PDF RIDE (URL pre-firmada)
invoices.importByAccessKey(input)Importar factura existente desde el SRI
invoices.queryExternalByAccessKey(input)Consultar comprobante externo por clave
invoices.void(id, reason)Anular comprobante
invoices.updateSequential(branch, point, seq)Cambiar secuencial del punto de emisión

Crear factura

Crea una factura electrónica completa. El sistema genera el XML, lo firma, lo envía al SRI, genera el PDF y envía el email al cliente. Usa sendEmail: false para omitir el correo.

Parámetros destacados

CampoTipoDefaultDescripción
sendEmailbooleantrueSi false, el PDF se genera pero no se envía correo al cliente.
const factura = await factuplan.invoices.create({
  // Punto de emisión: elige UNA de estas 3 opciones
  // Opción 1: por UUID
  emissionPointId: 'ep_id',
  // Opción 2: por códigos SRI
  // establishment: '001',
  // emissionPoint: '001',
  // Opción 3: omitir todo (auto-detecta si solo hay un punto de emisión activo)

  customer: {
    identificationType: 'RUC',
    identification: '0000000000001',    // RUC=13 dígitos | CEDULA=10 dígitos
    legalName: 'Cliente S.A.',
    email: 'cliente@email.com',
    saveToContacts: true,
  },
  items: [
    {
      code: 'SERV-001',
      auxiliaryCode: 'SERV-001',  // opcional — por defecto toma el mismo valor que code
      description: 'Servicio de consultoría',
      quantity: 1,
      unitPrice: 1.00,
      discount: 0,
      taxType: 'IVA_RATE',  // IVA_0 | IVA_RATE | NOT_TAXABLE | EXEMPT
      tax: 15,              // Tarifa IVA: 0, 5, 8, 12, 14, 15 (default: 15)
    },
  ],
  payments: [
    {
      method: '20',
      amount: 1.15,
      term: 30,
      timeUnit: 'dias',
    },
  ],
  additionalInfo: { 'Vendedor': 'Juan Pérez' },
  // sendEmail: false,  // opcional: omite el envío de correo al cliente (default: true)
});

console.log(factura.id);         // ID del comprobante
console.log(factura.accessKey);  // Clave de acceso (49 dígitos)
console.log(factura.sequential); // Número secuencial

Punto de emisión

El punto de emisión se puede resolver de tres formas. Si no se envía ningún parámetro, el sistema auto-detecta el único punto de emisión activo del contribuyente.

Por códigos SRI
// Usar códigos SRI del establecimiento y punto de emisión
const factura = await factuplan.invoices.create({
  establishment: '001',
  emissionPoint: '001',
  customer: { ... },
  items: [ ... ],
});
Auto-detección
// Si solo tienes un punto de emisión activo, no necesitas enviarlo
const factura = await factuplan.invoices.create({
  customer: { ... },
  items: [ ... ],
});

Tipos de Impuesto (taxType)

Define el tipo de impuesto aplicable a cada ítem. Los valores posibles son:

taxTypeDescripción
IVA_RATEAplica una tarifa de IVA específica (usar campo tax).
IVA_0Aplica tarifa 0% (Código SRI 0).
NOT_TAXABLENo aplica IVA (Código SRI 6).
EXEMPTExento de IVA (Código SRI 7).

Tarifas de IVA

El campo tax permite especificar la tarifa de IVA cuando usas taxType: 'IVA_RATE'. Si no se envía, se aplica 15% por defecto.

taxCódigo SRIDescripción
00IVA 0%
55IVA 5%
88IVA 8%
122IVA 12%
143IVA 14%
154IVA 15% (default)
Ejemplo con IVA 5%
items: [
  {
    code: 'PROD-001',
    description: 'Producto con IVA reducido',
    quantity: 1,
    unitPrice: 100.00,
    taxType: 'IVA_RATE',
    tax: 5,  // IVA 5% en lugar del 15% por defecto
  },
]

Formas de pago

Usa el arreglo payments para especificar una o varias formas de pago. Cada objeto debe incluir el method (código SRI), amount (monto total), y opcionalmente term y timeUnit. Si no se envía el arreglo, se aplica '01' (Sin utilización del sistema financiero) por defecto.

Importante

La suma de los campos amount en todas las formas de pago enviadas debe ser exactamente igual al total de la factura (incluyendo impuestos). Si los montos no coinciden, la petición será rechazada con un error 400 Bad Request.

CódigoDescripción
01Sin utilización del sistema financiero
15Compensación de deudas
16Tarjeta de débito
17Dinero electrónico
18Tarjeta prepago
19Tarjeta de crédito
20Otros con utilización del sistema financiero
21Endoso de títulos
Unidad de tiempo (timeUnit)Descripción
diasDías
mesesMeses
aniosAños

Redondeo y Precisión

Factuplan utiliza reglas de redondeo automáticas para cumplir con los estándares del SRI manteniendo la mayor precisión en los cálculos.

Precios Unitarios

Puedes enviar el campo unitPrice con hasta 4 decimales. Esto es ideal para productos con costos unitarios bajos donde la precisión es crítica.

Totales e Impuestos

Todos los totales finales (subtotal, IVA, total factura) se redondean automáticamente a 2 decimales usando el estándar del SRI.

Escenarios de Redondeo

1. Multiplicación de precisión:

Cantidad: 1000 × Precio: 0.1234 = Subtotal: 123.40

2. Redondeo de IVA (15%):

Base Imponible: 10.05 × 0.15 = 1.5075 → IVA: 1.51

Total a pagar (Base + IVA): 11.56

Buscar facturas

Lista las facturas emitidas con paginación. Filtra por RUC del cliente, estado y rango de fechas.

const result = await factuplan.invoices.list({
  status: 'AUTHORIZED',
  dateFrom: '2025-01-01',
  dateTo: '2025-12-31',
  page: 1,
  limit: 20,
});

result.data.forEach((inv) => {
  console.log(inv.sequential, inv.total, inv.customer.legalName);
});

// result.meta: { total, page, limit, totalPages }
ParámetroTipoDescripción
rucstringFiltrar por RUC o cédula del cliente
statusstringEstado: DRAFT | PENDING | PROCESSING | AUTHORIZED | REJECTED | VOIDED
dateFromstringFecha de inicio en formato ISO 8601 (ej. "2025-01-01")
dateTostringFecha de fin en formato ISO 8601 (ej. "2025-12-31")
pagenumberNúmero de página (por defecto: 1)
limitnumberResultados por página (por defecto: 20, máximo: 100)

Consultar estado

const estado = await factuplan.invoices.getStatus('factura_id');
// estado.status: PROCESSING | AUTHORIZED | COMPLETED | ERROR | REJECTED

if (estado.status === 'COMPLETED') {
  console.log(estado.authorizationNumber);
}

Descargar XML y PDF

URLs pre-firmadas que expiran en 5 minutos.

const { url: xmlUrl } = await factuplan.invoices.downloadXml('id');
const { url: pdfUrl } = await factuplan.invoices.downloadPdf('id');

Importar por clave de acceso

Importa una factura ya autorizada por el SRI usando su clave de acceso de 49 dígitos. Útil para sincronizar comprobantes emitidos fuera de Factuplan.

Solo API key de producción (ak_live_...). Esta función consulta el SRI en tiempo real y no está disponible con claves de prueba (ak_test_...).
const factura = await factuplan.invoices.importByAccessKey({
  accessKey: '0104202601099337815000110010010000000011234567890',
});

console.log(factura.id);
console.log(factura.sequential);
POST/developer/receipts/query

Consultar comprobante externo por clave de acceso

Consulta un comprobante ya autorizado por el SRI emitido desde cualquier sistema de facturación. El contribuyente emisor se crea automáticamente en tu workspace (con firma vacía) si no existe. Soporta Facturas, Notas de Crédito y Guías de Remisión.

Solo API key de producción (ak_live_...). Esta función consulta el SRI en tiempo real y no está disponible con claves de prueba (ak_test_...).

Parámetros

CampoTipoRequeridoDescripción
accessKeystringClave de acceso de 49 dígitos del SRI
savebooleanNo (default: true)Si false, no guarda registros en la base de datos ni genera PDF. El contador sí se descuenta si el comprobante está autorizado.
Nota sobre el contador: El crédito se descuenta siempre que el comprobante esté autorizado en el SRI, independientemente del valor de save. Si el comprobante no está autorizado, no se descuenta ningún crédito.
// Consultar y guardar (comportamiento por defecto)
const comprobante = await factuplan.invoices.queryExternalByAccessKey({
  accessKey: '0104202601019999999900110010010000000011234567813',
});

console.log(comprobante.id);         // ID del documento guardado
console.log(comprobante.status);     // 'AUTHORIZED'
console.log(comprobante.xmlBase64);  // XML en base64

// Solo verificar sin guardar (save=false)
const verificado = await factuplan.invoices.queryExternalByAccessKey({
  accessKey: '0104202601019999999900110010010000000011234567813',
  save: false,
});

console.log(verificado.id);          // null (no guardado)
console.log(verificado.xmlBase64);   // XML autorizado en base64

Ejemplo completo

ejemplo-factura.ts
import { Factuplan } from 'factuplan';

const factuplan = new Factuplan(process.env.FACTUPLAN_API_KEY!);

async function crearFactura() {
  // 1. Buscar o crear cliente
  const { data: clientes } = await factuplan.customers.list({
    search: '0993378150001',
  });
  let cliente = clientes[0] ?? await factuplan.customers.create({
    identificationType: 'RUC',
    identification: '0993378150001',
    legalName: 'Mi Cliente S.A.',
    email: 'cliente@email.com',
  });

  // 2. Crear factura (sin emissionPointId → auto-detecta)
  const factura = await factuplan.invoices.create({
    customer: {
      identificationType: cliente.identificationType,
      identification: cliente.identification,
      legalName: cliente.legalName,
      email: cliente.email,
    },
    items: [
      {
        code: 'PROD-001',
        description: 'Laptop Dell XPS 15',
        quantity: 1,
        unitPrice: 1500.00,
        taxType: 'IVA_RATE',
        tax: 15,
      },
    ],
    payments: [
      {
        method: '19',
        amount: 1725.00, // Total con IVA
        term: 6,
        timeUnit: 'meses',
      },
    ],
  });

  // 3. Esperar autorización
  let estado;
  do {
    await new Promise((r) => setTimeout(r, 3000));
    estado = await factuplan.invoices.getStatus(factura.id);
  } while (!['COMPLETED', 'ERROR', 'REJECTED'].includes(estado.status));

  if (estado.status === 'COMPLETED') {
    const { url } = await factuplan.invoices.downloadPdf(factura.id);
    console.log('PDF:', url);
  }
}

crearFactura();

Firmar y autorizar XML

Si ya generas tu propio XML de factura (sin firmar), puedes enviarlo a Factuplan para que lo firme con tu certificado electrónico y lo autorice ante el SRI.

Importante: El XML debe enviarse sin firmar. Factuplan se encarga de la firma electrónica.

const result = await factuplan.invoices.signAndAuthorize({
  xml: xmlString, // XML sin firmar como string
});

console.log(result.id);        // ID del comprobante
console.log(result.accessKey); // Clave de acceso SRI
console.log(result.status);    // Estado inicial

// Esperar autorización
let estado;
do {
  await new Promise(r => setTimeout(r, 3000));
  estado = await factuplan.invoices.getStatus(result.id);
} while (!['COMPLETED', 'ERROR', 'REJECTED'].includes(estado.status));

Notas de crédito

Corrige total o parcialmente una factura ya autorizada. Se identifica la factura original por su clave de acceso de 49 dígitos. Si no existe en el sistema, se importa automáticamente desde el SRI.

Método SDKDescripción
creditNotes.create(input)Emitir nota de crédito
creditNotes.list(params)Buscar y listar notas de crédito con filtros
creditNotes.get(id)Obtener detalle del comprobante
creditNotes.getStatus(id)Consultar estado de procesamiento
creditNotes.downloadXml(id)Descargar XML autorizado (URL pre-firmada)
creditNotes.downloadPdf(id)Descargar PDF RIDE (URL pre-firmada)
creditNotes.void(id, reason)Anular nota de crédito

Emitir nota de crédito

const creditNote = await factuplan.creditNotes.create({
  invoiceAccessKey: '2312202301179214289100110010020000001231234567811',
  reason: 'Error en datos del cliente',
  items: [
    {
      code: 'SERV-001',
      description: 'Servicio de desarrollo web',
      quantity: 1,
      unitPrice: 100.00,
      taxType: 'IVA_RATE',
    },
  ],
});

console.log(creditNote.id);        // 'clx1234...'
console.log(creditNote.accessKey); // '0412202404...'
console.log(creditNote.status);    // 'PROCESSING'

Cuerpo de la petición

CampoTipoReq.Descripción
invoiceAccessKeystringClave de acceso de 49 dígitos de la factura original
reasonstringMotivo de la nota de crédito
itemsarrayÍtems a acreditar (misma estructura que en facturas)
emissionPointIdstringNoUUID del punto de emisión (hereda el de la factura si se omite)
paymentsarrayNoMétodos de pago
additionalInfoobjectNoCampos adicionales clave-valor

Respuesta 201

CampoTipoDescripción
idstringID de la nota de crédito
accessKeystringClave de acceso de 49 dígitos
sequentialstringSecuencial (ej: "000000001")
statusstring"PROCESSING" — se autoriza de forma asíncrona
totalnumberImporte total de la nota de crédito

Consultar detalle

const creditNote = await factuplan.creditNotes.get('CREDIT_NOTE_ID');

console.log(creditNote.id);
console.log(creditNote.accessKey);
console.log(creditNote.status);
console.log(creditNote.total);

Consultar estado

const estado = await factuplan.creditNotes.getStatus('CREDIT_NOTE_ID');
// estado.status: PROCESSING | AUTHORIZED | COMPLETED | ERROR | REJECTED

if (estado.status === 'COMPLETED') {
  console.log(estado.authorizationNumber);
}

Descargar XML y PDF

const { url: xmlUrl } = await factuplan.creditNotes.downloadXml('CREDIT_NOTE_ID');
const { url: pdfUrl } = await factuplan.creditNotes.downloadPdf('CREDIT_NOTE_ID');

Buscar notas de crédito

Lista las notas de crédito emitidas con paginación. Filtra por RUC del cliente, estado y rango de fechas.

const result = await factuplan.creditNotes.list({
  status: 'AUTHORIZED',
  dateFrom: '2025-01-01',
  dateTo: '2025-12-31',
  page: 1,
  limit: 20,
});

result.data.forEach((nc) => {
  console.log(nc.sequential, nc.total, nc.customer.legalName);
});

// result.meta: { total, page, limit, totalPages }
ParámetroTipoDescripción
rucstringFiltrar por RUC o cédula del cliente
statusstringEstado: DRAFT | PENDING | PROCESSING | AUTHORIZED | REJECTED | VOIDED
dateFromstringFecha de inicio en formato ISO 8601 (ej. "2025-01-01")
dateTostringFecha de fin en formato ISO 8601 (ej. "2025-12-31")
pagenumberNúmero de página (por defecto: 1)
limitnumberResultados por página (por defecto: 20, máximo: 100)

Firmar y autorizar XML

Si ya tienes el XML de la nota de crédito (sin firmar), Factuplan lo firma y autoriza ante el SRI. La factura vinculada se resuelve automáticamente desde el XML.

Importante: El XML debe enviarse sin firmar.

const result = await factuplan.creditNotes.signAndAuthorize({
  xml: xmlString,
});

console.log(result.id);
console.log(result.accessKey);
console.log(result.status);

Anular nota de crédito

const result = await factuplan.creditNotes.void(
  'CREDIT_NOTE_ID',
  'Error en los datos del cliente',
);

console.log(result.status);     // "VOIDED"
console.log(result.voidReason); // Motivo ingresado
console.log(result.message);    // Confirmación

La anulación de la nota de crédito ante el SRI es responsabilidad del desarrollador. El endpoint /void solo cambia el estado en Factuplan.

Secuencial

Establece el próximo número secuencial para notas de crédito en el punto de emisión indicado. El nuevo valor entra en efecto en el siguiente comprobante emitido.

const result = await factuplan.creditNotes.updateSequential(
  '001', // branchCode — código del establecimiento
  '001', // emissionCode — código del punto de emisión
  100,   // creditNoteSequential — nuevo valor del secuencial
);

console.log(result.creditNoteSequential); // 100
console.log(result.updatedAt);            // '2026-06-08T12:00:00.000Z'
CampoTipoDescripción
branchCodestringCódigo del establecimiento
emissionCodestringCódigo del punto de emisión
creditNoteSequentialnumberNuevo valor del secuencial aplicado
updatedAtstringFecha de actualización (ISO 8601)

Notas de débito

Registra cargos adicionales sobre una factura ya autorizada: ajustes de precio, mora, flete no facturado, etc. Se identifica la factura original por su clave de acceso de 49 dígitos.

Método SDKDescripción
debitNotes.create(input)Emitir nota de débito
debitNotes.list(params)Buscar y listar notas de débito con filtros
debitNotes.get(id)Obtener detalle del comprobante
debitNotes.getStatus(id)Consultar estado de procesamiento
debitNotes.downloadXml(id)Descargar XML autorizado (URL pre-firmada)
debitNotes.downloadPdf(id)Descargar PDF RIDE (URL pre-firmada)
debitNotes.void(id, reason)Anular nota de débito

Emitir nota de débito

const debitNote = await factuplan.debitNotes.create({
  invoiceAccessKey: '2312202301179214289100110010020000001231234567811',
  reasons: [
    {
      description: 'Ajuste por flete no facturado',
      amount: 25.00,
      taxType: 'IVA',  // IVA | IVA_0 | NO_TAX | ICE | IRBPNR | IVA_EXEMPT
    },
  ],
  // additionalInfo: { Referencia: 'OC-4521' },  // opcional
});

console.log(debitNote.id);        // 'clx1234...'
console.log(debitNote.accessKey); // '0512202404...'
console.log(debitNote.status);    // 'PROCESSING'

Cuerpo de la petición

CampoTipoReq.Descripción
invoiceAccessKeystringClave de acceso de 49 dígitos de la factura original
reasonsarrayMotivos / cargos (al menos uno requerido)
reasons[].descriptionstringDescripción del cargo o ajuste
reasons[].amountnumberImporte del cargo (mínimo 0.01)
reasons[].taxTypestringTipo de impuesto: IVA | IVA_0 | NO_TAX | ICE | IRBPNR | IVA_EXEMPT
emissionPointIdstringNoUUID del punto de emisión (auto-detecta si se omite)
additionalInfoobjectNoCampos adicionales clave-valor

Buscar notas de débito

const { data: debitNotes, meta } = await factuplan.debitNotes.list({
  status: 'COMPLETED',
  page: 1,
  limit: 20,
});

Consultar estado

const estado = await factuplan.debitNotes.getStatus('DEBIT_NOTE_ID');
// estado.status: PROCESSING | AUTHORIZED | COMPLETED | ERROR | REJECTED

if (estado.status === 'COMPLETED') {
  console.log(estado.authorizationNumber);
}

Descargar XML y PDF

const { url: xmlUrl } = await factuplan.debitNotes.downloadXml('DEBIT_NOTE_ID');
const { url: pdfUrl } = await factuplan.debitNotes.downloadPdf('DEBIT_NOTE_ID');

Firmar y autorizar XML

Si ya tienes el XML de la nota de débito (sin firmar), Factuplan lo firma y autoriza ante el SRI. La factura vinculada se resuelve automáticamente desde el XML.

Importante: El XML debe enviarse sin firmar.

const result = await factuplan.debitNotes.signAndAuthorize({
  xml: xmlString,
});

console.log(result.id);
console.log(result.accessKey);
console.log(result.status);

Anular

const voided = await factuplan.debitNotes.void('DEBIT_NOTE_ID', 'Emitida por error');

console.log(voided.status); // 'VOIDED'

La anulación de la nota de débito ante el SRI es responsabilidad del desarrollador. El endpoint /void solo cambia el estado en Factuplan.

Secuencial

Establece el próximo número secuencial para notas de débito en el punto de emisión indicado. El nuevo valor entra en efecto en el siguiente comprobante emitido.

const result = await factuplan.debitNotes.updateSequential(
  '001', // branchCode — código del establecimiento
  '001', // emissionCode — código del punto de emisión
  100,   // debitNoteSequential — nuevo valor del secuencial
);

console.log(result.debitNoteSequential); // 100
console.log(result.updatedAt);           // '2026-06-08T12:00:00.000Z'
CampoTipoDescripción
branchCodestringCódigo del establecimiento
emissionCodestringCódigo del punto de emisión
debitNoteSequentialnumberNuevo valor del secuencial aplicado
updatedAtstringFecha de actualización (ISO 8601)

Retenciones

Emite comprobantes de retención en la fuente (renta, IVA e ISD) vinculados a una factura ya autorizada, identificada por su clave de acceso de 49 dígitos. El sistema reserva el secuencial, genera el XML, lo firma y lo autoriza ante el SRI de forma asíncrona.

Método SDKDescripción
withholdings.create(input)Emitir comprobante de retención
withholdings.list(params)Buscar y listar retenciones con filtros
withholdings.get(id)Obtener detalle del comprobante
withholdings.getStatus(id)Consultar estado de procesamiento
withholdings.downloadXml(id)Descargar XML autorizado (URL pre-firmada)
withholdings.downloadPdf(id)Descargar PDF RIDE (URL pre-firmada)
withholdings.void(id, reason)Anular retención

Emitir retención

const withholding = await factuplan.withholdings.create({
  invoiceAccessKey: '2312202301179214289100110010020000001231234567811',
  taxes: [
    {
      taxType: 'RENTA',          // 'RENTA' | 'IVA' | 'ISD'
      taxRateCode: '303',        // Código SRI (Tabla 25)
      taxRate: 10,               // Porcentaje de retención
      taxBase: 1000.00,          // Base imponible
      fiscalPeriod: '06/2026',   // Período fiscal MM/YYYY (opcional)
    },
    {
      taxType: 'IVA',
      taxRateCode: '725',
      taxRate: 30,
      taxBase: 150.00,
    },
  ],
  additionalInfo: { observacion: 'Servicios profesionales' }, // opcional
});

console.log(withholding.id);        // 'clx1234...'
console.log(withholding.accessKey); // '0512202404...'
console.log(withholding.status);    // 'PROCESSING'

Buscar retenciones

const { data: withholdings, meta } = await factuplan.withholdings.list({
  status: 'AUTHORIZED',
  dateFrom: '2026-01-01',
  dateTo: '2026-06-30',
  page: 1,
  limit: 20,
});

Consultar estado

const estado = await factuplan.withholdings.getStatus('WITHHOLDING_ID');

console.log(estado.status);              // 'AUTHORIZED'
console.log(estado.authorizationNumber); // Número de autorización SRI
console.log(estado.authorizationDate);   // Fecha de autorización

Descargar XML / PDF

const { url: xmlUrl } = await factuplan.withholdings.downloadXml('WITHHOLDING_ID');
const { url: pdfUrl } = await factuplan.withholdings.downloadPdf('WITHHOLDING_ID');

Firmar y autorizar XML

Si ya tienes el XML de la retención (sin firmar), Factuplan lo firma y autoriza ante el SRI. La factura vinculada se resuelve automáticamente desde el XML.

Importante: El XML debe enviarse sin firmar.

const result = await factuplan.withholdings.signAndAuthorize({
  xml: xmlString,
});

console.log(result.id);
console.log(result.accessKey);
console.log(result.status);

Anular

const voided = await factuplan.withholdings.void('WITHHOLDING_ID', 'Emitida por error');

console.log(voided.status); // 'VOIDED'

La anulación de la retención ante el SRI es responsabilidad del desarrollador. El endpoint /void solo cambia el estado en Factuplan.

Guías de remisión

Emite guías de remisión electrónicas para documentar el traslado de mercadería. El sistema genera el XML, lo firma con tu certificado y lo autoriza ante el SRI de forma asíncrona.

Método SDKDescripción
waybills.create(input)Emitir guía de remisión
waybills.list(params)Buscar y listar guías con filtros
waybills.get(id)Obtener detalle de la guía
waybills.getStatus(id)Consultar estado de procesamiento
waybills.downloadXml(id)Descargar XML autorizado (URL pre-firmada)
waybills.downloadPdf(id)Descargar PDF RIDE (URL pre-firmada)

Emitir guía de remisión

const guia = await factuplan.waybills.create({
  emissionPointId: 'EMISSION_POINT_ID',
  customer: {
    identificationType: 'RUC',
    identification: '1790012345001',
    legalName: 'Empresa Destino S.A.',
    email: 'destino@empresa.com',
  },
  transporterRuc: '1790012345001',
  transporterName: 'Transportes del Norte S.A.',
  vehiclePlate: 'ABC-1234',
  departureAddress: 'Av. 10 de Agosto N23-45, Quito',
  transportStartDate: '2026-06-10',
  transportEndDate: '2026-06-11',
  items: [
    {
      code: 'PROD-001',
      description: 'Caja de mercadería',
      quantity: 10,
    },
  ],
  destinations: [
    {
      receiverIdentification: '0950194407001',
      receiverName: 'Cliente Final',
      address: 'Av. del Ejército 123, Guayaquil',
      transferReason: 'Venta de mercadería',
    },
  ],
});

console.log(guia.id);        // 'clx1234...'
console.log(guia.accessKey); // '0606202604...'
console.log(guia.status);    // 'PROCESSING'
CampoTipoRequeridoDescripción
emissionPointIdstringID del punto de emisión
customerobjectDestinatario (ID existente o datos inline)
transporterRucstringRUC del transportista
transporterNamestringNombre del transportista
vehiclePlatestringPlaca del vehículo (ej. ABC-1234)
departureAddressstringDirección de inicio del transporte
transportStartDatestringFecha inicio transporte (ISO 8601)
transportEndDatestringFecha fin transporte (ISO 8601)
itemsarrayMercadería transportada
destinationsarrayNoDestinos del traslado
additionalInfoobjectNoInformación adicional clave-valor

Buscar guías

const { data: guias, meta } = await factuplan.waybills.list({
  status: 'AUTHORIZED',
  dateFrom: '2026-01-01',
  dateTo: '2026-06-30',
  page: 1,
  limit: 20,
});

Consultar estado

const estado = await factuplan.waybills.getStatus('WAYBILL_ID');

console.log(estado.status);              // 'AUTHORIZED'
console.log(estado.authorizationNumber); // Número de autorización SRI
console.log(estado.authorizationDate);   // Fecha de autorización

Descargar XML / PDF

const { url: xmlUrl } = await factuplan.waybills.downloadXml('WAYBILL_ID');
const { url: pdfUrl } = await factuplan.waybills.downloadPdf('WAYBILL_ID');

Anular

const voided = await factuplan.waybills.void('WAYBILL_ID', 'Emitida por error');

console.log(voided.status); // 'VOIDED'

La anulación de la guía ante el SRI es responsabilidad del desarrollador. El endpoint /void solo cambia el estado en Factuplan.

Firma & Certificados

Manejo de errores

import { FactuplanError, AuthenticationError } from 'factuplan';

try {
  await factuplan.invoices.create({ ... });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('API key inválida');
  } else if (error instanceof FactuplanError) {
    console.error(error.code);       // "INVOICE_4002"
    console.error(error.message);    // Descripción
    console.error(error.statusCode); // 422
    console.error(error.details);    // Detalles adicionales
  }
}

Códigos de error

CódigoHTTPDescripción
AUTH_ERROR401API key inválida o expirada
RATE_LIMIT429Límite de solicitudes excedido
API_10001403Plan no compatible con la operación
API_10002429Cuota mensual excedida
INVOICE_4002422Datos del cliente inválidos
INVOICE_4003422Detalle de factura vacío
CERT_3008422Certificado expirado
CUSTOMER_7004409Cliente duplicado
PRODUCT_6002409Código de producto duplicado

Rate Limit

Cada API key tiene un límite de velocidad y una cuota mensual para garantizar un servicio estable para todos los usuarios.

Límite de velocidad

100

peticiones por segundo por API key

Cuota mensual

Según plan

documentos emitidos por mes

Al superar cualquiera de los dos límites recibirás un HTTP 429. Implementa reintentos con espera exponencial para manejarlos correctamente.

Respuesta al superar el límite

// HTTP 429 — Rate limit de velocidad (100 req/s)
{
  "success": false,
  "error": {
    "code": "GENERAL_0003",
    "message": "Has superado el límite de peticiones. Intenta de nuevo en unos segundos."
  }
}

// HTTP 429 — Cuota mensual agotada
{
  "success": false,
  "error": {
    "code": "API_10002",
    "message": "Has alcanzado el límite mensual de 500 documentos.",
    "details": { "used": 500, "quota": 500 }
  }
}

Manejo recomendado con reintentos

import { RateLimitError } from 'factuplan';

async function emitirConReintento(datos: any, intentos = 3) {
  for (let i = 0; i < intentos; i++) {
    try {
      return await factuplan.invoices.create(datos);
    } catch (error) {
      if (error instanceof RateLimitError && i < intentos - 1) {
        const espera = Math.pow(2, i) * 500; // 500ms, 1s, 2s...
        console.log(`Rate limit — reintentando en ${espera}ms`);
        await new Promise(r => setTimeout(r, espera));
      } else {
        throw error;
      }
    }
  }
}

Webhooks

Antes de procesar un evento webhook, verifica que el comprobante exista y consulta su estado actual usando su ID.

Verificar comprobante

const receipt = await factuplan.webhooks.verifyReceipt('receipt_id');

console.log(receipt.id);
console.log(receipt.status);    // PROCESSING | AUTHORIZED | COMPLETED | ERROR | REJECTED
console.log(receipt.accessKey); // Clave de acceso SRI (49 dígitos)

Tip: Llama a este endpoint al recibir un evento webhook para confirmar que el comprobante existe antes de actualizar tu base de datos.

Uso & Límites

Consulta el consumo de API del mes en curso y el límite de tu plan.

Consultar uso

const uso = await factuplan.usage();

console.log(uso.requestsUsed, '/', uso.requestsLimit);
console.log('Costo estimado: $' + uso.estimatedCost);

Respuesta

CampoTipoDescripción
requestsUsednumberSolicitudes usadas este mes
requestsLimitnumberLímite del plan actual
estimatedCostnumberCosto estimado en USD
periodStartstringInicio del período (ISO 8601)
periodEndstringFin del período (ISO 8601)

Certificado

Consulta el estado del certificado electrónico P12 configurado en tu cuenta.

Estado del certificado

const cert = await factuplan.certificateStatus();

console.log(cert.valid);       // true = vigente
console.log(cert.expiresAt);   // Fecha de vencimiento (ISO 8601)
console.log(cert.taxpayerId);  // RUC del titular
console.log(cert.commonName);  // Nombre en el certificado

Respuesta

CampoTipoDescripción
validbooleantrue si el certificado está vigente
expiresAtstringFecha de vencimiento (ISO 8601)
taxpayerIdstringRUC del titular del certificado
commonNamestringNombre registrado en el certificado

Si valid es false, las facturas no podrán firmarse hasta que renueves el certificado en Configuración → Certificado.

Anular comprobante

Anula un comprobante a nivel del sistema. El estado cambia a VOIDED. Solo se pueden anular comprobantes en estado AUTHORIZED o COMPLETED.

const result = await factuplan.invoices.void(
  'ID_DEL_COMPROBANTE',
  'Error en los datos del cliente',
);

console.log(result.status);     // "VOIDED"
console.log(result.voidReason); // Motivo ingresado
console.log(result.message);    // Confirmación

Respuesta

CampoTipoDescripción
idstringID del comprobante anulado
statusstring"VOIDED" (estado anulado)
accessKeystringClave de acceso del comprobante
voidReasonstringMotivo de la anulación
messagestringMensaje de confirmación

La anulación ante el SRI es responsabilidad del desarrollador. Este endpoint solo cambia el estado del comprobante en Factuplan.

Secuencial de facturas

Establece el próximo número secuencial para facturas en un punto de emisión específico. El nuevo valor entra en efecto en el siguiente comprobante emitido.

const result = await factuplan.invoices.updateSequential(
  '001', // branchCode — código del establecimiento
  '001', // emissionCode — código del punto de emisión
  100,   // nuevo secuencial
);

console.log(result.invoiceSequential); // 100
console.log(result.updatedAt);         // ISO 8601

Respuesta

CampoTipoDescripción
branchCodestringCódigo del establecimiento
emissionCodestringCódigo del punto de emisión
invoiceSequentialnumberNuevo valor del secuencial aplicado
updatedAtstringFecha de actualización (ISO 8601)

El cambio afecta únicamente el secuencial de facturas en el punto de emisión indicado. Para actualizar el secuencial de notas de crédito o notas de débito usa sus respectivos endpoints.

Subir firma electrónica (P12)

Sube un certificado P12. Si el RUC del certificado no existe aún como contribuyente en tu cuenta y tu plan lo permite, el contribuyente se crea automáticamente y se vincula a tu API key.

import { readFileSync } from 'fs';

const p12 = readFileSync('./mi-firma.p12');
const result = await factuplan.certificates.upload(p12, 'mi-contraseña');

console.log(result.ruc);       // RUC extraído del certificado
console.log(result.legalName); // Nombre legal
console.log(result.expiresAt); // Fecha de vencimiento
console.log(result.created);   // true si se creó un nuevo contribuyente

Respuesta

CampoTipoDescripción
hasCertificatebooleantrue si el certificado se subió correctamente
isExpiredbooleantrue si el certificado ya expiró
rucstringRUC extraído del certificado
legalNamestringNombre legal extraído del certificado
expiresAtstringFecha de vencimiento (ISO 8601)
createdboolean?true si se creó automáticamente un nuevo contribuyente