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.retry(id)Reintentar una factura que no llegó a autorizarse
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
accessKeystringSíClave 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 en la misma llamada.

Es un solo endpoint para todos los tipos de comprobante: POST /v1/developer/sign-and-authorize. Acepta factura, nota de crédito, nota de débito, guía de remisión, retención y liquidación de compra; no hay una ruta por tipo.

Con una API key de producción necesitas cupo disponible en tu plan: se comprueba antes de firmar, y solo se descuenta un documento si el SRI lo autoriza. Reenviar el mismo comprobante no lo vuelve a descontar. En el entorno de pruebas no consume cupo.

Importante: El XML debe enviarse sin firmar y con su declaración <?xml ...?>. Factuplan se encarga de la firma electrónica.

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

console.log(result.authorized);          // true | false
console.log(result.authorizationNumber); // número de autorización del SRI
console.log(result.authorizationDate);   // fecha de autorización
console.log(result.authorizedXml);       // XML autorizado (base64)
console.log(result.messages);            // mensajes del SRI

if (!result.authorized) {
  // El SRI no lo autorizó: los motivos vienen en result.messages
  // y no se descuenta ningún documento de tu plan.
  console.error(result.messages);
}

Respuesta

La respuesta es la del SRI, tal cual, y llega en la misma llamada. Si el SRI no autoriza el comprobante, authorized viene en false y los motivos están en messages.

CampoTipoDescripción
authorizedbooleanSi el SRI autorizó el comprobante
authorizationNumberstring | nullNúmero de autorización del SRI
authorizationDatestring | nullFecha de autorización del SRI
authorizedXmlstring | nullXML autorizado, en base64
messagesarrayMensajes que devolvió el SRI

Este endpoint no guarda el comprobante en Factuplan: no devuelve un id, no genera PDF ni envía correo, el comprobante no aparece en tu panel y no hay nada que consultar ni descargar después. Guarda el authorizedXml que recibes.

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
creditNotes.retry(id)Reintentar una nota de crédito que no llegó a autorizarse

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
invoiceAccessKeystringSíClave de acceso de 49 dígitos de la factura original
reasonstringSíMotivo de la nota de crédito
itemsarraySíÍ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 lo autoriza ante el SRI en la misma llamada. Es el mismo endpoint que para las facturas (POST /v1/developer/sign-and-authorize): el tipo de comprobante se deduce del nodo raíz del XML.

Con una API key de producción necesitas cupo disponible en tu plan: se comprueba antes de firmar, y solo se descuenta un documento si el SRI lo autoriza. Reenviar el mismo comprobante no lo vuelve a descontar. En el entorno de pruebas no consume cupo.

Importante: El XML debe enviarse sin firmar y con su declaración <?xml ...?>.

// Un solo método para todos los tipos de comprobante: vive en invoices
const result = await factuplan.invoices.signAndAuthorize({
  xml: xmlString, // XML de la nota de crédito, sin firmar
});

console.log(result.authorized);          // true | false
console.log(result.authorizationNumber); // número de autorización del SRI
console.log(result.authorizationDate);   // fecha de autorización
console.log(result.authorizedXml);       // XML autorizado (base64)

if (!result.authorized) {
  console.error(result.messages); // motivos del rechazo del SRI
}

Devuelve la respuesta del SRI y no guarda el comprobante: no hay id, PDF ni correo, y nada que consultar o descargar después. Los campos son los mismos que en Firmar y autorizar XML de facturas.

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.

Reintentar nota de crédito

Vuelve a lanzar el flujo de emisión de una nota de crédito que no llegó a autorizarse. El paso que se reintenta se deduce del estado y llega en action. Los estados admitidos y las advertencias están en Reintentar comprobante.

const result = await factuplan.creditNotes.retry('CREDIT_NOTE_ID');

console.log(result.action);    // "reprocess" | "authorize" | "pdf"
console.log(result.accessKey); // Puede ser una clave NUEVA

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
debitNotes.retry(id)Reintentar una nota de débito que no llegó a autorizarse

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
invoiceAccessKeystringSíClave de acceso de 49 dígitos de la factura original
reasonsarraySíMotivos / cargos (al menos uno requerido)
reasons[].descriptionstringSíDescripción del cargo o ajuste
reasons[].amountnumberSíImporte del cargo (mínimo 0.01)
reasons[].taxTypestringSíTipo 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 lo autoriza ante el SRI en la misma llamada. Es el mismo endpoint que para las facturas (POST /v1/developer/sign-and-authorize): el tipo de comprobante se deduce del nodo raíz del XML.

Con una API key de producción necesitas cupo disponible en tu plan: se comprueba antes de firmar, y solo se descuenta un documento si el SRI lo autoriza. Reenviar el mismo comprobante no lo vuelve a descontar. En el entorno de pruebas no consume cupo.

Importante: El XML debe enviarse sin firmar y con su declaración <?xml ...?>.

// Un solo método para todos los tipos de comprobante: vive en invoices
const result = await factuplan.invoices.signAndAuthorize({
  xml: xmlString, // XML de la nota de débito, sin firmar
});

console.log(result.authorized);          // true | false
console.log(result.authorizationNumber); // número de autorización del SRI
console.log(result.authorizationDate);   // fecha de autorización
console.log(result.authorizedXml);       // XML autorizado (base64)

if (!result.authorized) {
  console.error(result.messages); // motivos del rechazo del SRI
}

Devuelve la respuesta del SRI y no guarda el comprobante: no hay id, PDF ni correo, y nada que consultar o descargar después. Los campos son los mismos que en Firmar y autorizar XML de facturas.

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.

Reintentar nota de débito

Vuelve a lanzar el flujo de emisión de una nota de débito que no llegó a autorizarse. El paso que se reintenta se deduce del estado y llega en action. Los estados admitidos y las advertencias están en Reintentar comprobante.

