Faktur

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

CampoTipoObligatorioDescripción
buyerIdTypestring04 RUC · 05 Cédula · 06 Pasaporte · 07 Consumidor final · 08 ID exterior.
buyerIdstringMáx. 20 chars. RUC 13 dígitos terminados en 001. Consumidor final: 9999999999999.
buyerNamestringRazón social o nombre. Máx. 300 chars.
buyerAddressstringNoDirección. Máx. 300 chars.
buyerEmailstringNoSi se envía, se entrega XML/RIDE al comprador por correo.
issueDatestringFormato YYYY-MM-DD. No puede ser futura.

Campos del body — Ítems (items[])

Al menos 1 ítem requerido.

CampoTipoObligatorioDescripción
descriptionstringDescripción. Máx. 300 chars.
quantitynumber> 0. Hasta 6 decimales.
unitPricenumber≥ 0. Hasta 6 decimales.
taxesarrayAl menos 1 impuesto. taxCode, rateCode; para IVA rate/taxableBase opcionales.
mainCodestringNoCódigo interno. Recomendado para partial-refund.
discountnumberNoDescuento en valor. Default 0.

Pagos (payments[])

Si se omite, el servidor genera un pago único method: "20" por el total.

CampoObligatorioDescripción
method01 efectivo, 19 tarjeta crédito, 20 otros sistema financiero, etc.
amountLa suma debe ser igual al totalAmount.
termDays, termUnitCondicionalSi termDays > 0, termUnit es obligatorio.

Reglas de negocio SRI

ReglaCódigoHTTP
issueDate no puede ser futuraFUTURE_ISSUE_DATE422
RUC / cédula / consumidor final formato incorrectoINVALID_BUYER_ID422
IVA 0% con Consumidor FinalIVA_ZERO_CONSUMIDOR_FINAL422
termDays > 0 sin termUnitMISSING_TERM_UNIT422
taxCode + rateCode duplicado en ítemDUPLICATE_TAX_ENTRY422
Descuento > quantity × unitPriceDISCOUNT_EXCEEDS_SUBTOTAL422
Totales no coinciden con calculados (±0.01)INCONSISTENT_TOTALS422

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:

HTTPerror.codeCuándo
401INVALID_API_KEY / API_KEY_REQUIREDAPI Key o Secret faltantes o inválidos.
402TX_LIMIT_REACHEDLímite de transacciones del plan alcanzado.
422FUTURE_ISSUE_DATEissueDate es una fecha futura.
422INVALID_BUYER_IDRUC, cédula o consumidor final con formato incorrecto.
422IVA_ZERO_CONSUMIDOR_FINALIVA 0% con buyerIdType = "07".
422MISSING_TERM_UNITtermDays > 0 sin termUnit en un pago.
422DUPLICATE_TAX_ENTRYtaxCode + rateCode duplicado en un ítem.
422DISCOUNT_EXCEEDS_SUBTOTALDescuento mayor que quantity × unitPrice.
422MISSING_RATErate no enviado en ICE/IRBPNR.
422MISSING_TAX_BASEtaxableBase no enviado en ICE/IRBPNR.
422INVALID_IVA_RATE_CODErateCode de IVA no válido.
422INCONSISTENT_TOTALSAlgú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.