Invoices
POST /v1/invoices issues an invoice (factura, XML 2.1.0): the document that supports a sale of goods or services. The buyer can be identified or a final consumer.
Request
Section titled “Request”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{ "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 }}| Field | Required | Notes |
|---|---|---|
customer |
Yes | The buyer: idType, id, name, and optionally address and email. The email is where Flashpi sends the RIDE and the XML. |
items |
Yes | 1 to 1000 lines. |
items[].code |
Yes | Your product code, up to 25 characters. auxiliaryCode is an optional second code, such as the barcode. |
items[].description |
Yes | Up to 300 characters. |
items[].unit |
No | Unit of measure, such as UNIT, LITER or KILOGRAM. |
items[].quantity, items[].unitPrice |
Yes | Decimal strings with up to 6 decimals. The unit price excludes taxes. |
items[].discount |
No | The discount amount of the line (not a percentage), up to 2 decimals. It cannot exceed quantity × unit price. |
items[].taxes |
Yes | Exactly one VAT, and optionally one ad valorem ICE. See Taxes. |
items[].additionalDetails |
No | Up to 3 extra details printed with the line, name to value. |
payments |
Yes | 1 to 50 payments that add up to the total, each with method, amount and an optional term and termUnit. |
tip |
No | The tip (propina), up to 2 decimals. Defaults to 0. |
waybillNumber |
No | The number of the waybill that carried the goods (001-001-000000045). |
expectedTotal |
No | Your own total; a different computed total is refused with 422 total_mismatch. |
The fields every request shares (issuerId, establishment, emissionPoint, issueDate, sequential, additionalInfo, options) work as everywhere.
What Flashpi computes
Section titled “What Flashpi computes”For the request above:
| Line | Quantity × price − discount | Subtotal | VAT |
|---|---|---|---|
| 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 % |
The VAT is computed per rate on the summed base: 15 % of 57.50 is 8.625, rounded half up to 8.63. The total is the subtotal (60.31) plus the taxes (8.63) plus the tip (0.00): 68.94, which matches the payment and the expectedTotal.
Response
Section titled “Response”202 Accepted, with 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"}With options.waitForAuthorization, the answer is 200 OK with the document authorized, rejected or failed when the SRI answers within the wait (see Sync and async modes). The invoice.authorized and invoice.rejected webhooks report the SRI’s decision.
- VAT (
vat): every line has exactly one. The rates are0,5,8(the differentiated tourism rate, on the days a decree sets),12,13,14,15,exempt(exento) andnot_subject(no objeto de impuesto). Use the rate in force on the issue date: the SRI rejects a document with a wrong one. Flashpi does not know yet when every rate started or ended, so it only refuses withnot_in_forcethe rates it knows were not in force. - ICE (
ice): one optional ad valorem ICE per line, with its 4-digit code of the SRI’s TABLA 18 and its percentage. The ICE is part of the VAT base. Specific ICE (an amount per unit) is not supported yet.
A line of 10 × 1.00 with "taxes": [{ "type": "ice", "code": "3031", "rate": "75" }, { "type": "vat", "rate": "15" }] gets an ICE of 7.50 and a VAT of 15 % on 17.50 (2.63), for a total of 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" }]Final consumer
Section titled “Final consumer”For a buyer who does not identify themselves, send only the identification type. Flashpi writes the final consumer identification (9999999999999) and prints 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" }]}The document’s counterparty is then { "id": "9999999999999", "name": "CONSUMIDOR FINAL" }, and without an email email.status is skipped.
Errors
Section titled “Errors”| Answer | When |
|---|---|
422 validation_failed |
A field is missing or invalid; errors[] lists every one (for example items[0].code required, customer.id invalid_check_digit, payments amount_mismatch). |
422 total_mismatch |
expectedTotal differs from the computed total; the body has both. |
409 sequential_already_used |
The sequential you chose is taken in that series. |
409 sequentials_exhausted |
The series reached sequential 999,999,999. |
403 key_scope_mismatch, key_read_only |
The key cannot issue for this issuer. |
See Errors for the format and every code.