const result = await factuplan.debitNotes.retry('DEBIT_NOTE_ID');

console.log(result.action);    // "reprocess" | "authorize" | "pdf"
console.log(result.accessKey); // Puede ser una clave NUEVA

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) sobre una compra registrada — el documento que te emitió tu proveedor — 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.

Cambio importante (SDK 0.18.0): la retención ya no se emite sobre una factura propia, sino sobre la compra que sustenta el gasto — una retención se declara contra el comprobante que recibiste, no contra uno que emitiste. La compra debe existir previamente en Factuplan, identificada por su clave de acceso, y se referencia como purchaseAccessKey. El campo invoiceAccessKey queda eliminado.

Limitación actual: registrar una compra solo es posible desde el panel de Factuplan, por su clave de acceso — todavía no existe un endpoint público para hacerlo. En la práctica, una integración por API key solo puede emitir retenciones sobre compras que un usuario ya haya registrado desde el panel.

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
withholdings.retry(id)Reintentar una retención que no llegó a autorizarse

Emitir retención

const withholding = await factuplan.withholdings.create({
  purchaseAccessKey: '0109202601179214673900110020010000000011234567815', // clave de la COMPRA
  codSustento: '01',         // Código de sustento (Tabla 5 del SRI)
  pagoLocExt: '01',          // '01' local | '02' exterior (opcional, default '01')
  fiscalPeriod: '09/2026',   // Período fiscal MM/AAAA (opcional; por defecto, el mes de la compra)
  taxes: [
    {
      taxType: 'IVA',             // 'RENTA' | 'IVA' | 'ISD'
      taxRateCode: '9',           // Código SRI (tabla 20): '9' = 10% de IVA
      taxRate: 10,                // Porcentaje de retención
      taxBase: 12.23,             // Base imponible
    },
    {
      taxType: 'RENTA',
      taxRateCode: '303',
      taxRate: 10,
      taxBase: 101.94,
    },
  ],
  additionalInfo: { observacion: 'Servicios profesionales' }, // opcional
});

console.log(withholding.id);            // 'clx1234...'
console.log(withholding.accessKey);     // '0512202404...'
console.log(withholding.status);        // 'DRAFT'
console.log(withholding.totalWithheld); // Total retenido
console.log(withholding.purchaseId);    // Compra que sustenta la retención

Cuerpo de la petición

CampoTipoReq.Descripción
purchaseAccessKeystringSíClave de acceso de 49 dígitos de la compra registrada que sustenta la retención
codSustentostringSíCódigo de sustento del comprobante (tabla 5 del SRI). Ver "Códigos del SRI"
taxesarraySíLíneas de retención (mínimo una): taxType, taxRateCode, taxRate, taxBase
taxes[].taxTypestringSí'RENTA' | 'IVA' | 'ISD'
taxes[].taxRateCodestringSíCódigo de retención del SRI según el taxType (ver "Códigos del SRI"). En IVA el código no es el porcentaje
taxes[].taxRatenumberSíPorcentaje a retener. Debe coincidir con el parametrizado por el SRI para ese código
taxes[].taxBasenumberSíBase imponible; no puede superar el total de la compra
pagoLocExtstringNoTipo de pago: '01' local (por defecto) o '02' exterior
fiscalPeriodstringNoPeríodo fiscal MM/AAAA. Por defecto, el mes de emisión de la compra
emissionPointIdstringNoUUID del punto de emisión (auto-resuelto si el contribuyente tiene uno solo activo)
additionalInfoobjectNoCampos adicionales clave-valor

Respuesta 201

CampoTipoDescripción
idstringID de la retención
accessKeystringClave de acceso de 49 dígitos
sequentialstringSecuencial (ej: "000000001")
statusstring"DRAFT" — se autoriza de forma asíncrona
totalWithheldnumberImporte total retenido
purchaseIdstringID de la compra que sustenta la retención

Códigos del SRI

Estos son los códigos que acepta la API. Salen de las tablas oficiales del SRI: la de renta, de los porcentajes vigentes desde marzo de 2026 (Resolución NAC-DGERCGC26-00000009), y la de IVA, de la ficha técnica de comprobantes electrónicos.

  • Un taxRateCode o codSustento que no esté en estas tablas se rechaza al crear la retención con WH_5308 o WH_5307.
  • La API no valida el porcentaje, pero el SRI sí: si taxRate no coincide con el parametrizado para el código, la retención queda NO AUTORIZADA con el error 52 ("La tarifa no es igual al parametrizado"). Los códigos de tarifa variable (dividendos, banano, autorretenciones) llevan el porcentaje que corresponda a tu caso.
  • En IVA el código no es el porcentaje: "9" es 10% y "1" es 30%.
  • Todavía no se admiten pagos a no residentes (códigos 500 en adelante).
