Saltar al contenido
Factuplan
Factuplan — Facturar ya no es un dolor de cabeza. Activa tu cuenta y obtén tus primeras 25 facturas gratis, autorizadas por el SRI.
Editor de código mostrando TypeScript con el SDK oficial de Factuplan importado, emitiendo una factura electrónica al SRI Ecuador en pocas líneas

SDK TypeScript para facturación electrónica en Ecuador (SRI)

SDK oficial Node/TypeScript para emitir facturas electrónicas en Ecuador con type safety. Quickstart, ejemplos, manejo de errores y comparativa con cURL.

· 10 min de lectura · Equipo Factuplan

Si trabajas con Node.js o TypeScript y necesitas emitir facturas electrónicas en Ecuador desde tu app, llamar a la API SOAP del SRI directamente es una pesadilla: XMLs gigantes, firmas XAdES-BES, certificados P12, manejo de respuestas mal documentadas. El SDK oficial de Factuplan resuelve eso con una librería tipada que te deja emitir una factura en menos de 10 líneas.

En este post te muestro cómo arrancar con el SDK TypeScript de Factuplan (factuplan en npm, v0.8.0 vigente), por qué un SDK tipado le gana a cURL directo, y los patrones de error que vas a encontrar en producción.

¿Por qué usar un SDK y no llamar a la API con cURL?

Si llamas directamente a una API REST con cURL o fetch, ganas en simplicidad inicial pero pierdes:

  • Type safety: no sabes qué campos son obligatorios hasta que el servidor te devuelve 422.
  • Autocompletado: tienes que abrir docs cada vez que escribes un endpoint.
  • Reintentos automáticos: tienes que implementarlos a mano para 429 y 500.
  • Validación local: enviarías un RUC inválido y descubrirías el error después de la llamada HTTP.
  • Manejo de respuestas: cada endpoint tiene un sobre { data, meta } que tienes que parsear manualmente.

El SDK de Factuplan resuelve todo eso de fábrica.

Comparativa: emitir factura con cURL vs SDK

Opción A: cURL crudo (60+ líneas de bash, sin type safety)

Ventana de terminal
curl https://api.factuplan.com.ec/v1/developer/invoices \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "x-taxpayer-ruc: 0950194407001" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"identificationType": "RUC",
"identification": "0993378150001",
"legalName": "Cliente S.A.",
"email": "facturas@cliente.ec"
},
"items": [{
"code": "SERV-001",
"description": "Servicio de consultoría",
"quantity": 1,
"unitPrice": 150.00,
"taxType": "IVA_RATE",
"tax": 15
}],
"sendEmail": true
}'

Sin autocomplete, sin validación. Si te equivocas en identificationType o taxType, te enteras solo cuando el SRI rechaza.

Opción B: SDK TypeScript (10 líneas, todo tipado)

import { Factuplan } from "factuplan"
const factuplan = new Factuplan(process.env.FACTUPLAN_API_KEY!, {
ruc: "0950194407001",
})
const factura = await factuplan.invoices.create({
customer: {
identificationType: "RUC",
identification: "0993378150001",
legalName: "Cliente S.A.",
email: "facturas@cliente.ec",
},
items: [
{
code: "SERV-001",
description: "Servicio de consultoría",
quantity: 1,
unitPrice: 150,
taxType: "IVA_RATE",
tax: 15,
},
],
sendEmail: true,
})
console.log(factura.accessKey) // 49 dígitos de clave de acceso SRI

El editor te autocompleta identificationType con "RUC" | "CEDULA" | "PASSPORT" | "FINAL_CONSUMER" y taxType con "IVA_0" | "IVA_RATE" | "NOT_TAXABLE" | "EXEMPT". Si pones cualquier otra cosa, el TypeScript compiler te lo marca antes de ejecutar.

Instalación

El SDK soporta Node.js 18+, ESM y CJS, cero dependencias externas:

Ventana de terminal
# npm
npm install factuplan
# pnpm
pnpm add factuplan
# yarn
yarn add factuplan

Si usas Deno o Bun, también funciona porque el paquete está publicado con exports ESM compatibles.

Crear una API key

