Ir al contenido
Factuplan

API REST de facturación electrónica Ecuador

Descargar colección de Postman

El API HTTP REST de Factuplan te permite integrar facturación electrónica autorizada por el SRI del Ecuador desde cualquier lenguaje o plataforma: PHP, Python, Go, .NET, Ruby, Bash + cURL, etc.

  • Base URL: https://api-rest.factuplan.com.ec
  • Versionado por path: todos los endpoints viven bajo /v1/developer/*.
  • Respuestas siempre JSON con sobre { data, meta }.
  • Dos entornos: pruebas (ak_test_*) y producción (ak_live_*).
  • Compatible con Postman: descarga la colección oficial.

Importante: los comprobantes creados con una API key de pruebas se eliminan automáticamente cada hora. Usa pruebas solo para desarrollo.

No hay nada que instalar — el API se consume con cualquier cliente HTTP.

  1. Inicia sesión en app.factuplan.com.ec.
  2. Anda a Developer → Create API key.
  3. Copia la clave generada — solo se muestra una vez.
  4. Guárdala en una variable de entorno (FACTUPLAN_API_KEY), nunca la subas al repositorio.

Las API keys son por workspace. El prefijo indica el entorno:

PrefijoEntornoComportamiento
ak_test_*PruebasComprobantes ficticios, eliminados cada hora
ak_live_*ProducciónComprobantes reales firmados y enviados al SRI
Ventana de terminal
curl https://api-rest.factuplan.com.ec/v1/developer/usage \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "Accept: application/json"

Todas las peticiones llevan X-API-Key; las que operan sobre un contribuyente llevan además x-taxpayer-ruc:

CabeceraValorNotas
X-API-KeyTu API key (ak_test_* o ak_live_*)Identifica el workspace y el plan.
x-taxpayer-rucRUC del contribuyente emisor (13 dígitos)Requerida en todo endpoint que opera sobre un contribuyente: comprobantes, clientes, productos, estado del certificado y firma de XML. Sin ella esos endpoints responden 400.
Ventana de terminal
curl https://api-rest.factuplan.com.ec/v1/developer/invoices \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "x-taxpayer-ruc: 0950194407001" \
-H "Content-Type: application/json" \
-d @factura.json

Seguridad: nunca expongas tu API key en el frontend. Úsala solo desde tu servidor.

Todas las respuestas exitosas siguen la misma forma:

{
"data": { "id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a" },
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:34:56.789Z"
}
}

Las listas paginadas añaden total, page, limit y totalPages directamente en meta:

{
"data": [{ "id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a" }],
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:34:56.789Z",
"total": 120,
"page": 1,
"limit": 20,
"totalPages": 6
}
}

Los errores devuelven el mismo formato en todos los endpoints: el sobre { data, meta } con data: null y un objeto error. error.details es un objeto y solo aparece cuando hay más contexto (en los errores de validación trae validation, la lista de mensajes):

{
"data": null,
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:34:56.789Z"
},
"error": {
"code": "GENERAL_0002",
"message": "customer.email is required and must be a valid email address",
"statusCode": 400,
"details": {
"validation": [
"customer.email is required and must be a valid email address"
]
}
}
}
Código HTTPSignificado
200 / 201Operación correcta.
400Error de validación o body mal formado.
401API key faltante, inválida o expirada.
403Permisos insuficientes, plan sin acceso al API o cupo de documentos del plan agotado (SUB_12002, SUB_12008).
404Recurso no encontrado.
409Conflicto: cliente o código de producto duplicado, o devReference ya usada con otro contenido (API_10011) o por un comprobante anulado (API_10012).
422Certificado P12 expirado al subirlo (CERT_3008) o dato del contribuyente que impide generar la clave de acceso (INVOICE_4016). Un P12 inválido o con contraseña incorrecta responde 400, y un rechazo del SRI no llega como error HTTP: lo ves en el status del comprobante (REJECTED).
429Rate limit por velocidad (GENERAL_0003) o límite propio de la API key (API_10002).
500Error interno. Reintenta con backoff; en los POST de emisión hazlo solo si enviaste devReference: sin ella, un reintento puede emitir un segundo comprobante.

Descarga la colección oficial e impórtala en Postman para probar el API en segundos. La colección incluye 45 peticiones organizadas en 8 carpetas (facturas, uso, certificado, clientes, productos, notas de crédito, notas de débito y retenciones) con ejemplos de request y response.

1) Descarga: /factuplan-developer.postman_collection.json
2) Abre Postman → Import → arrastra el archivo.
3) En la colección "Factuplan API" → pestaña Variables, reemplaza:
apiKey = tu API key (ak_test_* o ak_live_*)
taxpayerRuc = tu RUC de 13 dígitos
baseUrl ya viene pre-configurada en https://api-rest.factuplan.com.ec.

Descargar colección

Emisión completa de facturas electrónicas. La API soporta tres modos:

  • Modo A — Factuplan crea el XML, lo firma, lo autoriza ante el SRI, genera el PDF (RIDE) y manda el correo. Es el camino recomendado.
  • Modo B — Tú generas el XML sin firmar; Factuplan lo firma, lo envía al SRI y te devuelve la respuesta de autorización en la misma petición (sin PDF ni correo).
  • Modo C — Factuplan solo firma el XML; tú te encargas del envío al SRI.
POST /v1/developer/invoices

Flujo completo: crea el XML, lo firma con tu certificado P12, lo autoriza ante el SRI, genera el PDF (RIDE) y envía el correo al cliente automáticamente.

{
"customer": {
"identificationType": "RUC",
"identification": "0993378150001",
"legalName": "Empresa Demo S.A.",
"email": "facturas@empresademo.ec",
"address": "Guayaquil, Ecuador",
"phone": "+593958657236",
"saveToContacts": true
},
"items": [
{
"quantity": 1,
"code": "SERV-001",
"auxiliaryCode": "AUX-001",
"description": "Servicio de consultoría",
"unitPrice": 150.0,
"discount": 0,
"taxType": "IVA_RATE",
"tax": 15,
"saveToProducts": false
}
],
"establishment": "001",
"emissionPoint": "001",
"additionalInfo": { "Vendedor": "Juan Pérez" },
"sendEmail": true,
"devReference": "pedido-1001",
"payments": [
{
"method": "20",
"amount": 172.5,
"term": 30,
"timeUnit": "dias"
}
]
}
CampoTipoRequeridoNotas
customerIdstringNoSi se envía, ignora los demás campos y usa el cliente del directorio.
identificationTypeenumSíRUC, CEDULA, PASSPORT, FINAL_CONSUMER, EXTERIOR
identificationstringSíRUC = 13 dígitos · CEDULA = 10 dígitos.
legalNamestringSíRazón social o nombre completo.
emailstringNoCorreo al que se envía el RIDE. Es opcional: si el comprador no tiene correo, no lo inventes. Omite el campo o envíalo vacío ("" o null) y el comprobante se emite igual, sin enviar correo. Si lo omites y ese cliente ya tenía un correo guardado, se conserva y se usa; vacío o null lo borra. Un valor que no es un correo válido responde 400.
addressstringNoAparece en el RIDE.
phonestringNoFormato E.164: + seguido de 7 a 15 dígitos, sin espacios (ej. +593958657236). Otro formato responde 400.
saveToContactsbooleanNoSi true, crea o actualiza el cliente en tu directorio y lo reactiva si estaba inactivo. Aunque lo omitas, el cliente queda registrado para asociarlo al comprobante.
CampoTipoRequeridoNotas
quantitynumberSíCantidad facturada.
productIdstringNoSi se envía, los demás campos pueden omitirse.
codestringSíIdentificador del ítem.
auxiliaryCodestringNoCódigo secundario (SKU, código de barras…).
descriptionstringSíAparece en el RIDE.
unitPricenumberSíHasta 6 decimales de precisión.
discountnumberNoDescuento por línea.
taxTypeenumSíIVA_0, IVA_RATE, NOT_TAXABLE, EXEMPT.
taxnumberNoTarifa cuando taxType=IVA_RATE: 0, 5, 8, 12, 14, 15 (default 15). Otro valor responde 400.
saveToProductsbooleanNoSi true, lo guarda en tu catálogo.

Tres formas de resolverlo, en orden de prioridad:

  1. Por UUID — envía emissionPointId.
  2. Por códigos SRI — envía establishment + emissionPoint.
  3. Auto-detección — omite todo; si solo tienes un punto activo, lo usa.

Aplica cuando taxType es IVA_RATE. Default: 15.

taxCódigo SRIDescripción
00IVA 0%
55IVA 5%
88IVA 8%
122IVA 12%
143IVA 14%
154IVA 15% (default)

Cada entrada lleva el código SRI en method, el amount y, opcionalmente, term + timeUnit. Si omites payments, se aplica "01" (Sin utilización del sistema financiero).

La suma de los amount debe coincidir con el total con impuestos (tolerancia de 0.01). Si no, Factuplan rechaza la petición con 400 (INVOICE_4014) antes de enviar nada al SRI.

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
timeUnitDescripción
diasDías
mesesMeses
aniosAños

Dos campos opcionales del body no describen la factura, sino cómo se procesa:

CampoTipoDefaultNotas
sendEmailbooleantrueCon false no se envía el correo al cliente. El PDF (RIDE) se genera igual y el comprobante termina en COMPLETED como cualquier otro. Debe ser un booleano JSON: "false" como texto responde 400.
devReferencestring—Tu propia referencia para este comprobante (por ejemplo, tu número de pedido): texto libre de 1 a 100 caracteres. Hace idempotente la emisión: repetir la petición con la misma referencia no emite otra factura. Nunca se imprime en el comprobante. Ver Idempotencia.

Cualquier otro campo que el API no conozca (por ejemplo idempotencyKey) se rechaza con 400 y el mensaje property idempotencyKey should not exist.

{
"data": {
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"accessKey": "0104202601099337815000110010010000000011234567890",
"sequential": "000000001",
"status": "DRAFT",
"total": "172.5",
"source": "API",
"apiKeyId": "01964ac3-6a10-7c11-8d2e-1f2a3b4c5d6e",
"devReference": "pedido-1001",
"duplicate": false
},
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:34:56.789Z"
}
}

status empieza en DRAFT, pasa por estados intermedios (GENERATING_XML, SIGNING, SENDING_SRI, RECEIVED_SRI, AUTHORIZED, GENERATING_PDF, SENDING_EMAIL) y termina en COMPLETED, o en ERROR / REJECTED. AUTHORIZED es un estado de paso: el SRI ya autorizó el comprobante, pero el PDF puede no estar listo todavía. El estado final es COMPLETED, también cuando envías sendEmail: false. total llega como string decimal, y devReference y duplicate solo aparecen si enviaste devReference. Consulta el progreso con GET /v1/developer/receipts/{id}/status.

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

Envía tu propia referencia en el campo opcional devReference (por ejemplo, tu número de pedido) y repetir una petición nunca emitirá un segundo comprobante. Si tu sistema no recibe la respuesta o recibe un error del servidor (un timeout, un corte de red, un 5xx), el comprobante pudo quedar guardado: vuelve a enviar la misma petición con la misma referencia y recibirás el que ya existe.

Funciona en los cinco POST de emisión: /v1/developer/invoices, /v1/developer/credit-notes, /v1/developer/debit-notes, /v1/developer/withholdings y /v1/developer/purchase-settlements. Si no envías el campo, cada petición emite un comprobante nuevo.

SituaciónRespuesta
Primera petición con esa referencia201 · se emite el comprobante · duplicate: false
Misma referencia, mismo contenido201 · devuelve el comprobante que ya existe · duplicate: true
Misma referencia, contenido distinto409 · API_10011 · con los datos del comprobante existente
La referencia pertenece a un comprobante anulado409 · API_10012 · usa una referencia distinta

Si repites una petición que ya enviaste con "devReference": "pedido-1001", recibes otra vez 201 Created, con el mismo id y duplicate: true. No se emite ni se descuenta nada nuevo:

{
"data": {
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"accessKey": "0104202601099337815000110010010000000011234567890",
"sequential": "000000001",
"status": "COMPLETED",
"total": "172.5",
"source": "API",
"apiKeyId": "01964ac3-6a10-7c11-8d2e-1f2a3b4c5d6e",
"devReference": "pedido-1001",
"duplicate": true
},
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:40:00.000Z"
}
}

status es el estado actual del comprobante que ya existía: revísalo siempre, porque puede estar autorizado, en proceso o rechazado. Si sigue en DRAFT, espera: se procesa solo y puede tardar hasta unos 20 minutos.

Si la referencia ya pertenece a un comprobante y el body no es el mismo, el API responde 409 y no emite nada. Los datos del comprobante que tiene la referencia vienen en error.details:

{
"data": null,
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:40:00.000Z"
},
"error": {
"code": "API_10011",
"message": "Ya existe un documento con esa devReference (estado COMPLETED) y el contenido que envías no es el mismo. Si es el mismo documento, no lo envíes de nuevo: sus datos van en este error. Si es otro, usa una referencia distinta.",
"statusCode": 409,
"details": {
"message": "Ya existe un documento con esa devReference (estado COMPLETED) y el contenido que envías no es el mismo. Si es el mismo documento, no lo envíes de nuevo: sus datos van en este error. Si es otro, usa una referencia distinta.",
"devReference": "pedido-1001",
"receiptId": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"status": "COMPLETED",
"accessKey": "0104202601099337815000110010010000000011234567890",
"sequential": "000000001",
"total": "172.5",
"createdAt": "2026-05-13T12:34:56.789Z"
}
}
}

Si ese comprobante fue anulado, la respuesta tiene la misma forma con el código API_10012, también cuando el contenido es idéntico: la referencia de un comprobante anulado no se puede volver a usar.

  • Es texto libre de 1 a 100 caracteres. Se compara tal cual: distingue mayúsculas, y los espacios al inicio y al final no cuentan. Debe ser un texto JSON: un número de pedido va entre comillas. Vacía, de más de 100 caracteres o con saltos de línea responde 400.
  • Es única por contribuyente, ambiente y tipo de documento. Una factura y su nota de crédito pueden llevar la misma referencia.
  • Identifica un comprobante, no un pedido. Para emitir un comprobante corregido (por ejemplo, después de un rechazo del SRI) o un segundo comprobante del mismo pedido, usa una referencia distinta, como pedido-1001-2.
  • «Mismo contenido» compara el body tal como lo envías: cliente, líneas, pagos e información adicional. No cuentan el orden de los campos, los campos en null, las listas u objetos vacíos ni las opciones sendEmail, saveToContacts y saveToProducts. Todo lo demás sí: "discount": 0 no es lo mismo que omitirlo, así que un reintento debe mandar el mismo body.
  • La referencia vuelve en la respuesta del POST, en los listados, en el detalle (GET /v1/developer/receipts/{id}), en el estado, en el reintento (POST /v1/developer/invoices/{id}/retry y sus equivalentes de notas de crédito, notas de débito y retenciones), en la anulación y en los webhooks (junto con environment). Puedes buscar por ella con ?devReference=pedido-1001 en cualquier listado de comprobantes.
  • Repetir el POST no vuelve a procesar el comprobante: lo devuelve tal como está. Si quedó en ERROR, usa el endpoint de reintento de su tipo (las liquidaciones de compra no tienen uno); si necesitas uno corregido, emítelo con una referencia distinta.
  • No emitas la misma venta con otra referencia mientras no sepas qué pasó con la primera: terminarías con dos comprobantes. Consulta antes el listado con ?devReference=pedido-1001.
  • En producción una referencia usada queda ocupada para siempre. En pruebas los comprobantes se eliminan cada hora y sus referencias quedan libres otra vez, para que puedas repetir tus pruebas.
GET /v1/developer/invoices

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

ParámetroTipoDefaultNotas
pagenumber1Página solicitada.
limitnumber20Elementos por página (máx. 100).
rucstring—RUC o cédula del cliente (filtro opcional).
statusenum—Filtro exacto por estado: DRAFT, AUTHORIZED, COMPLETED, REJECTED, ERROR, VOIDED o cualquier otro estado del comprobante. Las facturas ya emitidas quedan en COMPLETED; AUTHORIZED solo devuelve las que aún no tienen su PDF.
dateFromstring—Fecha u hora de inicio ISO 8601. Una fecha sola (2025-01-01) se toma como las 00:00 UTC de ese día (las 19:00 del día anterior en Ecuador); para empezar a la medianoche de Ecuador envía el instante (ej: 2025-01-01T05:00:00Z).
dateTostring—Fecha u hora de fin ISO 8601. Una fecha sola (2025-12-31) corta a las 00:00 UTC de ese día y deja fuera lo emitido durante él; para incluir el día completo envía el instante final (ej: 2026-01-01T04:59:59Z, fin del 31 de diciembre en Ecuador).
devReferencestring—Tu propia referencia. Devuelve el comprobante que la tiene en el ambiente de tu API key (cero o uno). Vacía o de más de 100 caracteres responde 400. Ver Idempotencia.
{
"data": [
{
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"accessKey": "0104202601099337815000110010010000000011234567890",
"sequential": "000000007",
"status": "COMPLETED",
"total": "172.5",
"createdAt": "2026-05-13T12:34:56.789Z"
}
],
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:34:56.789Z",
"total": 120, "page": 1, "limit": 20, "totalPages": 6
}
}
POST /v1/developer/invoices/import

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

Envía la cabecera x-taxpayer-ruc para que la factura quede guardada bajo ese contribuyente. Sin ella, o con "save": false en el body, solo se verifica en el SRI: la respuesta trae id: null y no se guarda nada, así que no podrás usarla después en una nota de crédito o de débito.

Solo ak_live_*. No disponible con claves de pruebas.

{
"accessKey": "0104202601099337815000110010010000000011234567890"
}
{
"data": {
"id": "01964ac4-1d3f-7a52-b6c1-3e4f5a6b7c8d",
"accessKey": "0104202601099337815000110010010000000011234567890",
"sequential": "000000007",
"status": "AUTHORIZED",
"total": "230"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

Si tu sistema ya genera el XML de factura sin firmar, puedes enviarlo directamente a Factuplan para que lo firme con tu certificado y lo autorice ante el SRI.

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.

POST /v1/developer/sign-and-authorize

Headers

HeaderValor
X-API-Keyak_live_...
x-taxpayer-rucRUC del contribuyente emisor
Content-Typeapplication/json

Body

{
"xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><factura>...</factura>"
}
CampoTipoRequeridoDescripción
xmlstringSíXML de factura sin firmar, como string UTF-8. Debe incluir la declaración <?xml ...?>; sin ella la petición responde 400 (API_10004)

Respuesta 201 Created

{
"data": {
"authorized": true,
"authorizationNumber": "0104202601099337815000110010010000000011234567890",
"authorizationDate": "2026-05-13T12:34:56-05:00",
"authorizedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4...",
"messages": []
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
CampoTipoDescripción
authorizedbooleantrue si el SRI autorizó el comprobante; con false, revisa messages
authorizationNumberstring o nullNúmero de autorización del SRI
authorizationDatestring o nullFecha de autorización devuelta por el SRI
authorizedXmlstring o nullXML autorizado, codificado en Base64
messagesarrayMensajes devueltos por el SRI (vacío si no hay)

Ejemplo con cURL

Ventana de terminal
curl -X POST https://api-rest.factuplan.com.ec/v1/developer/sign-and-authorize \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "x-taxpayer-ruc: 0950194407001" \
-H "Content-Type: application/json" \
-d '{ "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><factura>...</factura>" }'

La respuesta ya trae el resultado del SRI. Este modo no guarda el comprobante en Factuplan: no hay id que consultar, ni PDF, ni correo.

POST /v1/developer/sign

Firma el XML con tu certificado P12 de forma síncrona, sin enviarlo al SRI. Útil si necesitas controlar el envío al SRI por tu cuenta.

Body

{
"xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><factura>...</factura>"
}

Respuesta 201 Created — signedXml llega codificado en Base64.

{
"data": {
"signedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4..."
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
POST /v1/developer/validate-xml

Hace una revisión básica de estructura, sin procesar ni enviar el XML: que tenga la declaración <?xml, un nodo raíz del SRI, <infoTributaria>, <detalles> y una <claveAcceso> de 49 dígitos. No valida contra el esquema XSD del SRI: un XML con valid: true todavía puede ser rechazado al enviarlo.

Body

{
"xml": "<factura>...</factura>"
}

Respuesta 201 Created

{
"data": {
"valid": false,
"errors": [
"Missing XML declaration",
"Missing <infoTributaria> section",
"Missing <detalles> section",
"Missing <claveAcceso> element"
]
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

Consultar comprobante externo por clave de acceso

Sección titulada «Consultar comprobante externo por clave de acceso»
POST /v1/developer/receipts/query

Consulta cualquier comprobante autorizado por el SRI (Facturas 01, Notas de Crédito 04, Guías de Remisión 06), sin importar qué sistema lo emitió. Si el contribuyente emisor no existe en tu workspace, se crea automáticamente con firma vacía.

Solo ak_live_*. No disponible con claves de pruebas.

Cómo se descuenta el contador: el crédito se descuenta cuando el comprobante está autorizado, sin importar el valor de save. Si no está autorizado, no se descuenta ningún crédito.

CampoTipoRequeridoNotas
accessKeystringSíClave de acceso SRI (49 dígitos).
savebooleanNo (default true)Si false, solo verifica sin guardar en BD ni generar PDF.
{
"accessKey": "0104202601019999999900110010010000000011234567813",
"save": true
}
GET /v1/developer/receipts/{id}

Devuelve todos los datos del comprobante: estado, clave de acceso, secuencial, totales y fechas.

{
"data": {
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"accessKey": "0104202601099337815000110010010000000011234567890",
"sequential": "000000001",
"status": "COMPLETED",
"total": "172.5",
"source": "API",
"apiKeyId": "01964ac3-6a10-7c11-8d2e-1f2a3b4c5d6e"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

Si el comprobante se emitió con devReference, la respuesta también la incluye.

GET /v1/developer/receipts/{id}/status

Devuelve el estado actual. Los que más vas a ver: DRAFT (recién creado), RECEIVED_SRI (recibido por el SRI, pendiente de autorización), AUTHORIZED (autorizado; el PDF puede no estar listo), COMPLETED (final), ERROR, REJECTED y VOIDED. Mientras se procesa también pasa por GENERATING_XML, SIGNING, SENDING_SRI, GENERATING_PDF y SENDING_EMAIL (y por PROCESSING tras un reintento).

{
"data": {
"status": "AUTHORIZED",
"authorizationNumber": "0104202601...",
"messages": []
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

Si el comprobante se emitió con devReference, la respuesta también la incluye.

GET /v1/developer/receipts/{id}/xml

Devuelve una URL pre-firmada del XML autorizado por el SRI (expira en 5 minutos) y una URL permanente de visualización (previewUrl) que no expira.

{
"data": {
"url": "https://files.factuplan.com.ec/...?sig=...",
"previewUrl": "https://app.factuplan.com.ec/verify?clave=0104202601099337815000110010010000000011234567890"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
GET /v1/developer/receipts/{id}/pdf

Devuelve una URL pre-firmada del PDF (RIDE) del comprobante (expira en 5 minutos) y una URL permanente de visualización (previewUrl).

{
"data": {
"url": "https://files.factuplan.com.ec/...?sig=...",
"previewUrl": "https://app.factuplan.com.ec/verify?clave=0104202601099337815000110010010000000011234567890"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

CRUD del directorio de clientes (receptores) que aparecerán en tus comprobantes.

POST /v1/developer/customers
{
"identificationType": "RUC",
"identification": "0993378150001",
"legalName": "Empresa Demo S.A.",
"email": "facturas@empresademo.ec",
"address": "Guayaquil, Ecuador",
"phone": "+593958657236",
"additionalEmail": "contabilidad@empresademo.ec"
}
CampoTipoRequeridoNotas
identificationTypeenumSíRUC, CEDULA, PASSPORT, FINAL_CONSUMER, EXTERIOR.
identificationstringSíValidamos el formato: RUC = 13 dígitos terminados en 001, cédula = 10 dígitos, pasaporte = 3 a 20 caracteres alfanuméricos, consumidor final = 9999999999999.
legalNamestringSíRazón social o nombre completo.
emailstringNoCorreo al que se envían los comprobantes de ese cliente. Es opcional: un cliente sin correo no recibe correos. Un valor que no es un correo válido responde 400.
addressstringNoAparece en el RIDE.
phonestringNoFormato libre.
additionalEmailstringNoSe guarda con el cliente. Hoy no se le envía copia del RIDE.
{
"data": {
"id": "01964ac3-5c1d-7b33-9e44-0a1b2c3d4e5f",
"taxpayerId": "01964ac3-4a0b-7d22-8c55-6f7e8d9c0b1a",
"identificationType": "RUC",
"identification": "0993378150001",
"legalName": "Empresa Demo S.A.",
"email": "facturas@empresademo.ec",
"isActive": true,
"createdAt": "2026-05-13T12:34:56.789Z",
"updatedAt": "2026-05-13T12:34:56.789Z"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
GET /v1/developer/customers
ParámetroTipoDefaultNotas
pagenumber1Página solicitada.
limitnumber20Elementos por página.
searchstring—Busca por nombre, identificación o email.
identificationTypeenum—RUC, CEDULA, PASSPORT, FINAL_CONSUMER, EXTERIOR.
isActiveboolean—Filtra por estado activo.
{
"data": [
{
"id": "01964ac3-5c1d-7b33-9e44-0a1b2c3d4e5f",
"taxpayerId": "01964ac3-4a0b-7d22-8c55-6f7e8d9c0b1a",
"identificationType": "RUC",
"identification": "0993378150001",
"legalName": "Empresa Demo S.A.",
"email": "facturas@empresademo.ec",
"isActive": true,
"createdAt": "2026-05-13T12:34:56.789Z",
"updatedAt": "2026-05-13T12:34:56.789Z"
}
],
"meta": {
"requestId": "req_1a2b3c",
"timestamp": "2026-05-13T12:34:56.789Z",
"total": 120, "page": 1, "limit": 20, "totalPages": 6
}
}
GET /v1/developer/customers/{id}

Devuelve el detalle completo de un cliente específico por su ID.

PATCH /v1/developer/customers/{id}

Actualiza uno o más campos. Solo se modifican los enviados en el body. Para quitarle el correo a un cliente envía "email": "".

{
"email": "nuevo@empresademo.ec",
"phone": "+593958657236",
"isActive": true
}
DELETE /v1/developer/customers/{id}

Desactiva el cliente (isActive pasa a false): ya no puedes usarlo por customerId al emitir, pero sigue apareciendo en los listados. No afecta los comprobantes ya emitidos a ese cliente.

{
"data": {
"id": "01964ac3-5c1d-7b33-9e44-0a1b2c3d4e5f",
"taxpayerId": "01964ac3-4a0b-7d22-8c55-6f7e8d9c0b1a",
"identificationType": "RUC",
"identification": "0993378150001",
"legalName": "Empresa Demo S.A.",
"email": "facturas@empresademo.ec",
"isActive": false,
"createdAt": "2026-05-13T12:34:56.789Z",
"updatedAt": "2026-05-13T12:40:00.000Z"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

CRUD del catálogo de ítems facturables. Funciona igual para productos tangibles y servicios.

POST /v1/developer/products
{
"code": "SERV-001",
"type": "SERVICE",
"name": "Servicio de consultoría",
"unitPrice": 150.0,
"taxType": "IVA_RATE",
"auxiliaryCode": "AUX-001",
"description": "Hora de consultoría técnica",
"iceCode": "",
"iceRate": 0,
"irbpnr": false,
"unitOfMeasure": "hora"
}
CampoTipoRequeridoNotas
codestringSíIdentificador único por contribuyente (máx. 25 caracteres).
typeenumNoPRODUCT (default), SERVICE.
namestringSíAparece en el RIDE.
unitPricenumberSíHasta 4 decimales de precisión.
taxTypeenumNoIVA_0, IVA_RATE (default), NOT_TAXABLE, EXEMPT.
auxiliaryCodestringNoCódigo secundario (SKU, código de barras…).
descriptionstringNoDescripción larga.
iceCodestringNoCódigo ICE del SRI.
iceRatenumberNoTarifa ICE aplicable.
irbpnrbooleanNotrue si el ítem aplica IRBPNR.
unitOfMeasurestringNoEj. unidad, kg, hora.
GET /v1/developer/products
ParámetroTipoDefaultNotas
pagenumber1Página solicitada.
limitnumber20Elementos por página.
searchstring—Busca por nombre, código o descripción.
typeenum—PRODUCT, SERVICE.
taxTypeenum—IVA_0, IVA_RATE, NOT_TAXABLE, EXEMPT.
isActiveboolean—Filtra por estado activo.
GET /v1/developer/products/{id}
PATCH /v1/developer/products/{id}
DELETE /v1/developer/products/{id}

PATCH acepta cualquier subconjunto de los campos de creación más isActive: boolean. DELETE no afecta comprobantes históricos que ya referencien el ítem.

GET /v1/developer/usage

Devuelve el uso de tu API key en el mes actual (operaciones registradas y costo estimado), el plan de la key y, si lo tiene, su límite propio. El cupo de documentos de tu suscripción no se consulta aquí.

{
"data": {
"currentMonth": { "documents": 273, "cost": 13.65 },
"plan": "DEVELOPER_PAY_AS_YOU_GO",
"monthlyQuota": null,
"monthlyUsed": 1840
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
CampoTipoDescripción
currentMonthobjectConsumo del mes en curso: documents (operaciones registradas con esta API key) y cost (costo estimado).
planstringPlan de la API key (ej. DEVELOPER_PAY_AS_YOU_GO).
monthlyQuotanumber o nullLímite propio de la API key; null si no tiene (lo habitual). El cupo de documentos de tu plan no se devuelve aquí.
monthlyUsednumberOperaciones acumuladas de la API key. Para el consumo del mes usa currentMonth.documents.
GET /v1/developer/certificate/status
x-taxpayer-ruc: 0950194407001

Indica si el certificado P12 configurado en tu cuenta está vigente, su fecha de vencimiento y el RUC del titular. Si no está vigente, los comprobantes no podrán firmarse.

{
"data": {
"hasCertificate": true,
"isExpired": false,
"daysUntilExpiry": 87,
"ruc": "0950194407001",
"legalName": "Mi Empresa S.A.",
"issuedAt": "2025-05-13T00:00:00.000Z",
"expiresAt": "2026-08-08T00:00:00.000Z",
"issuer": "Banco Central del Ecuador"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
CampoTipoDescripción
hasCertificatebooleanSi hay un certificado cargado.
isExpiredbooleantrue si el certificado expiró.
daysUntilExpirynumberDías restantes hasta el vencimiento.
rucstringRUC del titular del certificado.
legalNamestringNombre registrado en el certificado.
issuedAtstringFecha de emisión (ISO 8601).
expiresAtstringFecha de vencimiento (ISO 8601).
issuerstringEntidad certificadora emisora.

Si isExpired es true, renueva el certificado en Configuración → Certificado desde tu cuenta antes de seguir emitiendo.

POST /v1/developer/certificate
Content-Type: multipart/form-data

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.

CampoTipoRequeridoDescripción
filearchivosíArchivo P12 (.p12 o .pfx).
passwordstringsíContraseña del certificado.
Ventana de terminal
curl -X POST https://api-rest.factuplan.com.ec/v1/developer/certificate \
-H "x-api-key: ak_live_TU_CLAVE" \
-F "file=@/ruta/firma.p12" \
-F "password=MiContraseña"
{
"data": {
"hasCertificate": true,
"isExpired": false,
"daysUntilExpiry": 87,
"ruc": "0950194407001",
"legalName": "Mi Empresa S.A.",
"expiresAt": "2026-08-08T00:00:00.000Z",
"created": true
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
CampoTipoDescripción
hasCertificatebooleantrue si el certificado se subió correctamente.
isExpiredbooleanSi el certificado ya expiró.
daysUntilExpirynumberDías restantes hasta el vencimiento.
rucstringRUC extraído del certificado.
legalNamestringNombre legal extraído del certificado.
expiresAtstringFecha de vencimiento (ISO 8601).
createdbooleantrue si se creó automáticamente un nuevo contribuyente. Solo presente cuando se creó.

Una nota 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. La factura debe existir en Factuplan en estado AUTHORIZED o COMPLETED: si la emitiste en otro sistema, impórtala primero con POST /v1/developer/invoices/import. Si no existe, la petición responde 400 con el código CN_5001.

POST /v1/developer/credit-notes

Headers

HeaderValor
X-API-Keyak_live_...
x-taxpayer-rucRUC del contribuyente emisor
Content-Typeapplication/json

Body

{
"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"
}
]
}
CampoTipoRequeridoDescripció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. Si lo omites se usa el único punto de emisión activo del contribuyente; si tienes más de uno, envía emissionPointId o establishment + emissionPoint
paymentsarrayNoMétodos de pago
additionalInfoobjectNoCampos adicionales clave-valor
devReferencestringNoTu propia referencia (texto libre de 1 a 100 caracteres, por ejemplo tu número de pedido). Hace idempotente la emisión: si repites la petición con la misma referencia y el mismo contenido no se emite otro documento y recibes el existente con duplicate: true; con otro contenido responde 409 API_10011, y si ese documento fue anulado, 409 API_10012. Va en el body: no existe ningún header de idempotencia

Respuesta 201

{
"data": {
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"accessKey": "0412202404179214289100110010040000001231234567814",
"sequential": "000000001",
"status": "DRAFT",
"total": "115",
"source": "API",
"apiKeyId": "01964ac3-6a10-7c11-8d2e-1f2a3b4c5d6e"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

status empieza en DRAFT y sigue el mismo ciclo que una factura: pasa por AUTHORIZED cuando el SRI la autoriza y termina en COMPLETED. total llega como string. Si enviaste devReference, la respuesta añade devReference y duplicate (false si esta petición emitió la nota, true si ya existía y no se emitió nada nuevo).

GET /v1/developer/credit-notes

Lista las notas de crédito emitidas con paginación. Acepta los mismos filtros que el listado de facturas (page, limit, ruc, status, dateFrom, dateTo, devReference).

Una vez emitida, usa los endpoints genéricos de comprobantes:

GET /v1/developer/receipts/:id # Detalle
GET /v1/developer/receipts/:id/status # Estado
GET /v1/developer/receipts/:id/xml # XML autorizado
GET /v1/developer/receipts/:id/pdf # PDF RIDE
POST /v1/developer/receipts/:id/void # Anular (solo a nivel sistema)

Importante: La anulación es solo a nivel del sistema Factuplan. La notificación de anulación ante el SRI es responsabilidad del desarrollador.

PATCH /v1/developer/credit-notes/sequential

Fija el contador de secuencial de notas de crédito en el punto de emisión indicado. El valor que envías es el último secuencial usado: la siguiente nota de crédito se emite con ese valor + 1 (envía 99 para que la próxima sea la 000000100).

{
"branchCode": "001",
"emissionCode": "001",
"creditNoteSequential": 100
}
CampoTipoRequeridoDescripción
branchCodestringsíCódigo del establecimiento (3 dígitos, ej: "001")
emissionCodestringsíCódigo del punto de emisión (3 dígitos, ej: "001")
creditNoteSequentialintegersíÚltimo secuencial usado (≥ 0). La siguiente nota de crédito sale con este valor + 1

Si tu sistema ya genera el XML de nota de crédito sin firmar, puedes enviarlo a Factuplan para que lo firme con tu certificado y lo autorice ante el SRI. No necesitas importar antes la factura original: en este modo Factuplan no la consulta, solo firma tu XML y lo envía al SRI.

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.

Firmar y autorizar el XML de la nota de crédito

Ventana de terminal
curl -X POST https://api-rest.factuplan.com.ec/v1/developer/sign-and-authorize \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "x-taxpayer-ruc: 0950194407001" \
-H "Content-Type: application/json" \
-d '{ "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><notaCredito>...</notaCredito>" }'

Respuesta 201 Created

{
"data": {
"authorized": true,
"authorizationNumber": "0412202404179214289100110010040000001231234567814",
"authorizationDate": "2026-05-13T12:34:50-05:00",
"authorizedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4...",
"messages": []
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
CampoTipoDescripción
authorizedbooleantrue si el SRI autorizó el comprobante
authorizationNumberstring o nullNúmero de autorización del SRI
authorizationDatestring o nullFecha de autorización del SRI
authorizedXmlstring o nullXML autorizado, en base64
messagesarrayMensajes del SRI (por ejemplo, los motivos de un rechazo)

La respuesta es síncrona: este modo no guarda el comprobante en Factuplan, así que no hay id ni estado que consultar, y no se genera PDF ni se envía correo. Si authorized llega en false, revisa messages: el SRI lo rechazó o todavía no terminó de procesarlo.

Una nota 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.

POST /v1/developer/debit-notes

Emite una nota de débito a partir de una factura ya autorizada. Si la factura no existe en el sistema, impórtala primero con POST /v1/developer/invoices/import.

{
"invoiceAccessKey": "2312202301179214289100110010020000001231234567811",
"reasons": [
{
"description": "Ajuste de precio",
"amount": 10.00,
"taxType": "IVA_RATE"
}
],
"additionalInfo": {}
}
CampoTipoRequeridoDescripción
invoiceAccessKeystringSíClave de acceso de 49 dígitos de la factura original
reasonsarraySíMotivos del cargo: description, amount y taxType
additionalInfoobjectNoCampos adicionales clave-valor
devReferencestringNoTu propia referencia (texto libre de 1 a 100 caracteres, por ejemplo tu número de pedido). Hace idempotente la emisión: si repites la petición con la misma referencia y el mismo contenido no se emite otro documento y recibes el existente con duplicate: true; con otro contenido responde 409 API_10011, y si ese documento fue anulado, 409 API_10012. Va en el body: no existe ningún header de idempotencia

Cada entrada de reasons lleva:

CampoTipoRequeridoDescripción
descriptionstringSíConcepto del cargo (ej: “Ajuste de precio”)
amountnumberSíMonto del cargo
taxTypeenumSíIVA_RATE (IVA 15%), IVA_0, NOT_TAXABLE o EXEMPT. Cualquier otro valor (por ejemplo IVA) se acepta pero se calcula como IVA 0%
{
"data": {
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"accessKey": "0512202404179214289100110010050000001231234567815",
"sequential": "000000001",
"status": "DRAFT",
"total": "11.5",
"source": "API",
"apiKeyId": "01964ac3-6a10-7c11-8d2e-1f2a3b4c5d6e"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

status empieza en DRAFT, pasa por AUTHORIZED cuando el SRI la autoriza y termina en COMPLETED. total llega como string. Si enviaste devReference, la respuesta añade devReference y duplicate (false si esta petición emitió la nota, true si ya existía).

GET /v1/developer/debit-notes

Lista las notas de débito emitidas con paginación. Acepta los mismos filtros que el listado de facturas (page, limit, ruc, status, dateFrom, dateTo, devReference).

Una vez emitida, usa los endpoints genéricos de comprobantes:

GET /v1/developer/receipts/:id # Detalle
GET /v1/developer/receipts/:id/status # Estado
GET /v1/developer/receipts/:id/xml # XML autorizado
GET /v1/developer/receipts/:id/pdf # PDF RIDE
POST /v1/developer/receipts/:id/void # Anular (solo a nivel sistema)
PATCH /v1/developer/debit-notes/sequential

Mismo contrato que el secuencial de facturas, con el campo debitNoteSequential:

{
"branchCode": "001",
"emissionCode": "001",
"debitNoteSequential": 100
}

Si tu sistema ya genera el XML de nota de débito sin firmar, puedes enviarlo a Factuplan para que lo firme con tu certificado y lo autorice ante el SRI. No necesitas importar antes la factura original: en este modo Factuplan no la consulta, solo firma tu XML y lo envía al SRI.

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.

Firmar y autorizar el XML de la nota de débito

Ventana de terminal
curl -X POST https://api-rest.factuplan.com.ec/v1/developer/sign-and-authorize \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "x-taxpayer-ruc: 0950194407001" \
-H "Content-Type: application/json" \
-d '{ "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><notaDebito>...</notaDebito>" }'

Respuesta 201 Created

{
"data": {
"authorized": true,
"authorizationNumber": "0512202404179214289100110010050000001231234567815",
"authorizationDate": "2026-05-13T12:34:50-05:00",
"authorizedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4...",
"messages": []
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
CampoTipoDescripción
authorizedbooleantrue si el SRI autorizó el comprobante
authorizationNumberstring o nullNúmero de autorización del SRI
authorizationDatestring o nullFecha de autorización del SRI
authorizedXmlstring o nullXML autorizado, en base64
messagesarrayMensajes del SRI (por ejemplo, los motivos de un rechazo)

La respuesta es síncrona: este modo no guarda el comprobante en Factuplan, así que no hay id ni estado que consultar, y no se genera PDF ni se envía correo. Si authorized llega en false, revisa messages: el SRI lo rechazó o todavía no terminó de procesarlo.

Emisión de comprobantes de retención electrónica (codDoc 07) autorizados por el SRI.

POST /v1/developer/withholdings

Emite un comprobante de retención sobre una compra ya registrada en Factuplan (POST /v1/developer/purchases), identificada por la clave de acceso de 49 dígitos de la factura de tu proveedor. Si la compra no está registrada responde 404 WH_5304. El registro de la compra recibe { "accessKey": "..." } en el body, solo está disponible con ak_live_* y consume cupo de tu plan.

{
"purchaseAccessKey": "2312202301179214289100110010020000001231234567811",
"codSustento": "01",
"taxes": [
{
"taxType": "RENTA",
"taxRateCode": "303",
"taxRate": 10,
"taxBase": 1000.00
},
{
"taxType": "IVA",
"taxRateCode": "1",
"taxRate": 30,
"taxBase": 150.00
}
],
"additionalInfo": {}
}
CampoTipoRequeridoDescripción
purchaseAccessKeystringSíClave de acceso de 49 dígitos de la compra registrada que sustenta la retención (regístrala antes con POST /v1/developer/purchases)
codSustentostringSíCódigo de sustento del comprobante según la tabla del SRI (ej: 01)
taxesarraySíImpuestos a retener (al menos uno)
pagoLocExtstringNoTipo de pago: 01 local (por defecto) o 02 exterior
fiscalPeriodstringNoPeriodo fiscal MM/AAAA. Por defecto, el mes de emisión de la compra
additionalInfoobjectNoCampos adicionales clave-valor
devReferencestringNoTu propia referencia (texto libre de 1 a 100 caracteres, por ejemplo tu número de pedido). Hace idempotente la emisión: si repites la petición con la misma referencia y el mismo contenido no se emite otro documento y recibes el existente con duplicate: true; con otro contenido responde 409 API_10011, y si ese documento fue anulado, 409 API_10012. Va en el body: no existe ningún header de idempotencia

Cada entrada de taxes lleva:

CampoTipoRequeridoDescripción
taxTypeenumSíRENTA, IVA o ISD
taxRateCodestringSíCódigo SRI de la retención (ej: 303 renta, 1 IVA 30%)
taxRatenumberSíPorcentaje de retención
taxBasenumberSíBase imponible sobre la que se calcula
{
"data": {
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"accessKey": "0712202404179214289100110010070000001231234567817",
"sequential": "000000001",
"status": "DRAFT",
"totalWithheld": 145,
"purchaseId": "01964ac3-5f00-7a21-b3c4-9d8e7f6a5b4c"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

status empieza en DRAFT, pasa por AUTHORIZED cuando el SRI la autoriza y termina en COMPLETED. Si enviaste devReference, la respuesta añade devReference y duplicate (false si esta petición emitió la retención, true si ya existía).

GET /v1/developer/withholdings

Lista las retenciones emitidas con paginación. Filtros: page, limit, ruc (RUC o cédula del proveedor), status, dateFrom, dateTo, devReference.

Una vez emitida, usa los endpoints genéricos de comprobantes:

GET /v1/developer/receipts/:id # Detalle
GET /v1/developer/receipts/:id/status # Estado
GET /v1/developer/receipts/:id/xml # XML autorizado
GET /v1/developer/receipts/:id/pdf # PDF RIDE
POST /v1/developer/receipts/:id/void # Anular (solo a nivel sistema)

Si tu sistema ya genera el XML de retención sin firmar, puedes enviarlo a Factuplan para que lo firme con tu certificado y lo autorice ante el SRI. No necesitas registrar antes la compra en Factuplan: en este modo solo se firma tu XML y se envía al SRI.

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.

Firmar y autorizar el XML de la retención

Ventana de terminal
curl -X POST https://api-rest.factuplan.com.ec/v1/developer/sign-and-authorize \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "x-taxpayer-ruc: 0950194407001" \
-H "Content-Type: application/json" \
-d '{ "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><comprobanteRetencion>...</comprobanteRetencion>" }'

Respuesta 201 Created

{
"data": {
"authorized": true,
"authorizationNumber": "0712202404179214289100110010070000001231234567817",
"authorizationDate": "2026-05-13T12:34:50-05:00",
"authorizedXml": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4...",
"messages": []
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}
CampoTipoDescripción
authorizedbooleantrue si el SRI autorizó el comprobante
authorizationNumberstring o nullNúmero de autorización del SRI
authorizationDatestring o nullFecha de autorización del SRI
authorizedXmlstring o nullXML autorizado, en base64
messagesarrayMensajes del SRI (por ejemplo, los motivos de un rechazo)

La respuesta es síncrona: este modo no guarda el comprobante en Factuplan, así que no hay id ni estado que consultar, y no se genera PDF ni se envía correo. Si authorized llega en false, revisa messages: el SRI lo rechazó o todavía no terminó de procesarlo.

El API de desarrollador no tiene endpoints para emitir ni listar guías de remisión (/v1/developer/waybills no existe). Tienes dos caminos:

  • Desde el panel de Factuplan, donde se emiten como cualquier otro comprobante.
  • Con tu propio XML <guiaRemision>: envíalo sin firmar a POST /v1/developer/sign-and-authorize para que Factuplan lo firme y lo autorice ante el SRI, o envíalo ya firmado por ti a POST /v1/developer/authorize-signed (solo ak_live_*; el XML debe declarar el RUC de Factuplan como proveedor en infoAdicional, en el campoAdicional de nombre RUC Proveedor; si falta o no coincide, el error API_10009 o API_10010 trae el RUC esperado en error.details.expected). Los dos reciben el XML en el campo xml del body.

Por API la respuesta es síncrona y trae el resultado del SRI. El comprobante no queda guardado en Factuplan: no hay id que consultar, ni PDF, ni listado.

POST /v1/developer/receipts/{id}/void
x-taxpayer-ruc: 0950194407001

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

Importante: la anulación ante el SRI la debe gestionar el desarrollador de forma independiente. Este endpoint únicamente cambia el estado en Factuplan.

ParámetroDescripción
idID del comprobante a anular.
{
"reason": "Error en los datos del cliente"
}
CampoTipoRequeridoDescripción
reasonstringsíMotivo de la anulación. Obligatorio.
{
"data": {
"id": "01964ac3-7b2e-7e45-a9f2-2c3f4d5e6f7a",
"status": "VOIDED",
"accessKey": "2405202501179261786900110010010000000011234567816",
"voidReason": "Error en los datos del cliente",
"message": "Comprobante anulado correctamente"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" }
}

Si el comprobante se emitió con devReference, la respuesta también la incluye. Esa referencia queda ocupada: volver a emitir con ella responde 409 API_10012, así que el documento que lo reemplace necesita una referencia nueva.

Fija el contador de secuencial de facturas de un punto de emisión específico. El valor que envías es el último secuencial usado: la siguiente factura se emite con ese valor + 1 (envía 99 para que la próxima sea la 000000100).

PATCH /v1/developer/invoices/sequential
X-API-Key: ak_live_tu_api_key
x-taxpayer-ruc: 0950194407001
Content-Type: application/json
CampoTipoRequeridoDescripción
branchCodestringsíCódigo del establecimiento (3 dígitos, ej: "001")
emissionCodestringsíCódigo del punto de emisión (3 dígitos, ej: "001")
invoiceSequentialintegersíÚltimo secuencial usado (≥ 0). La siguiente factura sale con este valor + 1
{
"branchCode": "001",
"emissionCode": "001",
"invoiceSequential": 100
}
{
"data": {
"branchCode": "001",
"emissionCode": "001",
"invoiceSequential": 100,
"updatedAt": "2026-05-26T15:00:00.000Z"
},
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-26T15:00:00.000Z" }
}

Nota: Este endpoint afecta únicamente el secuencial de facturas. Para notas de crédito y notas de débito usa PATCH /v1/developer/credit-notes/sequential y PATCH /v1/developer/debit-notes/sequential (mismo contrato, con creditNoteSequential / debitNoteSequential).

Todos los endpoints devuelven el mismo formato de error: el sobre habitual con data: null y un objeto error.

{
"data": null,
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" },
"error": {
"code": "GENERAL_0002",
"message": "property foo should not exist",
"statusCode": 400,
"details": {
"validation": ["property foo should not exist"]
}
}
}

details es un objeto y solo aparece cuando hay información adicional: en los errores de validación trae la lista validation; en los demás, datos propios de cada código.

CódigoHTTPDescripción
INTEGRATION_8001401API key faltante, inválida, inactiva o expirada.
INTEGRATION_8005403El endpoint solo admite API keys de producción (ak_live_*).
INTEGRATION_8004403Tu plan no incluye acceso al API en producción.
SUB_12002403Cupo de documentos de tu plan agotado (SUB_12008 si también se agotó el excedente).
API_10002429Límite propio de la API key alcanzado (solo si tu API key tiene uno asignado).
GENERAL_0002400Error de validación: campo faltante o con tipo incorrecto, propiedad no permitida o header x-taxpayer-ruc ausente.
INVOICE_4003400Detalle de factura vacío.
CERT_3008422Certificado expirado.
CUSTOMER_7004409Cliente duplicado.
PRODUCT_6002409Código de producto duplicado.
GENERAL_0003429Rate limit por velocidad.
API_10011409La devReference ya pertenece a otro documento con contenido distinto.
API_10012409La devReference pertenece a un documento anulado.

Cada API key tiene dos límites:

  • Velocidad: 100 peticiones por segundo por API key.
  • Cupo de documentos: depende del plan contratado (consulta /precios).

Si superas el límite de velocidad recibes HTTP 429 (GENERAL_0003). Si agotas el cupo de documentos de tu plan, la emisión responde HTTP 403 con SUB_12002 (o SUB_12008 si también agotaste el excedente). Solo si tu API key tiene un límite propio, al alcanzarlo recibes HTTP 429 con API_10002.

Respuesta al superar el límite de velocidad

Sección titulada «Respuesta al superar el límite de velocidad»
{
"data": null,
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" },
"error": {
"code": "GENERAL_0003",
"message": "Has superado el límite de peticiones. Intenta de nuevo en unos segundos.",
"statusCode": 429,
"details": {
"message": "Has superado el límite de peticiones. Intenta de nuevo en unos segundos."
}
}
}

Respuesta al agotar el cupo de documentos del plan

Sección titulada «Respuesta al agotar el cupo de documentos del plan»
{
"data": null,
"meta": { "requestId": "req_1a2b3c", "timestamp": "2026-05-13T12:34:56.789Z" },
"error": {
"code": "SUB_12002",
"message": "Has alcanzado el límite de documentos de tu plan (500/500)",
"statusCode": 403,
"details": { "documentType": "INVOICE", "limit": 500, "used": 500, "reserved": 0 }
}
}

Implementa espera exponencial (500ms → 1s → 2s → 4s…) y un máximo de reintentos por petición. Un 429 por velocidad (GENERAL_0003) significa que la petición no se procesó, así que puedes repetirla tal cual. Si lo que falla es un timeout o un 5xx, no sabes si el comprobante se creó: envía siempre devReference en el body para que repetir la petición devuelva el mismo comprobante (duplicate: true) en lugar de emitir otro. No existe ningún header de idempotencia (ver Idempotencia). Ejemplo en Python con requests:

import os, time, requests
API = "https://api-rest.factuplan.com.ec/v1/developer"
HEADERS = {
"X-API-Key": os.environ["FACTUPLAN_API_KEY"],
"x-taxpayer-ruc": "0950194407001",
"Content-Type": "application/json",
}
def emit_invoice(body, intentos=3):
for i in range(intentos):
r = requests.post(f"{API}/invoices", json=body, headers=HEADERS, timeout=30)
if r.status_code != 429:
r.raise_for_status()
return r.json()
time.sleep(0.5 * (2 ** i))
r.raise_for_status()

Antes de procesar un evento webhook, verifica que el comprobante exista y consulta su estado actual con GET /v1/developer/receipts/{id}. Esto evita que actualices tu base de datos con eventos manipulados o duplicados.

Ventana de terminal
curl https://api-rest.factuplan.com.ec/v1/developer/receipts/$RECEIPT_ID \
-H "X-API-Key: $FACTUPLAN_API_KEY" \
-H "x-taxpayer-ruc: 0950194407001"

Tip: llama a este endpoint apenas recibes un evento webhook. Confirma que el comprobante existe antes de actualizar tu BD o disparar notificaciones a tus usuarios.


¿Necesitas ayuda integrando el API? Escríbenos a info@factuplan.com.ec o desde el Centro de ayuda. Si prefieres Node.js o TypeScript, te ahorra trabajo el SDK oficial.