Retención de IVA (taxType: 'IVA') (8 códigos)
CódigoConceptoPorcentaje
9Retención del 10% del IVA10%
10Retención del 20% del IVA20%
1Retención del 30% del IVA30%
11Retención del 50% del IVA50%
2Retención del 70% del IVA70%
3Retención del 100% del IVA100%
7Retención en cero0%
8No procede retención0%
Retención de renta (taxType: 'RENTA') (80 códigos)
CódigoConceptoPorcentaje
303Honorarios profesionales y demás pagos por servicios relacionados con el título profesional10%
303AServicios profesionales prestados por sociedades residentes5%
304Servicios predomina el intelecto no relacionados con el título profesional10%
304AComisiones y demás pagos por servicios predomina intelecto no relacionados con el título profesional10%
304BPagos a notarios y registradores de la propiedad y mercantil por sus actividades ejercidas como tales10%
304CPagos a deportistas, entrenadores, árbitros, miembros del cuerpo técnico por sus actividades ejercidas como tales10%
304DPagos a artistas por sus actividades ejercidas como tales10%
304EHonorarios y demás pagos por servicios de docencia10%
307Servicios predomina la mano de obra3%
308Utilización o aprovechamiento de la imagen o renombre (personas naturales, sociedades, "influencers")10%
309Servicios prestados por medios de comunicación y agencias de publicidad3%
310Servicio de transporte privado de pasajeros o transporte público o privado de carga1%
311Pagos a través de liquidación de compra (nivel cultural o rusticidad)3%
312Transferencia de bienes muebles de naturaleza corporal2%
312ACompras al productor: bienes de origen bioacuático, forestal y los descritos en el art. 27.1 de la LRTI1%
312CCompras al comercializador: bienes de origen bioacuático, forestal y los descritos en el art. 27.1 de la LRTI1.75%
314ARegalías por franquicias según el Código INGENIOS (COESCCI) - pago a personas naturales10%
314BCánones, derechos de autor, marcas, patentes y similares según el Código INGENIOS (COESCCI) - pago a personas naturales10%
314CRegalías por franquicias según el Código INGENIOS (COESCCI) - pago a sociedades10%
314DCánones, derechos de autor, marcas, patentes y similares según el Código INGENIOS (COESCCI) - pago a sociedades10%
319Cuotas de arrendamiento mercantil (prestado por sociedades), inclusive la de opción de compra2%
320Arrendamiento de bienes inmuebles10%
322Seguros y reaseguros (primas y cesiones)2%
323Rendimientos financieros pagados a naturales y sociedades (no a IFIs)3%
323ARendimientos financieros depósitos cuenta corriente3%
323B1Rendimientos financieros depósitos cuenta de ahorros sociedades3%
323ERendimientos financieros depósito a plazo fijo gravados3%
323E2Rendimientos financieros depósito a plazo fijo exentos0%
323FRendimientos financieros operaciones de reporto - repos3%
323GInversiones (captaciones) rendimientos distintos de aquellos pagados a IFIs3%
323HRendimientos financieros obligaciones3%
323IRendimientos financieros bonos convertibles en acciones3%
323QOtros intereses y rendimientos financieros gravados3%
323ROtros intereses y rendimientos financieros exentos0%
323SPagos y créditos en cuenta efectuados por el BCE y los depósitos centralizados de valores a instituciones del sistema financiero por cuenta de otras personas naturales y sociedades3%
323TRendimientos financieros originados en la deuda pública ecuatoriana0%
323URendimientos financieros de títulos valores de obligaciones de 360 días o más para proyectos públicos en asociación público-privada0%
324AIntereses en operaciones de crédito entre instituciones del sistema financiero y entidades de la economía popular y solidaria2%
324BInversiones entre instituciones del sistema financiero y entidades de la economía popular y solidaria2%
324CPagos y créditos en cuenta efectuados por el BCE y los depósitos centralizados de valores a instituciones del sistema financiero por cuenta de otras instituciones del sistema financiero2%
325Anticipo dividendos25%
325APréstamos a accionistas, beneficiarios o partícipes residentes o establecidos en el Ecuador25%
3250Dividendos exentos (por no llegar a franja exenta o beneficio de otras leyes)0%
326Dividendos distribuidos que correspondan al impuesto a la renta único del art. 27 de la LRTIvariable
327Dividendos distribuidos a personas naturales residentesvariable
328Dividendos distribuidos a sociedades residentes0%
329Dividendos distribuidos a fideicomisos residentes0%
331Dividendos en acciones (capitalización de utilidades)0%
332Otras compras de bienes y servicios no sujetas a retención (incluye RIMPE - Negocios Populares)0%
332BCompra de bienes inmuebles0%
332CTransporte público de pasajeros0%
332DPagos en el país por transporte de pasajeros o transporte internacional de carga, a compañías nacionales o extranjeras de aviación o marítimas0%
332EValores entregados por las cooperativas de transporte a sus socios0%
332FCompraventa de divisas distintas al dólar de los Estados Unidos de América0%
332GPagos con tarjeta de crédito0%
332HPago al exterior con tarjeta de crédito reportada por la emisora de tarjeta de crédito, solo recap0%
332IPago a través de convenio de débito (clientes IFIs)0%
333Ganancia en la enajenación de derechos representativos de capital u otros derechos de sociedades que se coticen en bolsa de valores del Ecuador10%
334Contraprestación por la enajenación de derechos representativos de capital u otros derechos de sociedades no cotizados en bolsa de valores del Ecuador2%
335Loterías, rifas, pronósticos deportivos, apuestas y similares15%
336Venta de combustibles a comercializadoras (2 por mil)0.2%
337Venta de combustibles a distribuidores (3 por mil)0.3%
338Producción y venta local de banano producido o no por el mismo sujeto pasivovariable
340Impuesto único a la exportación de banano3%
343Otras retenciones aplicables el 1% (incluye RIMPE - Emprendedores)1%
343AEnergía eléctrica2%
343BActividades de construcción de obra material inmueble, urbanización, lotización o actividades similares2%
343CRecepción de botellas plásticas no retornables de PET2%
3440Otras retenciones aplicables el 3%3%
344APago local con tarjeta de crédito/débito reportada por la emisora de tarjeta / entidades del sistema financiero / sistemas auxiliares de pago2%
344BAdquisición de sustancias minerales dentro del territorio nacional2%
346Otras retenciones aplicables a otros porcentajesvariable
346AOtras ganancias de capital distintas de enajenación de derechos representativos de capitalvariable
346BDonaciones en dinero - impuesto a las donacionesvariable
346CRetención a cargo del propio sujeto pasivo por la producción y/o comercialización de minerales y otros bienesvariable
346DRetención a cargo del propio sujeto pasivo por la comercialización de productos forestalesvariable
350Otras autorretenciones (incisos 1 y 2 del art. 92.1 del RLRTI)variable
3480Impuesto a la renta único sobre los ingresos de los operadores de pronósticos deportivos15%
3481Autorretenciones de sociedades grandes contribuyentesvariable
3482Comisiones a sociedades nacionales o extranjeras residentes y establecimientos permanentes domiciliados en el país5%
Retención de ISD (taxType: 'ISD') (1 códigos)
CódigoConceptoPorcentaje
4580Impuesto a la salida de divisas5%
Sustento del comprobante (codSustento) (11 códigos)
CódigoConcepto
01Crédito tributario para declaración de IVA (compras)
02Costo o gasto para declaración de impuesto a la renta (compras)
03Activo fijo - crédito tributario para declaración de IVA (compras)
04Activo fijo - costo o gasto para declaración de impuesto a la renta (compras)
05Liquidación de compra de bienes o prestación de servicios en las que el vendedor no tiene sustento tributario
06Adquisiciones a proveedores no obligados a emitir comprobantes de venta o no residentes en el Ecuador
07Sin derecho a crédito tributario ni a costo o gasto
08Reembolso como intermediario
09Reembolso por el contribuyente
10Impuesto a la salida de divisas
11Compensación de deudas tributarias con el sector público