Antes de escribir código, necesitas una API key del workspace:

  1. Crea cuenta en app.factuplan.com.ec.
  2. Anda a Developer → Create API key.
  3. Copia la clave (ak_test_* o ak_live_*). Se muestra una sola vez.
  4. Guárdala en una variable de entorno (FACTUPLAN_API_KEY).

Nunca la subas al repositorio. Nunca la expongas en el frontend.

PrefijoEntornoComportamiento
ak_test_*PruebasComprobantes ficticios, eliminados cada hora
ak_live_*ProducciónComprobantes reales firmados y enviados al SRI

Empieza siempre en pruebas. Cuando todo funcione, intercambia la API key por la de producción sin tocar código.

Inicializar el cliente

import { Factuplan } from "factuplan"
const factuplan = new Factuplan(process.env.FACTUPLAN_API_KEY!, {
ruc: "0950194407001", // RUC del contribuyente emisor
})

El SDK adjunta automáticamente la cabecera X-API-Key y x-taxpayer-ruc en cada request. No tienes que pensar en autenticación.

Si vas a usar operaciones que no requieren emisor (consultar tu uso del mes, por ejemplo), puedes omitir el ruc:

const factuplan = new Factuplan(process.env.FACTUPLAN_API_KEY!)
const usage = await factuplan.usage.current()
console.log(usage.invoicesUsed, "/", usage.invoicesLimit)

Hello world: emitir una factura en 10 líneas

import { Factuplan } from "factuplan"
const factuplan = new Factuplan(process.env.FACTUPLAN_API_KEY!, {
ruc: "0950194407001",
})
const factura = await factuplan.invoices.create({
customer: {
identificationType: "RUC",
identification: "0993378150001",
legalName: "Empresa Demo S.A.",
email: "facturas@empresademo.ec",
saveToContacts: true,
},
items: [
{
code: "SERV-001",
description: "Servicio de consultoría técnica",
quantity: 1,
unitPrice: 150,
taxType: "IVA_RATE",
tax: 15,
},
],
sendEmail: true,
})
console.log("Clave de acceso SRI:", factura.accessKey)
console.log("RIDE PDF:", factura.pdfUrl)

Eso es todo. El SDK se encarga de:

  1. Generar el XML SRI.
  2. Firmarlo con tu certificado P12 (cargado previamente en tu workspace).
  3. Enviarlo al SRI para autorización.
  4. Generar el RIDE PDF con tu branding.
  5. Mandar el email al cliente.

Type safety: el corazón del SDK

Todo el SDK está tipado con TypeScript. Esto significa que:

1. Autocompletado en tu editor

Al escribir factuplan.invoices. tu editor te lista todos los métodos disponibles:

factuplan.invoices.create(...)
factuplan.invoices.get(id)
factuplan.invoices.list({ page, limit, search })
factuplan.invoices.void(id, { reason }) // anular

2. Validación en tiempo de compilación

const factura = await factuplan.invoices.create({
customer: {
identificationType: "DNI", // ❌ Error TS: Type "DNI" is not assignable
// El SDK solo acepta "RUC" | "CEDULA" | "PASSPORT" | "FINAL_CONSUMER"
identification: "0993378150001",
legalName: "Empresa Demo S.A.",
},
// ...
})

3. Tipado de respuestas

const factura = await factuplan.invoices.create({ /* ... */ })
// factura es typed: tiene id, accessKey, pdfUrl, xmlUrl, status, etc.
const status: "PENDING" | "AUTHORIZED" | "REJECTED" = factura.status

4. Discriminated unions para campos opcionales

// Punto de emisión: 3 formas mutuamente excluyentes
type EmissionPoint =
| { emissionPointId: string } // Por UUID
| { establishment: string; emissionPoint: string } // Por códigos SRI
| {} // Auto-detect
const factura = await factuplan.invoices.create({
emissionPointId: "ep_id_123", // ✓ válido
// o
// establishment: "001", emissionPoint: "001", // ✓ válido
// o
// (nada) // ✓ auto-detecta si solo hay un punto activo
// ...
})

Operaciones disponibles

El SDK cubre el ciclo completo de facturación:

Clientes (customers)

