Ir al contenido

Facturas

POST /v1/invoices emite una factura (XML 2.1.0): el comprobante que respalda una venta de bienes o servicios. El comprador puede estar identificado o ser consumidor final.

Ventana de terminal
curl https://api.flashpi.com.ec/v1/invoices \
-H "Authorization: Bearer $FLASHPI_API_KEY" \
-H "Idempotency-Key: order-PO-4812" \
-H "Content-Type: application/json" \
-d @invoice.json
invoice.json
{
"issuerId": "01a0fd6a-d098-76ca-b492-1bcb6505a7bd",
"establishment": "001",
"emissionPoint": "001",
"issueDate": "2026-10-15",
"customer": {
"idType": "ruc",
"id": "1790000000001",
"name": "Supermercados La Pradera S.A.",
"email": "compras@example.com",
"address": "Av. Amazonas N34-12, Quito"
},
"items": [
{
"code": "CAF-500",
"auxiliaryCode": "2001234500012",
"description": "Ground coffee 500 g",
"quantity": "3",
"unitPrice": "4.25",
"discount": "0.75",
"taxes": [{ "type": "vat", "rate": "15" }],
"additionalDetails": { "Batch": "L-2026-10" }
},
{
"code": "SRV-INST",
"description": "Espresso machine installation",
"quantity": "1",
"unitPrice": "45.50",
"taxes": [{ "type": "vat", "rate": "15" }]
},
{
"code": "LEC-1L",
"description": "Whole milk 1 L",
"unit": "LITER",
"quantity": "2.5",
"unitPrice": "1.123456",
"taxes": [{ "type": "vat", "rate": "0" }]
}
],
"payments": [{ "method": "credit_card", "amount": "68.94", "term": "3", "termUnit": "months" }],
"additionalInfo": { "Order": "PO-4812" },
"expectedTotal": "68.94",
"options": { "sendEmail": true }
}
Campo Obligatorio Notas
customer Sí El comprador: idType, id, name y, opcionalmente, address y email. El correo es donde Flashpi envía el RIDE y el XML.
items Sí De 1 a 1000 líneas.
items[].code Sí Tu código de producto, de hasta 25 caracteres. auxiliaryCode es un segundo código opcional, como el código de barras.
items[].description Sí Hasta 300 caracteres.
items[].unit No La unidad de medida, como UNIT, LITER o KILOGRAM.
items[].quantity, items[].unitPrice Sí Cadenas decimales de hasta 6 decimales. El precio unitario no incluye impuestos.
items[].discount No El valor del descuento de la línea (no un porcentaje), con hasta 2 decimales. No puede superar cantidad × precio unitario.
items[].taxes Sí Exactamente un IVA y, opcionalmente, un ICE ad valorem. Consulta Impuestos.
items[].additionalDetails No Hasta 3 detalles adicionales impresos con la línea, nombre y valor.
payments Sí De 1 a 50 pagos que suman el total, cada uno con method, amount y, opcionalmente, term y termUnit.
tip No La propina, con hasta 2 decimales. Por defecto 0.
waybillNumber No El número de la guía de remisión que transportó la mercadería (001-001-000000045).
expectedTotal No Tu propio total; si el total calculado es distinto, se rechaza con 422 total_mismatch.

Los campos que comparten todas las solicitudes (issuerId, establishment, emissionPoint, issueDate, sequential, additionalInfo, options) funcionan igual que en todos los comprobantes.

Para la solicitud anterior:

Línea Cantidad × precio − descuento Subtotal IVA
Ground coffee 500 g 3 × 4.25 − 0.75 12.00 15 %
Espresso machine installation 1 × 45.50 45.50 15 %
Whole milk 1 L 2.5 × 1.123456 = 2.80864 2.81 0 %

El IVA se calcula por tarifa sobre la base sumada: el 15 % de 57.50 es 8.625, que se redondea mitad hacia arriba a 8.63. El total es el subtotal (60.31) más los impuestos (8.63) más la propina (0.00): 68.94, que coincide con el pago y con expectedTotal.

202 Accepted, con Location: /v1/documents/01a1412b-ae38-7c39-8c71-16d2b5dfc472:

{
"id": "01a1412b-ae38-7c39-8c71-16d2b5dfc472",
"type": "invoice",
"environment": "test",
"status": "processing",
"issuerId": "01a0fd6a-d098-76ca-b492-1bcb6505a7bd",
"establishment": "001",
"emissionPoint": "001",
"sequential": 123,
"number": "001-001-000000123",
"accessKey": "1510202601179000000000110010010000001239052713811",
"issueDate": "2026-10-15",
"xmlVersion": "2.1.0",
"counterparty": { "id": "1790000000001", "name": "Supermercados La Pradera S.A." },
"totals": {
"subtotal": "60.31",
"discount": "0.75",
"taxes": [
{ "type": "vat", "rate": "15", "taxableBase": "57.50", "amount": "8.63" },
{ "type": "vat", "rate": "0", "taxableBase": "2.81", "amount": "0.00" }
],
"tip": "0.00",
"total": "68.94"
},
"authorization": { "number": null, "authorizedAt": null },
"ride": { "status": "pending", "template": null, "generatedAt": null },
"email": { "requested": true, "status": "queued", "deliveries": [], "warnings": [] },
"sriMessages": [],
"failure": null,
"voidedAt": null,
"createdAt": "2026-10-15T20:05:44.120Z",
"updatedAt": "2026-10-15T20:05:44.120Z"
}

Con options.waitForAuthorization, la respuesta es 200 OK con el comprobante authorized, rejected o failed cuando el SRI responde dentro de la espera (consulta Modo síncrono y asíncrono). Los webhooks invoice.authorized e invoice.rejected informan la decisión del SRI.

  • IVA (vat): cada línea tiene exactamente uno. Las tarifas son 0, 5, 8 (la tarifa diferenciada de turismo, en los días que fija un decreto), 12, 13, 14, 15, exempt (exento) y not_subject (no objeto de impuesto). Usa la tarifa vigente en la fecha de emisión: el SRI rechaza un comprobante con una tarifa equivocada. Flashpi todavía no sabe cuándo empezó o terminó cada tarifa, así que solo rechaza con not_in_force las tarifas que sabe que no estaban vigentes.
  • ICE (ice): un ICE ad valorem opcional por línea, con su código de 4 dígitos de la TABLA 18 del SRI y su porcentaje. El ICE forma parte de la base del IVA. El ICE específico (un valor por unidad) todavía no está disponible.

Una línea de 10 × 1.00 con "taxes": [{ "type": "ice", "code": "3031", "rate": "75" }, { "type": "vat", "rate": "15" }] tiene un ICE de 7.50 y un IVA del 15 % sobre 17.50 (2.63), con un total de 20.13:

"taxes": [
{ "type": "ice", "code": "3031", "rate": "75", "taxableBase": "10.00", "amount": "7.50" },
{ "type": "vat", "rate": "15", "taxableBase": "17.50", "amount": "2.63" }
]

Para un comprador que no se identifica, envía solo el tipo de identificación. Flashpi escribe la identificación de consumidor final (9999999999999) e imprime CONSUMIDOR FINAL:

{
"issuerId": "01a0fd6a-d098-76ca-b492-1bcb6505a7bd",
"establishment": "001",
"emissionPoint": "001",
"customer": { "idType": "final_consumer" },
"items": [
{
"code": "CAF-500",
"description": "Ground coffee 500 g",
"quantity": "1",
"unitPrice": "4.25",
"taxes": [{ "type": "vat", "rate": "15" }]
}
],
"payments": [{ "method": "cash", "amount": "4.89" }]
}

El counterparty del comprobante es entonces { "id": "9999999999999", "name": "CONSUMIDOR FINAL" } y, sin correo, email.status es skipped.

Respuesta Cuándo
422 validation_failed Falta un campo o no es válido; errors[] los lista todos (por ejemplo items[0].code required, customer.id invalid_check_digit, payments amount_mismatch).
422 total_mismatch expectedTotal es distinto del total calculado; el cuerpo trae ambos.
409 sequential_already_used El sequential que elegiste ya está usado en esa serie.
409 sequentials_exhausted La serie llegó al secuencial 999.999.999.
403 key_scope_mismatch, key_read_only La llave no puede emitir para este emisor.

Consulta Errores para ver el formato y todos los códigos.