Qué completa Factuplan en el XML

  • Los datos del sujeto retenido (tipo de identificación, razón social, identificación) se toman del proveedor de la compra. parteRel se envía como NO.
  • Los impuestos del documento sustento (impuestosDocSustento) se agrupan a partir de los ítems de la compra.
  • Las formas de pago (pagos) se copian de la compra. Si la compra no trae ninguna, se declara el total con la forma de pago 20 (otros con utilización del sistema financiero).

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 lo autoriza ante el SRI en la misma llamada. Es el mismo endpoint que para las facturas (POST /v1/developer/sign-and-authorize): el tipo de comprobante se deduce del nodo raíz del XML.

Con una API key de producción necesitas cupo disponible en tu plan: se comprueba antes de firmar, y solo se descuenta un documento si el SRI lo autoriza. Reenviar el mismo comprobante no lo vuelve a descontar. En el entorno de pruebas no consume cupo.

Importante: El XML debe enviarse sin firmar y con su declaración <?xml ...?>.

// Un solo método para todos los tipos de comprobante: vive en invoices
const result = await factuplan.invoices.signAndAuthorize({
  xml: xmlString, // XML de la retención, sin firmar
});

console.log(result.authorized);          // true | false
console.log(result.authorizationNumber); // número de autorización del SRI
console.log(result.authorizationDate);   // fecha de autorización
console.log(result.authorizedXml);       // XML autorizado (base64)

if (!result.authorized) {
  console.error(result.messages); // motivos del rechazo del SRI
}

Devuelve la respuesta del SRI y no guarda el comprobante: no hay id, PDF ni correo, y nada que consultar o descargar después. Los campos son los mismos que en Firmar y autorizar XML de facturas.

Anular

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

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

Reintentar retención

Vuelve a lanzar el flujo de emisión de una retención que no llegó a autorizarse. El paso que se reintenta se deduce del estado y llega en action. Los estados admitidos y las advertencias están en Reintentar comprobante.

const result = await factuplan.withholdings.retry('WITHHOLDING_ID');

console.log(result.action);    // "reprocess" | "authorize" | "pdf"
console.log(result.accessKey); // Puede ser una clave NUEVA

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
emissionPointIdstringSíID del punto de emisión
customerobjectSíDestinatario (ID existente o datos inline)
transporterRucstringSíRUC del transportista
transporterNamestringSíNombre del transportista
vehiclePlatestringSíPlaca del vehículo (ej. ABC-1234)
departureAddressstringSíDirección de inicio del transporte
transportStartDatestringSíFecha inicio transporte (ISO 8601)
transportEndDatestringSíFecha fin transporte (ISO 8601)
itemsarraySíMercaderí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 & Contribuyentes

Enviar XML firmado

Si firmas los comprobantes con tu propio certificado, Factuplan puede enviarlos al SRI y devolverte su respuesta. Sirve para cualquier tipo de comprobante: el tipo se deduce del nodo raíz del XML, no hace falta indicarlo.

No confundir con «Firmar XML». Ahí envías el XML sin firmar y Factuplan lo firma. Aquí lo envías ya firmado y Factuplan solo lo transmite al SRI.

Requisitos

  • API key de producción (ak_live_…). Con una key de pruebas el endpoint responde 403.
  • El XML debe venir firmado (con su elemento <ds:Signature>). Sin firma se rechaza.
  • El nodo raíz debe ser uno de: factura, notaCredito, notaDebito, guiaRemision, comprobanteRetencion o liquidacionCompra.
  • Debe declarar el RUC del proveedor (ver abajo).
  • Debes tener cupo disponible en tu plan. Se comprueba antes de enviar al SRI, y solo se descuenta un documento si el SRI autoriza.
  • No lleva el header x-taxpayer-ruc. Ese header sirve para elegir con qué certificado firmar, y aquí el XML ya viene firmado por ti: no necesitas tener ninguna firma cargada en Factuplan. El cupo se descuenta de tu espacio de trabajo.

Este endpoint no genera PDF ni envía correo, y el comprobante no queda registrado en tu panel: Factuplan actúa únicamente como transmisor ante el SRI.

RUC del proveedor

El comprobante debe identificar a Factuplan como proveedor del software. Va dentro de <infoAdicional>, en un <campoAdicional> cuyo atributo nombre es exactamente RUC Proveedor:

<infoAdicional>
  <campoAdicional nombre="RUC Proveedor">0993411372001</campoAdicional>
</infoAdicional>

El nombre del campo no distingue mayúsculas ni espacios sobrantes (ruc proveedor también vale), pero el valor debe coincidir exactamente con el RUC de arriba. Si falta el campo o el RUC es otro, el comprobante se rechaza antes de llegar al SRI y no consume cupo.

Enviar al SRI

La respuesta es la del SRI, tal cual: si autorizó, con qué número y en qué fecha, el XML autorizado en base64 y los mensajes que haya devuelto.

const result = await factuplan.documents.authorizeSigned({
  xml: signedXmlString, // comprobante YA firmado, como string
});

console.log(result.authorized);          // true | false
console.log(result.authorizationNumber); // número de autorización del SRI
console.log(result.authorizationDate);   // fecha de autorización
console.log(result.authorizedXml);       // XML autorizado (base64)
console.log(result.messages);            // mensajes del SRI

if (!result.authorized) {
  // El SRI no lo autorizó: los motivos vienen en result.messages
  // y no se descuenta ningún documento de tu plan.
  console.error(result.messages);
}

Errores propios de este endpoint

CódigoQué pasó
INTEGRATION_8005Usaste una API key de pruebas. Este endpoint solo acepta ak_live_….
API_10004El contenido enviado no es un XML válido.
API_10007El XML no lleva firma electrónica. Este endpoint no firma.
API_10008El nodo raíz no es un comprobante que el SRI reconozca.
API_10009Falta el campoAdicional con nombre RUC Proveedor.
API_10010El RUC Proveedor declarado no es el de Factuplan. El error te dice el esperado y el recibido.

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
WH_5304404No se encontró una compra registrada con esa clave de acceso
WH_5305400La compra todavía no está registrada correctamente y no puede sustentar una retención
WH_5306409Esa compra ya tiene una retención emitida
WH_5307400El código de sustento no existe en la tabla del SRI
WH_5308400El código de retención no corresponde al impuesto seleccionado

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

Cuando un comprobante cambia de estado te enviamos un POST a la URL que configures. Cada entrega va firmada: verifica siempre la firma antes de confiar en el contenido.

Eventos

Cada evento se llama {comprobante}.{resultado}. El resultado es authorized (el SRI autorizó el comprobante), rejected (el SRI lo rechazó) o error (no pudimos completar su emisión). Al crear un webhook eliges a cuáles se suscribe, y puedes cambiarlos después desde Developer → Webhooks sin perder el secreto ni el historial.

ComprobanteEventos
Facturainvoice.authorized · invoice.rejected · invoice.error
Nota de créditocredit_note.authorized · credit_note.rejected · credit_note.error
Nota de débitodebit_note.authorized · debit_note.rejected · debit_note.error
Guía de remisiónwaybill.authorized · waybill.rejected · waybill.error
Liquidación de comprapurchase_settlement.authorized · purchase_settlement.rejected · purchase_settlement.error
Comprobante de retenciónwithholding.authorized · withholding.rejected · withholding.error
Cualquier comprobante (genéricos)receipt.authorized · receipt.rejected · receipt.error

Los eventos receipt.* son genéricos: se disparan con cualquier tipo de comprobante, además del evento propio de ese tipo. Un webhook suscrito a receipt.authorized y a invoice.authorized recibe dos notificaciones por la misma factura, cada una con un id de evento distinto. Suscríbete a los genéricos o a los de cada tipo, no a ambos.

Verificar la firma

Cada entrega incluye la cabecera X-Factuplan-Signature con este formato:

X-Factuplan-Signature: t=1756288800,v1=5f3c9a...
X-Factuplan-Event:     invoice.authorized
X-Factuplan-Delivery:  0198f2c1-...
X-Factuplan-Webhook-Id: 0198f2b0-...

t es el timestamp Unix del envío y v1 es el HMAC-SHA256, en hexadecimal, de la cadena {t}.{cuerpo_crudo}. La clave es el secreto whsec_… que se muestra una sola vez al crear el webhook.

import express from 'express';
import { verifyWebhookSignature } from 'factuplan';

const app = express();

// Conserva el cuerpo crudo: es sobre esos bytes que se calcula la firma.
app.use(express.json({ verify: (req, _res, buf) => { (req as any).rawBody = buf } }));

app.post('/webhooks/factuplan', (req, res) => {
  const valido = verifyWebhookSignature(
    (req as any).rawBody,
    req.headers['x-factuplan-signature'],
    process.env.FACTUPLAN_WEBHOOK_SECRET!,
  );

  if (!valido) return res.status(400).send('firma invalida');

  // Responde rapido y procesa despues.
  res.sendStatus(200);
  procesarEvento(req.body);
});

Firma sobre el cuerpo crudo. Si tu framework ya parseó el JSON y lo vuelves a serializar, los bytes cambian y la firma no coincide.

Valida la ventana de tiempo. El timestamp va dentro del HMAC; rechazar entregas con más de 5 minutos es lo que evita que alguien reenvíe una entrega antigua.

Compara en tiempo constante (timingSafeEqual), no con ===.

Puedes rotar el secreto cuando quieras desde Developer → Webhooks. El anterior deja de validar de inmediato y el nuevo solo se muestra al rotar, así que ten listo el despliegue de tu endpoint antes de hacerlo.