// Crear
const cliente = await factuplan.customers.create({
identificationType: "RUC",
identification: "0993378150001",
legalName: "Empresa Demo S.A.",
email: "facturas@empresademo.ec",
})
// Listar con paginación
const { data: clientes, meta } = await factuplan.customers.list({
page: 1,
limit: 20,
search: "empresa",
})
// Obtener
const cliente = await factuplan.customers.get("cust_id")
// Actualizar
await factuplan.customers.update("cust_id", {
email: "nuevo@empresademo.ec",
})
// Eliminar
await factuplan.customers.delete("cust_id")

Productos (products)

const producto = await factuplan.products.create({
code: "SERV-001",
name: "Servicio de consultoría",
unitPrice: 150,
type: "SERVICE", // PRODUCT | SERVICE
taxType: "IVA_RATE", // IVA_0 | IVA_RATE | NOT_TAXABLE | EXEMPT
})

Facturas (invoices)

const factura = await factuplan.invoices.create({ /* ... */ })
// Anular (emite nota de crédito reversiva)
await factuplan.invoices.void(factura.id, {
reason: "Error en datos del cliente",
})

Notas de crédito (creditNotes)

const notaCredito = await factuplan.creditNotes.create({
customer: { /* ... */ },
items: [ /* ... */ ],
referenceDocument: {
accessKey: "1305202401099337815000110010010000056781234567811",
type: "INVOICE",
date: "2026-05-13",
},
reason: "Devolución parcial de mercadería",
})

Guías de remisión (waybills)

const guia = await factuplan.waybills.create({
carrier: {
identification: "0950194407001",
legalName: "Transportes Demo S.A.",
licensePlate: "ABC-1234",
},
// ...
})

Retenciones (withholdings) y liquidaciones (purchaseLiquidations)

Disponibles con la misma API consistente.

Manejo de errores

El SDK lanza errores tipados que puedes diferenciar con instanceof:

import {
Factuplan,
FactuplanError,
FactuplanValidationError,
FactuplanAuthError,
FactuplanRateLimitError,
FactuplanSRIError,
} from "factuplan"
try {
const factura = await factuplan.invoices.create({ /* ... */ })
} catch (err) {
if (err instanceof FactuplanValidationError) {
// 400/422 — datos inválidos
console.error("Validación:", err.code, err.details)
} else if (err instanceof FactuplanAuthError) {
// 401 — API key inválida o expirada
console.error("Autenticación falló")
} else if (err instanceof FactuplanRateLimitError) {
// 429 — cuota mensual o rate limit
console.error("Reintenta en:", err.retryAfter, "segundos")
} else if (err instanceof FactuplanSRIError) {
// 422 con código del SRI — rechazo de la autoridad fiscal
console.error("SRI rechazó:", err.sriCode, err.sriMessage)
} else if (err instanceof FactuplanError) {
// Cualquier otro error de la API
console.error("Error:", err.statusCode, err.code, err.message)
} else {
// Error no relacionado al SDK (red, etc.)
throw err
}
}

Reintentos automáticos

Por defecto, el SDK reintenta automáticamente con backoff exponencial en:

  • 5xx del servidor.
  • 429 con Retry-After header.
  • Errores de red transitorios.

Puedes configurar el comportamiento al inicializar:

const factuplan = new Factuplan(apiKey, {
ruc: "0950194407001",
retries: 5, // default 3
retryDelay: 1000, // ms base, default 500
retryMaxDelay: 30000, // ms tope, default 10000
})

Webhooks

Si tienes un endpoint que recibe eventos de Factuplan (autorización exitosa, rechazo, etc.), el SDK ofrece verificación de firma:

import { Factuplan } from "factuplan"
// Express/Next.js handler
app.post("/webhooks/factuplan", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-factuplan-signature"] as string
try {
const event = Factuplan.webhooks.constructEvent(
req.body,
signature,
process.env.FACTUPLAN_WEBHOOK_SECRET!,
)
switch (event.type) {
case "invoice.authorized":
// event.data es Invoice
console.log("Factura autorizada:", event.data.accessKey)
break
case "invoice.rejected":
console.log("Rechazada por SRI:", event.data.sriError)
break
// ...
}
res.json({ received: true })
} catch (err) {
res.status(400).send("Webhook signature inválida")
}
})

