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.
Instalación
Sección titulada «Instalación»No hay nada que instalar — el API se consume con cualquier cliente HTTP.
Crear una API key
Sección titulada «Crear una API key»- Inicia sesión en app.factuplan.com.ec.
- Anda a Developer → Create API key.
- Copia la clave generada — solo se muestra una vez.
- 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:
| Prefijo | Entorno | Comportamiento |
|---|---|---|
ak_test_* | Pruebas | Comprobantes ficticios, eliminados cada hora |
ak_live_* | Producción | Comprobantes reales firmados y enviados al SRI |
Prueba rápida con cURL
Sección titulada «Prueba rápida con cURL»curl https://api-rest.factuplan.com.ec/v1/developer/usage \ -H "X-API-Key: $FACTUPLAN_API_KEY" \ -H "Accept: application/json"Autenticación
Sección titulada «Autenticación»Todas las peticiones llevan X-API-Key; las que operan sobre un contribuyente
llevan además x-taxpayer-ruc:
| Cabecera | Valor | Notas |
|---|---|---|
X-API-Key | Tu API key (ak_test_* o ak_live_*) | Identifica el workspace y el plan. |
x-taxpayer-ruc | RUC 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. |
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.jsonSeguridad: nunca expongas tu API key en el frontend. Úsala solo desde tu servidor.
Convenciones
Sección titulada «Convenciones»Sobre de respuesta
Sección titulada «Sobre de respuesta»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 }}Sobre de error
Sección titulada «Sobre de error»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 HTTP | Significado |
|---|---|
200 / 201 | Operación correcta. |
400 | Error de validación o body mal formado. |
401 | API key faltante, inválida o expirada. |
403 | Permisos insuficientes, plan sin acceso al API o cupo de documentos del plan agotado (SUB_12002, SUB_12008). |
404 | Recurso no encontrado. |
409 | Conflicto: cliente o código de producto duplicado, o devReference ya usada con otro contenido (API_10011) o por un comprobante anulado (API_10012). |
422 | Certificado 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). |
429 | Rate limit por velocidad (GENERAL_0003) o límite propio de la API key (API_10002). |
500 | Error interno. Reintenta con backoff; en los POST de emisión hazlo solo si enviaste devReference: sin ella, un reintento puede emitir un segundo comprobante. |
Postman
Sección titulada «Postman»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.json2) 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.Comprobantes
Sección titulada «Comprobantes»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.
Emitir comprobante (Modo A)
Sección titulada «Emitir comprobante (Modo A)»POST /v1/developer/invoicesFlujo 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.
Request body
Sección titulada «Request body»{ "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" } ]}Campos del customer
Sección titulada «Campos del customer»| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
customerId | string | No | Si se envía, ignora los demás campos y usa el cliente del directorio. |
identificationType | enum | Sí | RUC, CEDULA, PASSPORT, FINAL_CONSUMER, EXTERIOR |
identification | string | Sí | RUC = 13 dígitos · CEDULA = 10 dígitos. |
legalName | string | Sí | Razón social o nombre completo. |
email | string | No | Correo 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. |
address | string | No | Aparece en el RIDE. |
phone | string | No | Formato E.164: + seguido de 7 a 15 dígitos, sin espacios (ej. +593958657236). Otro formato responde 400. |
saveToContacts | boolean | No | Si 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. |
Campos de cada item
Sección titulada «Campos de cada item»| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
quantity | number | Sí | Cantidad facturada. |
productId | string | No | Si se envía, los demás campos pueden omitirse. |
code | string | Sí | Identificador del ítem. |
auxiliaryCode | string | No | Código secundario (SKU, código de barras…). |
description | string | Sí | Aparece en el RIDE. |
unitPrice | number | Sí | Hasta 6 decimales de precisión. |
discount | number | No | Descuento por línea. |
taxType | enum | Sí | IVA_0, IVA_RATE, NOT_TAXABLE, EXEMPT. |
tax | number | No | Tarifa cuando taxType=IVA_RATE: 0, 5, 8, 12, 14, 15 (default 15). Otro valor responde 400. |
saveToProducts | boolean | No | Si true, lo guarda en tu catálogo. |
Punto de emisión
Sección titulada «Punto de emisión»Tres formas de resolverlo, en orden de prioridad:
- Por UUID — envía
emissionPointId. - Por códigos SRI — envía
establishment+emissionPoint. - Auto-detección — omite todo; si solo tienes un punto activo, lo usa.
Tarifas de IVA (tax)
Sección titulada «Tarifas de IVA (tax)»Aplica cuando taxType es IVA_RATE. Default: 15.
tax | Código SRI | Descripción |
|---|---|---|
0 | 0 | IVA 0% |
5 | 5 | IVA 5% |
8 | 8 | IVA 8% |
12 | 2 | IVA 12% |
14 | 3 | IVA 14% |
15 | 4 | IVA 15% (default) |
Formas de pago (payments)
Sección titulada «Formas de pago (payments)»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
amountdebe coincidir con el total con impuestos (tolerancia de0.01). Si no, Factuplan rechaza la petición con400(INVOICE_4014) antes de enviar nada al SRI.
| Código | Descripción |
|---|---|
01 | Sin utilización del sistema financiero |
15 | Compensación de deudas |
16 | Tarjeta de débito |
17 | Dinero electrónico |
18 | Tarjeta prepago |
19 | Tarjeta de crédito |
20 | Otros con utilización del sistema financiero |
21 | Endoso de títulos |
timeUnit | Descripción |
|---|---|
dias | Días |
meses | Meses |
anios | Años |
Campos de control
Sección titulada «Campos de control»Dos campos opcionales del body no describen la factura, sino cómo se procesa:
| Campo | Tipo | Default | Notas |
|---|---|---|---|
sendEmail | boolean | true | Con 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. |
devReference | string | — | 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.
Respuesta 201 Created
Sección titulada «Respuesta 201 Created»{ "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.
Ejemplo con cURL
Sección titulada «Ejemplo con cURL»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 } ] }'Idempotencia
Sección titulada «Idempotencia»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ón | Respuesta |
|---|---|
| Primera petición con esa referencia | 201 · se emite el comprobante · duplicate: false |
| Misma referencia, mismo contenido | 201 · devuelve el comprobante que ya existe · duplicate: true |
| Misma referencia, contenido distinto | 409 · API_10011 · con los datos del comprobante existente |
| La referencia pertenece a un comprobante anulado | 409 · API_10012 · usa una referencia distinta |
Petición repetida
Sección titulada «Petición repetida»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.
Misma referencia, otro contenido
Sección titulada «Misma referencia, otro contenido»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.
Reglas de la referencia
Sección titulada «Reglas de la referencia»- 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 opcionessendEmail,saveToContactsysaveToProducts. Todo lo demás sí:"discount": 0no 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}/retryy sus equivalentes de notas de crédito, notas de débito y retenciones), en la anulación y en los webhooks (junto conenvironment). Puedes buscar por ella con?devReference=pedido-1001en cualquier listado de comprobantes. - Repetir el
POSTno vuelve a procesar el comprobante: lo devuelve tal como está. Si quedó enERROR, 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.
Listar facturas
Sección titulada «Listar facturas»GET /v1/developer/invoicesLista las facturas emitidas con paginación. Puedes filtrar por RUC del cliente, estado y rango de fechas.
Query parameters
Sección titulada «Query parameters»| Parámetro | Tipo | Default | Notas |
|---|---|---|---|
page | number | 1 | Página solicitada. |
limit | number | 20 | Elementos por página (máx. 100). |
ruc | string | — | RUC o cédula del cliente (filtro opcional). |
status | enum | — | 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. |
dateFrom | string | — | 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). |
dateTo | string | — | 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). |
devReference | string | — | 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. |
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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 }}Importar comprobante por clave de acceso
Sección titulada «Importar comprobante por clave de acceso»POST /v1/developer/invoices/importImporta 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.
Request body
Sección titulada «Request body»{ "accessKey": "0104202601099337815000110010010000000011234567890"}Respuesta 201 Created
Sección titulada «Respuesta 201 Created»{ "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" }}Firmar y autorizar XML (Modo B)
Sección titulada «Firmar y autorizar XML (Modo B)»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-authorizeHeaders
| Header | Valor |
|---|---|
X-API-Key | ak_live_... |
x-taxpayer-ruc | RUC del contribuyente emisor |
Content-Type | application/json |
Body
{ "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><factura>...</factura>"}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
xml | string | Sí | 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" }}| Campo | Tipo | Descripción |
|---|---|---|
authorized | boolean | true si el SRI autorizó el comprobante; con false, revisa messages |
authorizationNumber | string o null | Número de autorización del SRI |
authorizationDate | string o null | Fecha de autorización devuelta por el SRI |
authorizedXml | string o null | XML autorizado, codificado en Base64 |
messages | array | Mensajes devueltos por el SRI (vacío si no hay) |
Ejemplo con cURL
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.
Firmar XML sin enviar al SRI (Modo C)
Sección titulada «Firmar XML sin enviar al SRI (Modo C)»POST /v1/developer/signFirma 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" }}Validar estructura de XML
Sección titulada «Validar estructura de XML»POST /v1/developer/validate-xmlHace 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 comprobantes
Sección titulada «Consultar comprobantes»Consultar comprobante externo por clave de acceso
Sección titulada «Consultar comprobante externo por clave de acceso»POST /v1/developer/receipts/queryConsulta 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.
Request body
Sección titulada «Request body»| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
accessKey | string | Sí | Clave de acceso SRI (49 dígitos). |
save | boolean | No (default true) | Si false, solo verifica sin guardar en BD ni generar PDF. |
{ "accessKey": "0104202601019999999900110010010000000011234567813", "save": true}Obtener detalle del comprobante
Sección titulada «Obtener detalle del comprobante»GET /v1/developer/receipts/{id}Devuelve todos los datos del comprobante: estado, clave de acceso, secuencial, totales y fechas.
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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.
Consultar estado del comprobante
Sección titulada «Consultar estado del comprobante»GET /v1/developer/receipts/{id}/statusDevuelve 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).
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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.
Descargar XML autorizado
Sección titulada «Descargar XML autorizado»GET /v1/developer/receipts/{id}/xmlDevuelve 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.
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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" }}Descargar PDF (RIDE)
Sección titulada «Descargar PDF (RIDE)»GET /v1/developer/receipts/{id}/pdfDevuelve una URL pre-firmada del PDF (RIDE) del comprobante (expira en
5 minutos) y una URL permanente de visualización (previewUrl).
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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" }}Clientes
Sección titulada «Clientes»CRUD del directorio de clientes (receptores) que aparecerán en tus comprobantes.
Crear cliente
Sección titulada «Crear cliente»POST /v1/developer/customersRequest body
Sección titulada «Request body»{ "identificationType": "RUC", "identification": "0993378150001", "legalName": "Empresa Demo S.A.", "email": "facturas@empresademo.ec", "address": "Guayaquil, Ecuador", "phone": "+593958657236", "additionalEmail": "contabilidad@empresademo.ec"}| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
identificationType | enum | Sí | RUC, CEDULA, PASSPORT, FINAL_CONSUMER, EXTERIOR. |
identification | string | Sí | 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. |
legalName | string | Sí | Razón social o nombre completo. |
email | string | No | Correo 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. |
address | string | No | Aparece en el RIDE. |
phone | string | No | Formato libre. |
additionalEmail | string | No | Se guarda con el cliente. Hoy no se le envía copia del RIDE. |
Respuesta 201 Created
Sección titulada «Respuesta 201 Created»{ "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" }}Listar clientes
Sección titulada «Listar clientes»GET /v1/developer/customersQuery parameters
Sección titulada «Query parameters»| Parámetro | Tipo | Default | Notas |
|---|---|---|---|
page | number | 1 | Página solicitada. |
limit | number | 20 | Elementos por página. |
search | string | — | Busca por nombre, identificación o email. |
identificationType | enum | — | RUC, CEDULA, PASSPORT, FINAL_CONSUMER, EXTERIOR. |
isActive | boolean | — | Filtra por estado activo. |
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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 }}Obtener cliente
Sección titulada «Obtener cliente»GET /v1/developer/customers/{id}Devuelve el detalle completo de un cliente específico por su ID.
Actualizar cliente
Sección titulada «Actualizar cliente»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}Eliminar cliente
Sección titulada «Eliminar cliente»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.
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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" }}Productos
Sección titulada «Productos»CRUD del catálogo de ítems facturables. Funciona igual para productos tangibles y servicios.
Crear producto o servicio
Sección titulada «Crear producto o servicio»POST /v1/developer/productsRequest body
Sección titulada «Request body»{ "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"}| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
code | string | Sí | Identificador único por contribuyente (máx. 25 caracteres). |
type | enum | No | PRODUCT (default), SERVICE. |
name | string | Sí | Aparece en el RIDE. |
unitPrice | number | Sí | Hasta 4 decimales de precisión. |
taxType | enum | No | IVA_0, IVA_RATE (default), NOT_TAXABLE, EXEMPT. |
auxiliaryCode | string | No | Código secundario (SKU, código de barras…). |
description | string | No | Descripción larga. |
iceCode | string | No | Código ICE del SRI. |
iceRate | number | No | Tarifa ICE aplicable. |
irbpnr | boolean | No | true si el ítem aplica IRBPNR. |
unitOfMeasure | string | No | Ej. unidad, kg, hora. |
Listar productos
Sección titulada «Listar productos»GET /v1/developer/productsQuery parameters
Sección titulada «Query parameters»| Parámetro | Tipo | Default | Notas |
|---|---|---|---|
page | number | 1 | Página solicitada. |
limit | number | 20 | Elementos por página. |
search | string | — | Busca por nombre, código o descripción. |
type | enum | — | PRODUCT, SERVICE. |
taxType | enum | — | IVA_0, IVA_RATE, NOT_TAXABLE, EXEMPT. |
isActive | boolean | — | Filtra por estado activo. |
Obtener, actualizar y eliminar
Sección titulada «Obtener, actualizar y eliminar»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.
Uso & Cuota
Sección titulada «Uso & Cuota»GET /v1/developer/usageDevuelve 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í.
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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" }}| Campo | Tipo | Descripción |
|---|---|---|
currentMonth | object | Consumo del mes en curso: documents (operaciones registradas con esta API key) y cost (costo estimado). |
plan | string | Plan de la API key (ej. DEVELOPER_PAY_AS_YOU_GO). |
monthlyQuota | number o null | Límite propio de la API key; null si no tiene (lo habitual). El cupo de documentos de tu plan no se devuelve aquí. |
monthlyUsed | number | Operaciones acumuladas de la API key. Para el consumo del mes usa currentMonth.documents. |
Certificado digital
Sección titulada «Certificado digital»GET /v1/developer/certificate/statusx-taxpayer-ruc: 0950194407001Indica 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.
Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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" }}| Campo | Tipo | Descripción |
|---|---|---|
hasCertificate | boolean | Si hay un certificado cargado. |
isExpired | boolean | true si el certificado expiró. |
daysUntilExpiry | number | Días restantes hasta el vencimiento. |
ruc | string | RUC del titular del certificado. |
legalName | string | Nombre registrado en el certificado. |
issuedAt | string | Fecha de emisión (ISO 8601). |
expiresAt | string | Fecha de vencimiento (ISO 8601). |
issuer | string | Entidad certificadora emisora. |
Si
isExpiredestrue, renueva el certificado en Configuración → Certificado desde tu cuenta antes de seguir emitiendo.
Subir firma electrónica (P12)
Sección titulada «Subir firma electrónica (P12)»POST /v1/developer/certificateContent-Type: multipart/form-dataSube 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.
Campos del body (multipart/form-data)
Sección titulada «Campos del body (multipart/form-data)»| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
file | archivo | sí | Archivo P12 (.p12 o .pfx). |
password | string | sí | Contraseña del certificado. |
Ejemplo con cURL
Sección titulada «Ejemplo con cURL»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"Respuesta 201 Created
Sección titulada «Respuesta 201 Created»{ "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" }}| Campo | Tipo | Descripción |
|---|---|---|
hasCertificate | boolean | true si el certificado se subió correctamente. |
isExpired | boolean | Si el certificado ya expiró. |
daysUntilExpiry | number | Días restantes hasta el vencimiento. |
ruc | string | RUC extraído del certificado. |
legalName | string | Nombre legal extraído del certificado. |
expiresAt | string | Fecha de vencimiento (ISO 8601). |
created | boolean | true si se creó automáticamente un nuevo contribuyente. Solo presente cuando se creó. |
Notas de crédito
Sección titulada «Notas de crédito»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.
Emitir nota de crédito
Sección titulada «Emitir nota de crédito»POST /v1/developer/credit-notesHeaders
| Header | Valor |
|---|---|
X-API-Key | ak_live_... |
x-taxpayer-ruc | RUC del contribuyente emisor |
Content-Type | application/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" } ]}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
invoiceAccessKey | string | Sí | Clave de acceso de 49 dígitos de la factura original |
reason | string | Sí | Motivo de la nota de crédito |
items | array | Sí | Ítems a acreditar (misma estructura que en facturas) |
emissionPointId | string | No | UUID 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 |
payments | array | No | Métodos de pago |
additionalInfo | object | No | Campos adicionales clave-valor |
devReference | string | No | Tu 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).
Listar notas de crédito
Sección titulada «Listar notas de crédito»GET /v1/developer/credit-notesLista 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).
Consultar, descargar y anular
Sección titulada «Consultar, descargar y anular»Una vez emitida, usa los endpoints genéricos de comprobantes:
GET /v1/developer/receipts/:id # DetalleGET /v1/developer/receipts/:id/status # EstadoGET /v1/developer/receipts/:id/xml # XML autorizadoGET /v1/developer/receipts/:id/pdf # PDF RIDEPOST /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.
Actualizar secuencial de notas de crédito
Sección titulada «Actualizar secuencial de notas de crédito»PATCH /v1/developer/credit-notes/sequentialFija 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}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
branchCode | string | sí | Código del establecimiento (3 dígitos, ej: "001") |
emissionCode | string | sí | Código del punto de emisión (3 dígitos, ej: "001") |
creditNoteSequential | integer | sí | Último secuencial usado (≥ 0). La siguiente nota de crédito sale con este valor + 1 |
Firmar XML
Sección titulada «Firmar XML»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
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" }}| Campo | Tipo | Descripción |
|---|---|---|
authorized | boolean | true si el SRI autorizó el comprobante |
authorizationNumber | string o null | Número de autorización del SRI |
authorizationDate | string o null | Fecha de autorización del SRI |
authorizedXml | string o null | XML autorizado, en base64 |
messages | array | Mensajes 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.
Notas de débito
Sección titulada «Notas de débito»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.
Emitir nota de débito
Sección titulada «Emitir nota de débito»POST /v1/developer/debit-notesEmite 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.
Request body
Sección titulada «Request body»{ "invoiceAccessKey": "2312202301179214289100110010020000001231234567811", "reasons": [ { "description": "Ajuste de precio", "amount": 10.00, "taxType": "IVA_RATE" } ], "additionalInfo": {}}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
invoiceAccessKey | string | Sí | Clave de acceso de 49 dígitos de la factura original |
reasons | array | Sí | Motivos del cargo: description, amount y taxType |
additionalInfo | object | No | Campos adicionales clave-valor |
devReference | string | No | Tu 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:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
description | string | Sí | Concepto del cargo (ej: “Ajuste de precio”) |
amount | number | Sí | Monto del cargo |
taxType | enum | Sí | IVA_RATE (IVA 15%), IVA_0, NOT_TAXABLE o EXEMPT. Cualquier otro valor (por ejemplo IVA) se acepta pero se calcula como IVA 0% |
Respuesta 201 Created
Sección titulada «Respuesta 201 Created»{ "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).
Listar notas de débito
Sección titulada «Listar notas de débito»GET /v1/developer/debit-notesLista 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).
Consultar, descargar y anular
Sección titulada «Consultar, descargar y anular»Una vez emitida, usa los endpoints genéricos de comprobantes:
GET /v1/developer/receipts/:id # DetalleGET /v1/developer/receipts/:id/status # EstadoGET /v1/developer/receipts/:id/xml # XML autorizadoGET /v1/developer/receipts/:id/pdf # PDF RIDEPOST /v1/developer/receipts/:id/void # Anular (solo a nivel sistema)Actualizar secuencial de notas de débito
Sección titulada «Actualizar secuencial de notas de débito»PATCH /v1/developer/debit-notes/sequentialMismo contrato que el secuencial de facturas, con el campo
debitNoteSequential:
{ "branchCode": "001", "emissionCode": "001", "debitNoteSequential": 100}Firmar XML
Sección titulada «Firmar XML»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
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" }}| Campo | Tipo | Descripción |
|---|---|---|
authorized | boolean | true si el SRI autorizó el comprobante |
authorizationNumber | string o null | Número de autorización del SRI |
authorizationDate | string o null | Fecha de autorización del SRI |
authorizedXml | string o null | XML autorizado, en base64 |
messages | array | Mensajes 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.
Retenciones
Sección titulada «Retenciones»Emisión de comprobantes de retención electrónica (codDoc 07) autorizados
por el SRI.
Emitir retención
Sección titulada «Emitir retención»POST /v1/developer/withholdingsEmite 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.
Request body
Sección titulada «Request body»{ "purchaseAccessKey": "2312202301179214289100110010020000001231234567811", "codSustento": "01", "taxes": [ { "taxType": "RENTA", "taxRateCode": "303", "taxRate": 10, "taxBase": 1000.00 }, { "taxType": "IVA", "taxRateCode": "1", "taxRate": 30, "taxBase": 150.00 } ], "additionalInfo": {}}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
purchaseAccessKey | string | Sí | Clave de acceso de 49 dígitos de la compra registrada que sustenta la retención (regístrala antes con POST /v1/developer/purchases) |
codSustento | string | Sí | Código de sustento del comprobante según la tabla del SRI (ej: 01) |
taxes | array | Sí | Impuestos a retener (al menos uno) |
pagoLocExt | string | No | Tipo de pago: 01 local (por defecto) o 02 exterior |
fiscalPeriod | string | No | Periodo fiscal MM/AAAA. Por defecto, el mes de emisión de la compra |
additionalInfo | object | No | Campos adicionales clave-valor |
devReference | string | No | Tu 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:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
taxType | enum | Sí | RENTA, IVA o ISD |
taxRateCode | string | Sí | Código SRI de la retención (ej: 303 renta, 1 IVA 30%) |
taxRate | number | Sí | Porcentaje de retención |
taxBase | number | Sí | Base imponible sobre la que se calcula |
Respuesta 201 Created
Sección titulada «Respuesta 201 Created»{ "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).
Listar retenciones
Sección titulada «Listar retenciones»GET /v1/developer/withholdingsLista las retenciones emitidas con paginación. Filtros: page, limit,
ruc (RUC o cédula del proveedor), status, dateFrom, dateTo, devReference.
Consultar, descargar y anular
Sección titulada «Consultar, descargar y anular»Una vez emitida, usa los endpoints genéricos de comprobantes:
GET /v1/developer/receipts/:id # DetalleGET /v1/developer/receipts/:id/status # EstadoGET /v1/developer/receipts/:id/xml # XML autorizadoGET /v1/developer/receipts/:id/pdf # PDF RIDEPOST /v1/developer/receipts/:id/void # Anular (solo a nivel sistema)Firmar XML
Sección titulada «Firmar XML»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
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" }}| Campo | Tipo | Descripción |
|---|---|---|
authorized | boolean | true si el SRI autorizó el comprobante |
authorizationNumber | string o null | Número de autorización del SRI |
authorizationDate | string o null | Fecha de autorización del SRI |
authorizedXml | string o null | XML autorizado, en base64 |
messages | array | Mensajes 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.
Guías de remisión
Sección titulada «Guías de remisión»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 aPOST /v1/developer/sign-and-authorizepara que Factuplan lo firme y lo autorice ante el SRI, o envíalo ya firmado por ti aPOST /v1/developer/authorize-signed(soloak_live_*; el XML debe declarar el RUC de Factuplan como proveedor eninfoAdicional, en elcampoAdicionalde nombreRUC Proveedor; si falta o no coincide, el errorAPI_10009oAPI_10010trae el RUC esperado enerror.details.expected). Los dos reciben el XML en el campoxmldel 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.
Anular comprobante
Sección titulada «Anular comprobante»POST /v1/developer/receipts/{id}/voidx-taxpayer-ruc: 0950194407001Anula 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ámetros de ruta
Sección titulada «Parámetros de ruta»| Parámetro | Descripción |
|---|---|
id | ID del comprobante a anular. |
Body JSON
Sección titulada «Body JSON»{ "reason": "Error en los datos del cliente"}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
reason | string | sí | Motivo de la anulación. Obligatorio. |
Respuesta 201 Created
Sección titulada «Respuesta 201 Created»{ "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.
Actualizar secuencial de facturas
Sección titulada «Actualizar secuencial de facturas»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/sequentialX-API-Key: ak_live_tu_api_keyx-taxpayer-ruc: 0950194407001Content-Type: application/jsonCuerpo de la petición
Sección titulada «Cuerpo de la petición»| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
branchCode | string | sí | Código del establecimiento (3 dígitos, ej: "001") |
emissionCode | string | sí | Código del punto de emisión (3 dígitos, ej: "001") |
invoiceSequential | integer | sí | Último secuencial usado (≥ 0). La siguiente factura sale con este valor + 1 |
{ "branchCode": "001", "emissionCode": "001", "invoiceSequential": 100}Respuesta 200 OK
Sección titulada «Respuesta 200 OK»{ "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/sequentialyPATCH /v1/developer/debit-notes/sequential(mismo contrato, concreditNoteSequential/debitNoteSequential).
Errores
Sección titulada «Errores»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ódigos frecuentes
Sección titulada «Códigos frecuentes»| Código | HTTP | Descripción |
|---|---|---|
INTEGRATION_8001 | 401 | API key faltante, inválida, inactiva o expirada. |
INTEGRATION_8005 | 403 | El endpoint solo admite API keys de producción (ak_live_*). |
INTEGRATION_8004 | 403 | Tu plan no incluye acceso al API en producción. |
SUB_12002 | 403 | Cupo de documentos de tu plan agotado (SUB_12008 si también se agotó el excedente). |
API_10002 | 429 | Límite propio de la API key alcanzado (solo si tu API key tiene uno asignado). |
GENERAL_0002 | 400 | Error de validación: campo faltante o con tipo incorrecto, propiedad no permitida o header x-taxpayer-ruc ausente. |
INVOICE_4003 | 400 | Detalle de factura vacío. |
CERT_3008 | 422 | Certificado expirado. |
CUSTOMER_7004 | 409 | Cliente duplicado. |
PRODUCT_6002 | 409 | Código de producto duplicado. |
GENERAL_0003 | 429 | Rate limit por velocidad. |
API_10011 | 409 | La devReference ya pertenece a otro documento con contenido distinto. |
API_10012 | 409 | La devReference pertenece a un documento anulado. |
Rate limit
Sección titulada «Rate limit»Cada API key tiene dos límites:
- Velocidad:
100peticiones 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 } }}Manejo recomendado con reintentos
Sección titulada «Manejo recomendado con reintentos»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()Webhooks
Sección titulada «Webhooks»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.
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.