Entregas y reintentos

  • Consideramos entregada una notificación solo si respondes 2xx. Si no, reintentamos 7 veces más con espera creciente (30s, 1m, 2m, 4m, 8m, 16m y 32m: cerca de una hora en total).
  • Tras 10 entregas fallidas consecutivas (una entrega cuenta como fallida cuando agota sus reintentos) desactivamos el webhook y avisamos por correo al propietario del workspace. Puedes reactivarlo desde Developer → Webhooks; al hacerlo, el contador de fallos vuelve a cero.
  • Mientras el webhook está desactivado por fallos no enviamos nada. Durante los 7 días siguientes a la desactivación, cada evento queda registrado en su historial como entrega omitida; pasado ese plazo los eventos ya no se registran, así que reactívalo antes para no perderlos.
  • El historial de entregas del panel muestra las 50 más recientes de cada webhook. Con el filtro Solo sin entregar ves únicamente las que no llegaron (fallidas, omitidas o todavía en curso), también las que ya no caben entre las más recientes: cada entrega que reenvías con éxito sale de esa lista y aparecen las siguientes.
  • Para reenviar una entrega el webhook debe estar activo y la entrega no puede estar todavía en su ciclo de reintentos: mientras dure aparece como Reintentando y la reintentamos nosotros. Tampoco se reenvía un evento error o rejected de un comprobante que después fue autorizado, porque quedó obsoleto. El reenvío es la misma entrega: conserva el id del evento.
  • Todos los reintentos y reenvíos de un mismo evento comparten el id del evento y la cabecera X-Factuplan-Delivery. Guárdalos y descarta duplicados: un reintento puede llegar después de que el original ya se procesó.
  • Responde rápido (idealmente menos de 10 segundos) y procesa de forma asíncrona. El tiempo de espera de nuestra parte es de 10 segundos.
  • El ambiente de pruebas dispara exactamente los mismos eventos, con el mismo formato y la misma firma que producción.

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.

Reintentar comprobante

Vuelve a lanzar el flujo de emisión de un comprobante que no llegó a autorizarse. No necesitas saber en qué paso se quedó: el sistema lo deduce del estado del comprobante y te lo informa en el campo action.

Factuplan ya reintenta solo los fallos transitorios del SRI (5 intentos por cola, más un barrido automático cada 5 minutos). Este endpoint es para cuando esos reintentos automáticos se agotaron y quieres volver a intentarlo desde tu integración.

Estado del comprobanteactionQué hace
ERROR, REJECTED, RETURNEDreprocessRegenera el XML, lo firma y lo reenvía al SRI
RECEIVED_SRIauthorizeVuelve a consultar la autorización en el SRI
AUTHORIZED sin PDFpdfRegenera únicamente el RIDE (PDF)
PROCESSING, SENDING_SRI, VOIDED, AUTHORIZED con PDF—Devuelve 400: no hay nada que reintentar
const result = await factuplan.invoices.retry('ID_DEL_COMPROBANTE');

console.log(result.action);    // "reprocess" | "authorize" | "pdf"
console.log(result.status);    // Estado tras encolar el reintento
console.log(result.accessKey); // Clave de acceso vigente (puede ser NUEVA)

// Mismo método en el resto de comprobantes:
// factuplan.creditNotes.retry(id)
// factuplan.debitNotes.retry(id)
// factuplan.withholdings.retry(id)

Respuesta

CampoTipoDescripción
idstringID del comprobante reintentado
typestringINVOICE | CREDIT_NOTE | DEBIT_NOTE | WITHHOLDING
actionstringPaso que se volvió a ejecutar: reprocess | authorize | pdf
statusstringEstado del comprobante tras encolar el reintento
accessKeystring | nullClave de acceso vigente. Cambia si el comprobante estaba en REJECTED o RETURNED
messagestringMensaje de confirmación

En REJECTED y RETURNED el SRI rechazó el contenido del XML, así que el comprobante se regenera con una clave de acceso nueva y con los mismos datos. Si la devolución fue por datos inválidos, el reintento volverá a fallar: corrige el origen y emite un comprobante nuevo. Guarda siempre el accessKey que devuelve la respuesta.

Un comprobante se descuenta de tu plan una sola vez, cuando el SRI lo autoriza: reintentarlo nunca lo cobra dos veces. Si todavía no se ha descontado, el reintento necesita que tengas cupo disponible.

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

Actualizar firma electrónica (P12)

Reemplaza la firma electrónica de un contribuyente que ya existe en tu workspace, indicando su RUC. Es la vía para renovar una firma vencida o por vencer sin tocar el resto de la configuración del contribuyente. El RUC del certificado debe coincidir con el RUC enviado.

A diferencia de POST /developer/certificate, este endpoint nunca crea un contribuyente ni consume cupo de contribuyentes de tu plan: si el RUC no existe en tu workspace devuelve 404.

import { readFileSync } from 'fs';

const p12 = readFileSync('./mi-firma-renovada.p12');
const result = await factuplan.certificates.update(
  p12,
  'mi-contraseña',
  '0950194407001', // RUC del contribuyente a actualizar
);

console.log(result.updated);            // true
console.log(result.previousExpiresAt);  // vencimiento de la firma anterior
console.log(result.expiresAt);          // vencimiento de la firma nueva

Campos del formulario

CampoTipoDescripción
filearchivoNuevo archivo P12 (.p12 o .pfx)
passwordstringContraseña del certificado
rucstringRUC de 13 dígitos del contribuyente cuya firma se reemplaza

Respuesta

CampoTipoDescripción
hasCertificatebooleantrue si la nueva firma quedó cargada y operativa
isExpiredbooleantrue si el certificado ya expiró
daysUntilExpirynumberDías restantes hasta el vencimiento
rucstringRUC del titular del certificado
legalNamestringNombre legal extraído del certificado
issuedAtstringFecha de emisión (ISO 8601)
expiresAtstringVencimiento de la firma nueva (ISO 8601)
issuerstringEntidad certificadora emisora
updatedbooleanSiempre true: confirma que la firma fue reemplazada
previousExpiresAtstring | nullVencimiento de la firma anterior, null si no tenía