Comparativa con HTTP directo

AspectocURL/fetch directoSDK TypeScript
Autocompletado❌ No✅ Sí
Validación local❌ No✅ Sí
ReintentosManual✅ Automático
Manejo de errorestry/catch genérico✅ Errores tipados
Type safety❌ Cero✅ 100%
Curva de aprendizajeAlta (XMLs, firmas)Baja (10 líneas)
Tamaño del bundle0 (nativo)~30 KB minified
SostenibilidadTu mantienes el códigoAnthropic mantiene el SDK

Si tu proyecto es scriptcito de un solo uso, cURL está bien. Si vas a producción, SDK siempre.

Uso desde Next.js o aplicaciones web

El SDK nunca debe usarse desde el frontend (filtraría tu API key). Úsalo desde:

  • Server Actions o Route Handlers de Next.js.
  • Backend de Express, NestJS, Fastify, Koa, Hono.
  • Workers de Cloudflare, Vercel Edge Functions, AWS Lambda.
  • Scripts CLI internos.

Ejemplo: Next.js Server Action

app/actions/emitir-factura.ts
"use server"
import { Factuplan } from "factuplan"
const factuplan = new Factuplan(process.env.FACTUPLAN_API_KEY!, {
ruc: process.env.FACTUPLAN_RUC!,
})
export async function emitirFactura(formData: FormData) {
const factura = await factuplan.invoices.create({
customer: {
identificationType: "RUC",
identification: formData.get("ruc") as string,
legalName: formData.get("legalName") as string,
email: formData.get("email") as string,
},
items: [
{
description: "Servicio web",
quantity: 1,
unitPrice: Number(formData.get("precio")),
taxType: "IVA_RATE",
tax: 15,
},
],
sendEmail: true,
})
return { accessKey: factura.accessKey, pdfUrl: factura.pdfUrl }
}

¿Dónde está el código fuente?

El SDK es open source (MIT). El repositorio está disponible para ver, reportar issues o contribuir. Próximamente publicaremos el link del repo en el hub de developers de Factuplan.

Resumen práctico

  1. El SDK factuplan para Node.js/TypeScript te deja emitir facturas electrónicas al SRI Ecuador en menos de 10 líneas.
  2. Es tipado de punta a punta — el compilador te marca errores antes de ejecutar.
  3. Maneja reintentos automáticos, firmas, errores del SRI y webhooks.
  4. Se instala con npm install factuplan y soporta Node 18+, ESM y CJS.
  5. Es muy superior a llamar a cURL directo si vas a producción: menos código, menos bugs, mejor mantenibilidad.

Si estás integrando facturación electrónica en tu app, crea una API key gratis y empieza con el SDK en pruebas. Sin tarjeta, sin compromisos. Cuando estés listo, intercambias la API key por la de producción.


Repositorio y referencia: ver documentación técnica del SDK en factuplan.com.ec/docs/sdk. Soporte para developers: próximamente hub completo en factuplan.com.ec/desarrolladores.

Preguntas frecuentes

¿Qué versión de Node.js requiere el SDK de Factuplan?

El SDK oficial de Factuplan (paquete factuplan en npm, versión actual v0.8.0) requiere Node.js 18 o superior. Está publicado como módulo dual ESM y CJS, por lo que funciona tanto en proyectos modernos con ECMAScript Modules como en proyectos legacy con CommonJS. También funciona en Deno y Bun gracias a su compatibilidad con los exports estándar. El paquete tiene cero dependencias externas, lo que minimiza el tamaño del bundle y elimina problemas de versionado transitivo.

¿El SDK funciona en TypeScript puro o solo en JavaScript?

El SDK está escrito en TypeScript y publica sus declaraciones de tipos (.d.ts) junto con el código. Puedes usarlo desde TypeScript con tipado completo de punta a punta (incluyendo discriminated unions, enums tipados como literales y autocompletado en VSCode/WebStorm) o desde JavaScript puro con la misma API y los tipos disponibles vía JSDoc. La validación en tiempo de compilación funciona automáticamente en TypeScript; en JavaScript los errores aparecen en runtime al hacer la llamada HTTP. Recomendamos TypeScript para producción.

