POST /v1/documents/invoices
Scope: documents:write
Emite una factura electrónica. Modo síncrono por defecto; agrega ?async=true o X-Emit-Async: true para modo asíncrono.
Campos del body — Comprador
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
buyerIdType | string | Sí | 04 RUC · 05 Cédula · 06 Pasaporte · 07 Consumidor final · 08 ID exterior. |
buyerId | string | Sí | Máx. 20 chars. RUC 13 dígitos terminados en 001. Consumidor final: 9999999999999. |
buyerName | string | Sí | Razón social o nombre. Máx. 300 chars. |
buyerAddress | string | No | Dirección. Máx. 300 chars. |
buyerEmail | string | No | Si se envía, se entrega XML/RIDE al comprador por correo. |
issueDate | string | Sí | Formato YYYY-MM-DD. No puede ser futura. |
Campos del body — Ítems (items[])
Al menos 1 ítem requerido.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
description | string | Sí | Descripción. Máx. 300 chars. |
quantity | number | Sí | > 0. Hasta 6 decimales. |
unitPrice | number | Sí | ≥ 0. Hasta 6 decimales. |
taxes | array | Sí | Al menos 1 impuesto. taxCode, rateCode; para IVA rate/taxableBase opcionales. |
mainCode | string | No | Código interno. Recomendado para partial-refund. |
discount | number | No | Descuento en valor. Default 0. |
Pagos (payments[])
Si se omite, el servidor genera un pago único method: "20" por el total.
| Campo | Obligatorio | Descripción |
|---|---|---|
method | Sí | 01 efectivo, 19 tarjeta crédito, 20 otros sistema financiero, etc. |
amount | Sí | La suma debe ser igual al totalAmount. |
termDays, termUnit | Condicional | Si termDays > 0, termUnit es obligatorio. |
Reglas de negocio SRI
| Regla | Código | HTTP |
|---|---|---|
issueDate no puede ser futura | FUTURE_ISSUE_DATE | 422 |
| RUC / cédula / consumidor final formato incorrecto | INVALID_BUYER_ID | 422 |
| IVA 0% con Consumidor Final | IVA_ZERO_CONSUMIDOR_FINAL | 422 |
termDays > 0 sin termUnit | MISSING_TERM_UNIT | 422 |
taxCode + rateCode duplicado en ítem | DUPLICATE_TAX_ENTRY | 422 |
| Descuento > quantity × unitPrice | DISCOUNT_EXCEEDS_SUBTOTAL | 422 |
| Totales no coinciden con calculados (±0.01) | INCONSISTENT_TOTALS | 422 |
Respuestas
201 — Modo síncrono (éxito, SRI autoriza)
El status será AUTHORIZED, REJECTED o ERROR. Ejemplo de respuesta cuando el SRI autoriza:
{
"success": true,
"data": {
"id": "uuid",
"documentType": "INVOICE",
"documentNumber": "001-001-000000001",
"accessKey": "0703202601099123456000110010010000000014000000011",
"status": "AUTHORIZED",
"ambiente": "PRUEBAS",
"establishmentCode": "001",
"emissionPointCode": "001",
"sriResponse": {
"authorizationNumber": "07032...",
"authorizedAt": "2026-03-08T20:00:05.000Z",
"environment": "1"
},
"errorDetail": null,
"unsignedXml": "<?xml ...>",
"signedXml": "<?xml ...>",
"authorizedXml": "<?xml ...><autorizacion>...",
"createdAt": "2026-03-08T20:00:00.000Z",
"updatedAt": "2026-03-08T20:00:05.000Z"
}
} 201 — Modo síncrono (SRI rechaza)
status: "REJECTED". La respuesta incluye sriResponse.errors con códigos del SRI y errorDetail con el mensaje resumido.
201 — Modo asíncrono
status: "PENDING". Los XML y sriResponse vienen en null. Hacer polling a GET /v1/documents/:id o GET /v1/documents/detail?accessKey=... hasta estado final.
{
"success": true,
"data": {
"id": "uuid",
"status": "PENDING",
"documentNumber": "001-001-000000001",
"accessKey": "07032...",
"signedXml": null,
"authorizedXml": null,
"sriResponse": null,
"createdAt": "2026-03-08T20:00:00.000Z"
}
} Errores HTTP 4xx
Todas las respuestas de error siguen el formato success: false con objeto error (ver Referencia de errores). Tabla de códigos para este endpoint:
| HTTP | error.code | Cuándo |
|---|---|---|
| 401 | INVALID_API_KEY / API_KEY_REQUIRED | API Key o Secret faltantes o inválidos. |
| 402 | TX_LIMIT_REACHED | Límite de transacciones del plan alcanzado. |
| 422 | FUTURE_ISSUE_DATE | issueDate es una fecha futura. |
| 422 | INVALID_BUYER_ID | RUC, cédula o consumidor final con formato incorrecto. |
| 422 | IVA_ZERO_CONSUMIDOR_FINAL | IVA 0% con buyerIdType = "07". |
| 422 | MISSING_TERM_UNIT | termDays > 0 sin termUnit en un pago. |
| 422 | DUPLICATE_TAX_ENTRY | taxCode + rateCode duplicado en un ítem. |
| 422 | DISCOUNT_EXCEEDS_SUBTOTAL | Descuento mayor que quantity × unitPrice. |
| 422 | MISSING_RATE | rate no enviado en ICE/IRBPNR. |
| 422 | MISSING_TAX_BASE | taxableBase no enviado en ICE/IRBPNR. |
| 422 | INVALID_IVA_RATE_CODE | rateCode de IVA no válido. |
| 422 | INCONSISTENT_TOTALS | Algún total enviado no coincide con el calculado (±0.01). |
Ejemplo de respuesta 422 — INCONSISTENT_TOTALS
{
"success": false,
"error": {
"code": "INCONSISTENT_TOTALS",
"message": "One or more provided values do not match the computed values.",
"details": [
{ "field": "payments[].amount (sum)", "provided": 11, "expected": 11.5 }
]
}
} Ejemplo de respuesta 422 — INVALID_BUYER_ID
{
"success": false,
"error": {
"code": "INVALID_BUYER_ID",
"message": "When buyerIdType is \"07\" (Consumidor Final), buyerId must be \"9999999999999\".",
"details": [{ "field": "buyerId" }]
}
} Ejemplo de respuesta 402 — TX_LIMIT_REACHED
{
"success": false,
"error": {
"code": "TX_LIMIT_REACHED",
"message": "Transaction limit reached"
}
} Ejemplo mínimo (consumidor final, IVA 15%)
{
"buyerIdType": "07",
"buyerId": "9999999999999",
"buyerName": "CONSUMIDOR FINAL",
"issueDate": "2026-03-08",
"items": [
{
"description": "Producto genérico",
"quantity": 1,
"unitPrice": 10.00,
"taxes": [{ "taxCode": "2", "rateCode": "2" }]
}
]
} Total calculado: 10 + 15% = 11.50. Si se omite payments, el servidor genera [{ "method": "20", "amount": 11.50 }].
Ejemplo con pago explícito
{
"buyerIdType": "07",
"buyerId": "9999999999999",
"buyerName": "CONSUMIDOR FINAL",
"issueDate": "2026-03-08",
"items": [
{
"description": "Producto genérico",
"quantity": 1,
"unitPrice": 10.00,
"taxes": [{ "taxCode": "2", "rateCode": "2" }]
}
],
"payments": [{ "method": "01", "amount": 11.50 }]
} Ejemplo en JavaScript
Consumo del endpoint con fetch. Sustituye API_BASE_URL, API_KEY y API_SECRET por tus credenciales.
const API_BASE_URL = 'https://live.faktur.com.ec';
const API_KEY = 'sk_...';
const API_SECRET = 'tu-secret';
const body = {
buyerIdType: '07',
buyerId: '9999999999999',
buyerName: 'CONSUMIDOR FINAL',
issueDate: new Date().toISOString().slice(0, 10),
items: [
{
description: 'Producto genérico',
quantity: 1,
unitPrice: 10.00,
taxes: [{ taxCode: '2', rateCode: '2' }]
}
]
};
const response = await fetch(`${API_BASE_URL}/v1/documents/invoices`, {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'X-API-Secret': API_SECRET,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
const json = await response.json();
if (json.success) {
console.log('Documento:', json.data.accessKey, 'Estado:', json.data.status);
} else {
console.error('Error:', json.error?.code, json.error?.message);
}Para modo asíncrono añade ?async=true a la URL o el header X-Emit-Async: true.