Errores específicos

HTTPCuándo ocurre
404El RUC no existe como contribuyente en tu workspace. Usa POST /developer/certificate para cargarlo por primera vez.
403Tu API key está restringida a otros contribuyentes y no puede operar con ese RUC.
400El RUC del certificado no coincide con el ruc enviado, el P12 es inválido o la contraseña es incorrecta.
422El certificado está vencido o su entidad certificadora no está autorizada por el SRI.

Compras

Las facturas que tus proveedores te emitieron. Registras una compra a partir de su clave de acceso: Factuplan descarga del SRI el comprobante ya autorizado, crea al proveedor y a los productos que falten, y genera el PDF. No firma nada, no envía nada al SRI y no manda correos.

No confundas compras con liquidaciones de compra. Una liquidación (SRI tipo 03) es un comprobante que emites tú cuando tu proveedor no puede darte factura. Una compra es lo contrario: un comprobante que otro contribuyente emitió a tu nombre y que tú solo registras.

Registrar una compra solo funciona con API keys de producción (ak_live_...). La clave de acceso se consulta contra el SRI real, así que en el entorno de pruebas no hay comprobantes que registrar: devuelve 403 con el código INTEGRATION_8005. El resto de endpoints de esta sección (listar, obtener, descargar, eliminar) funcionan en ambos entornos.

Registrar una compra consume cupo mensual de tu plan. Reintentar el PDF y eliminar una compra no consumen cupo, y eliminar tampoco lo devuelve.

Todos los endpoints de esta sección requieren la cabecera x-taxpayer-ruc con el RUC del contribuyente que hizo la compra.

Registrar una compra

Registra la factura que te emitió tu proveedor a partir de su clave de acceso de 49 dígitos. Por ahora solo se admiten facturas (tipo 01), y solo con una API key de producción.

const compra = await factuplan.purchases.register(
  '0509202601099234567800120020030000001231234567818',
);

console.log(compra.id);
console.log(compra.supplier?.legalName); // proveedor que te emitió la factura
console.log(compra.total);
console.log(compra.status);              // AUTHORIZED mientras se genera el PDF

Cuerpo de la petición

CampoTipoDescripción
accessKeystringClave de acceso de 49 dígitos de la factura de compra

Respuesta

CampoTipoDescripción
idstringID de la compra en Factuplan
statusstringAUTHORIZED (PDF en proceso), COMPLETED (PDF listo) o ERROR
accessKeystringClave de acceso de la factura
sriDocTypestringTipo de documento del SRI. Por ahora siempre 01
supplierIdstringID del proveedor, creado automáticamente si no existía
supplierobjectProveedor: legalName, identification y datos de contacto
issueDatestringFecha de emisión de la factura (ISO 8601)
authorizationNumberstring | nullNúmero de autorización del SRI
subtotal / taxAmount / totalstringImportes, como string para no perder decimales
itemsarrayÍtems de la factura con cantidades, precios e impuestos
withholdingsarrayRetención emitida sobre esta compra, si ya existe

Errores específicos

CódigoHTTPCuándo ocurre
INTEGRATION_8005403Estás usando una API key de pruebas (ak_test_...). Este endpoint requiere una de producción.
PU_5501400La clave de acceso no tiene 49 dígitos numéricos.
PU_5502400La clave no corresponde a una factura (tipo 01). Aún no se admiten otros tipos.
PU_5503400Esa clave es de un comprobante que emitiste tú, no de una compra.
PU_5504400Esa compra ya está registrada (incluso si la eliminaste antes).
PU_5505400No se pudo descargar el comprobante del SRI. Verifica la clave de acceso.
PU_5506400El comprobante no está autorizado en el SRI.
PU_5508400La factura fue emitida a otro RUC, no al del contribuyente indicado.
SUB_12002403Alcanzaste el límite de compras de tu plan.

Buscar compras

Lista las compras registradas del contribuyente, con paginación. Las fechas se interpretan en días de Ecuador y son inclusivas.

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

console.log(meta.total, meta.totalPages);
for (const compra of data) {
  console.log(compra.sequential, compra.supplier?.legalName, compra.total);
}

Parámetros de consulta

CampoTipoDescripción
statusstringAUTHORIZED, COMPLETED o ERROR
supplierIdstringFiltrar por proveedor (ID del contacto)
dateFromstringDesde (YYYY-MM-DD, día de Ecuador, inclusive)
dateTostringHasta (YYYY-MM-DD, día de Ecuador, inclusive)
searchstringBusca por razón social del proveedor, secuencial o clave de acceso
pagenumberNúmero de página. Por defecto 1
limitnumberElementos por página. Por defecto 20, máximo 100

Obtener una compra

Devuelve el detalle completo: proveedor, ítems con sus impuestos, totales y la retención emitida sobre esa compra, si ya existe.

const compra = await factuplan.purchases.get('019432a1-8f2c-7c3d-9a41-2b6e5d0f1a77');

console.log(compra.items?.length);
console.log(compra.withholdings); // [] si todavía no le emitiste retención

Descargar XML / PDF

A diferencia de los comprobantes que emites tú, las descargas de compras llegan en base64, no como una URL pre-firmada. El XML es el autorizado por el SRI, tal cual lo emitió tu proveedor.

El PDF se genera de forma asíncrona. Si pides el PDF mientras todavía se está generando, o si quedó en error, recibes 404 con el código PU_5510. Espera unos segundos o usa el reintento si la compra está en estado ERROR.

import { writeFileSync } from 'fs';

const pdf = await factuplan.purchases.getPdf(compra.id);
writeFileSync(pdf.filename, Buffer.from(pdf.base64, 'base64'));