¿Cómo manejo errores del SRI con el SDK TypeScript?

El SDK lanza errores tipados que puedes diferenciar con instanceof: FactuplanValidationError para errores 400/422 de validación de datos, FactuplanAuthError para 401 (API key inválida), FactuplanRateLimitError para 429 (con propiedad retryAfter en segundos), FactuplanSRIError para rechazos específicos del SRI (con sriCode y sriMessage), y FactuplanError como base para cualquier otro error de la API. Los reintentos automáticos con backoff exponencial cubren 5xx y 429 sin intervención. Para errores del SRI lo más útil es inspeccionar sriCode y sriMessage, que mapean a los códigos del catálogo de errores del SRI.

¿Puedo usar el SDK desde el frontend de mi aplicación web?

No, el SDK debe usarse solo desde el backend (Node.js server, Edge Functions, Server Actions, etc.). Usar el SDK desde el frontend expondría tu API key de Factuplan en el bundle JavaScript que se descarga al navegador, lo cual permitiría a cualquier usuario emitir facturas en nombre de tu empresa y consumir tu cuota mensual. La regla es: API key SIEMPRE en variable de entorno del servidor (FACTUPLAN_API_KEY), nunca en NEXT_PUBLIC_ ni VITE_PUBLIC_ ni en código del cliente. Para flujos donde el usuario completa un formulario y necesitas emitir factura, hazlo desde un Server Action (Next.js), API route (Express) o Edge Function.

¿Qué diferencia hay entre usar el SDK y llamar a la API REST directa con fetch?

Funcionalmente puedes hacer todo con fetch o cURL, pero el SDK añade: 1) Type safety completo, el compilador TypeScript te avisa de campos faltantes o valores inválidos antes de ejecutar; 2) Autocompletado en el editor para nombres de métodos, parámetros y enums; 3) Reintentos automáticos con backoff exponencial para 5xx, 429 y errores de red; 4) Manejo de paginación, parsing de sobres data/meta y normalización de respuestas; 5) Validación local de identificaciones (RUC dígito verificador, cédula módulo 10) antes de enviar al servidor; 6) Helper para verificar firmas de webhooks; 7) Errores tipados que facilitan el manejo con instanceof. Para scripts puntuales fetch está bien; para producción el SDK ahorra horas y previene bugs.

¿Hay otros SDKs de Factuplan para Python, PHP, Go o .NET?

El SDK oficial actual es para Node.js/TypeScript (paquete factuplan en npm). Para otros lenguajes la integración se hace contra la API REST directa, que está completamente documentada en factuplan.com.ec/docs/api con ejemplos en cURL, Python, PHP, Go, Ruby y .NET. La colección de Postman oficial también está disponible para acelerar la integración en cualquier lenguaje. Si tu proyecto justifica un SDK oficial en otro lenguaje, puedes solicitarlo en el roadmap público de developers de Factuplan; los SDKs en Python y PHP están en evaluación según la demanda de la comunidad.

¿El SDK soporta webhooks y verificación de firma?

Sí. El SDK incluye un módulo Factuplan.webhooks con el método constructEvent que verifica la firma HMAC del header x-factuplan-signature contra tu webhook secret (configurado en el dashboard de Factuplan) y devuelve el evento tipado. Si la firma no coincide, lanza FactuplanError, lo que protege contra payloads maliciosos. Los eventos soportados incluyen invoice.authorized, invoice.rejected, credit_note.authorized, waybill.authorized, withholding.authorized y los respectivos para errores. Cada evento tiene su data tipada según el tipo de documento, por lo que en TypeScript puedes usar switch sobre event.type con type narrowing automático.

Equipo Factuplan

Especialista en facturación electrónica

Equipo editorial de Factuplan, especializado en facturación electrónica y normativa tributaria del SRI.

Conocer al equipo

Empieza a facturar electrónicamente hoy

1 mes gratis al comprar tu firma electrónica con FirmaOK. Sin tarjeta de crédito, sin compromiso.