const xml = await factuplan.purchases.getXml(compra.id);
writeFileSync(xml.filename, Buffer.from(xml.base64, 'base64'));

Respuesta

CampoTipoDescripción
base64stringContenido del archivo codificado en base64
filenamestringNombre sugerido, p. ej. COMPRA-001-001-000000123.pdf
contentTypestringapplication/pdf o application/xml

Reintentar el PDF

Vuelve a encolar la generación del PDF de una compra que quedó en estado ERROR. Usa el XML que ya está guardado, así que no descarga nada del SRI otra vez y no consume cupo. Si el PDF ya está generado o todavía está en proceso, devuelve 400 con el código PU_5511.

const result = await factuplan.purchases.retryPdf(compra.id);

console.log(result.success); // true
console.log(result.message); // "Estamos regenerando el PDF de la compra."

Eliminar una compra

Elimina el registro de la compra en Factuplan. Es un borrado lógico y no afecta en nada al comprobante en el SRI, que sigue autorizado.

Eliminar no devuelve el cupo que consumiste al registrarla, y la clave de acceso queda reservada: volver a registrar esa misma compra devuelve 400 con el código PU_5504.

const result = await factuplan.purchases.delete(compra.id);

console.log(result.success); // true

Contribuyentes vinculados

Las tres peticiones siguientes controlan con qué contribuyentes puede operar tu API key. Solo cambian el vínculo entre la API key y el contribuyente: nunca crean ni eliminan un contribuyente, ni tocan su firma electrónica, sus comprobantes o su configuración.

Una API key sin ningún contribuyente vinculado no está restringida: puede operar con todos los contribuyentes de tu workspace. Por eso, vincular el primer contribuyente la restringe únicamente a él, y no puedes desvincular el último.

Este endpoint devuelve los contribuyentes con los que esta API key puede operar. Si unrestricted es true, la llave no tiene restricciones y taxpayers llega vacío.

const { unrestricted, taxpayers } = await factuplan.taxpayers.list();

if (unrestricted) {
  console.log('Esta API key puede operar con TODOS los contribuyentes');
} else {
  for (const t of taxpayers) {
    console.log(t.ruc, t.legalName, t.hasCertificate);
  }
}

Respuesta

CampoTipoDescripción
unrestrictedbooleantrue si la API key no tiene contribuyentes vinculados y puede operar con todos
taxpayersarrayContribuyentes vinculados a la API key
taxpayers[].rucstringRUC del contribuyente
taxpayers[].legalNamestringRazón social del contribuyente
taxpayers[].hasCertificatebooleantrue si tiene una firma electrónica cargada
taxpayers[].certificateExpiresAtstring | nullVencimiento de la firma (ISO 8601), null si no tiene

Vincular contribuyente

Añade un contribuyente que ya existe en tu workspace a la lista de contribuyentes con los que esta API key puede operar. Es idempotente: vincular dos veces el mismo RUC devuelve alreadyLinked: true y no cambia nada.

Nunca crea un contribuyente ni consume cupo de contribuyentes de tu plan: si el RUC no existe en tu workspace devuelve 404. Para crear un contribuyente nuevo usa POST /developer/certificate.

const result = await factuplan.taxpayers.link('0950194407001');

console.log(result.linked);        // true
console.log(result.alreadyLinked); // false si acaba de vincularse
console.log(result.restrictedNow); // true si la llave pasó de "todos" a solo este
console.log(result.linkedCount);   // contribuyentes vinculados en total

Cuerpo de la petición

CampoTipoDescripción
rucstringRUC de 13 dígitos del contribuyente a vincular. Debe existir en tu workspace.

Respuesta

CampoTipoDescripción
rucstringRUC del contribuyente vinculado
legalNamestringRazón social del contribuyente
linkedbooleanSiempre true: confirma que el contribuyente quedó vinculado
alreadyLinkedbooleantrue si ya estaba vinculado y la petición no cambió nada
restrictedNowbooleantrue si este vínculo restringió una API key que antes operaba con todos
linkedCountnumberContribuyentes vinculados tras la operación

Errores específicos

HTTPCuándo ocurre
400El RUC no tiene 13 dígitos numéricos.
404El RUC no existe como contribuyente en tu workspace.

Desvincular contribuyente

Quita un contribuyente de la lista de contribuyentes con los que esta API key puede operar. Es idempotente: desvincular un RUC que no estaba vinculado devuelve alreadyUnlinked: true.

No elimina el contribuyente. Su firma electrónica, sus comprobantes y su configuración quedan intactos, y sigue disponible desde el panel y desde otras API keys. Tampoco puedes desvincular el último contribuyente: devuelve 409 con el código INTEGRATION_8008, porque dejar la lista vacía habilitaría la llave para operar con todos los contribuyentes del workspace.

const result = await factuplan.taxpayers.unlink('0950194407001');

console.log(result.unlinked);        // true
console.log(result.alreadyUnlinked); // false si acaba de desvincularse
console.log(result.linkedCount);     // contribuyentes que siguen vinculados

// El contribuyente NO fue eliminado: sigue en tu workspace.

Cuerpo de la petición

CampoTipoDescripción
rucstringRUC de 13 dígitos del contribuyente a desvincular

Respuesta

CampoTipoDescripción
rucstringRUC del contribuyente desvinculado
unlinkedbooleanSiempre true: confirma que ya no está vinculado
alreadyUnlinkedbooleantrue si no estaba vinculado y la petición no cambió nada
linkedCountnumberContribuyentes que siguen vinculados tras la operación

Errores específicos

HTTPCuándo ocurre
400El RUC no tiene 13 dígitos numéricos.
404El RUC no existe como contribuyente en tu workspace.
409Es el único contribuyente vinculado (INTEGRATION_8008). Vincula otro antes de desvincular este.