Status
Invoices carry two derived, read-only status values on the listing rows:
The
GET /invoices/pages listing accepts a combined filter statusFilter with values all, notPaid, paid, unbooked, booked, excluded, and notExcluded, and supports groupBy=bookingStatus and groupBy=paymentStatus. all lists every document, excluded ones included. notExcluded lists every document still headed for the books, and excluded lists only the excluded ones. notPaid is the filter a payment run works from.
Amounts are integers in the minor units of their currency (1190000 is CLP 1,190,000; 125050 is USD 1,250.50). Dates are YYYY-MM-DD strings, tax periods are YYYY-MM, and timestamps are ISO 8601.
Document kinds
Every Invoice has adocumentKind. It says what the booking does with the Invoice’s total:
Tax-only documents
Usetax_only for a document that only declares tax. The usual case is a factura de compra (DTE 46) for a purchase abroad. The foreign vendor’s own Invoice already holds the payable, so the 46 books only the IVA: a debit to IVA Crédito Fiscal and a credit to IVA Retenido.
A tax_only document:
- Books a balanced Ledger Entry with no Control Line. Send
controlLineId: nullwhen you book it. ItscontrolLineIdandcontrolAccountIdstaynull, so readbookingEntryIdto tell whether it’s booked. - Can’t have a line on an account whose sub-type is
payable,expense_rendition_payable, orreceivable. A claim there would be an open item that no Invoice owns. The booking is refused with409andTAX_ONLY_LINE_ON_CONTROL_ACCOUNT_ERROR, and Reclassify refuses to move a line of its booking onto such an account the same way. - Owes nothing, booked or not. Its
paymentStatus,paymentDate, andopenAmountarenull. It never appears in Payables to Plan, a Payment Order can’t name it, and it adds nothing to the amounts of customer status. - Can’t be deferred.
- Is never a match candidate for a Bank Movement.
- Is pending work while it’s unbooked, like any other document, and you can exclude it.
tax_only. A vendor’s Booking Template can also book its new 46s as tax_only automatically.
Switch the kind
To make an unbooked Invoicetax_only, send { "documentKind": "tax_only" } to PATCH /invoices/:invoiceId. Send "invoice" to switch it back. In the app, open the document and pick Solo impuesto in Clase de documento. The listing then shows the document as, for example, 46 · solo impuesto, with no payment status.
The switch is refused when:
A Source Feed sync never changes the kind of an Invoice that already landed. Documents booked as
invoice before tax_only existed keep their kind. To correct one, unbook it, switch it to tax_only, and book it again.
Ontop invoices
Ontop pays a company’s foreign contractors from a prepaid USD wallet, and bills the company for each payment. Erwin can load those invoices, book them, and match each payment to its invoices. Ask him in a Slack channel bound to the Entity’s Organization through an Agent Channel, for example “carga las facturas de Ontop”. To load the wallet’s movements first, ask “carga Ontop”. See Ontop wallet. Ontop issues three kinds of invoice:Before the first load
Erwin reads the facts below from the Ontop page of his wiki. Ask him to note them there before the first load. If the page is missing or lacks a fact, he stops and asks. He never creates a Partner, a Source, a Bank Account, or an account.- The Partner of Ontop, the Source of its invoices, and the Bank Account of the wallet.
- The control account of the invoices, and the account for the exchange difference.
- The expense account of each category of contractor, and the category of each contractor.
- The expense account for Ontop’s fees (
ONTFandOMF).
What a load does
Each load reads from 3 days before the newest issue date among the Partner of Ontop’s Invoices to today. On the first load, Erwin asks from which date to start.-
Create. Erwin creates each invoice whose number no Invoice of the Partner of Ontop has yet, as an AP Invoice in USD:
-
Book. Erwin books each new
ONTWto the account of its contractor’s category, and eachONTFandOMFto the fee account. The entry credits the control account and debits the expense account, on the issue date, at the FX rate of USD for that date, or of the last earlier date with a rate. When the wiki has no category for a contractor, Erwin leaves the invoice unbooked with aquestionFollow-up that offers one option per category. He never guesses a category from the contractor’s role. -
Match. Erwin matches each payment to its invoices once all of them are booked and open:
- A
PAYMENTof the wallet pairs with the Ontop payment made from the wallet, with the same amount and a date at most 3 days away. - A card charge whose description names Ontop pairs with the Ontop payment made by card, or with an
OMF, with the same amount and a date at most 3 days away.
waitingFollow-up that names the invoices. If two payments fit, he pairs none and leaves aquestionFollow-up. - A
- A top-up by wire is recorded as an internal transfer from the company’s bank account.
- A top-up by card isn’t booked. Erwin leaves a
questionFollow-up. - A cashback gets a
proposalFollow-up that books it to the account the wiki gives, or aquestionone when the wiki gives none.
Erwin loads the invoices as Ontop issues them. The Chilean tax treatment of these payments, such as Impuesto Adicional or a factura de compra, is out of scope.
Expense renditions
A rendition (rendición) is an expense that a person paid with their own money for the company, which the company pays back. Sintropix records each rendition as apayable Invoice whose Partner is the person who paid, never the merchant. Erwin loads renditions from Rindegastos and books them.
People can also submit a rendition in Sintropix itself, as an Expense Claim, or report it to Erwin, who submits the claim for them. When the Approver approves it, Sintropix creates its Invoice already booked.
Mark the Source of the renditions, such as Rindegastos, as one that holds Expense Renditions. Its Invoices are then listed in Rendiciones instead of Compras, and GET /invoices/pages tells them apart with isExpenseRendition.
Load Rindegastos
Ask Erwin in a Slack channel bound to the Entity’s Organization through an Agent Channel, for example “carga Rindegastos”. Before the first load:- Add a login Credential for Rindegastos to the Entity in Integraciones. Erwin only reads Rindegastos. He never approves, rejects, closes, or edits anything there.
- If a person’s name in Rindegastos differs from the name of their Partner, note the pair on the Rindegastos page of Erwin’s wiki.
For an expense paid in another currency, such as a USD hotel, the Invoice holds the amount the person is paid back. The currency they paid in, the amount in that currency, and the rate Rindegastos applied are only in
Moneda original, Total original, and Tasa de cambio, as Rindegastos shows them.
A load books nothing. An expense without a receipt, or whose receipt fails to download, is still loaded, and Erwin reports it. Cuenta contable Rindegastos is information only: it doesn’t follow your chart of accounts, so Erwin never books to it. Erwin also ignores the Pagado status of Rindegastos, because Sintropix learns what’s paid from the bank.
Erwin never changes or deletes an Invoice that’s already in Sintropix. If Rindegastos changes an expense after it was loaded, so that it’s no longer approved, its report reopens, or its currency or amount changes, Erwin leaves a question Follow-up on the Invoice that states what changed.
He reports the Invoices he created by report with their totals, the receipts that are missing or failed, the Partners he created, and each Follow-up he left.
Book renditions
Ask Erwin, for example “contabiliza las rendiciones”. He books each unbooked Invoice of theRindegastos Source. He skips an Invoice that has a question Follow-up until a person answers it.
The Entity needs a control account for renditions, such as Rendiciones por pagar. If it has none, Erwin stops and asks. Each entry is dated on the Invoice’s issue date, and its Control Line credits that account with the person as Partner. The debit depends on the expense’s document:
- Expense account. Erwin uses the account the wiki gives for the expense’s category. Otherwise, he uses the one expense account that clearly fits. When none clearly fits, he leaves the Invoice unbooked with a
questionFollow-up that offers one option per account that fits. - Factura. Erwin reads the receipt to tell a factura from a boleta. A factura of a supplier with the same RUT and total, within 10 days of the expense, also confirms it. He finds the supplier by the RUT on the receipt, and creates its Partner when there is none. When the receipt shows no RUT, he leaves a
questionFollow-up that asks for the supplier. He never creates the supplier’s factura from a receipt. - Other currency. An Invoice of a Rindegastos load is already in the functional currency, so Erwin books its
totalas it is. - Not reimbursable, or a fund. When the expense is marked
Reembolsable: Noor spends aFondo, Erwin leaves it unbooked with aquestionFollow-up that asks how to book it.
GET /invoices
List every Invoice for the Entity, filtered by direction and document kind. Pass ids instead to read the full detail of up to 100 specific Invoices in one request.
Query parameters
Response: an array of Invoice objects.
Reading several Invoices by id
When you passids, the response is an array of Invoice detail objects instead of plain Invoice objects. Each element has the same shape as the response of GET /invoices/:invoiceId, including dte, attachmentCount, and bookingEntry. Use this when you already hold a set of Invoice ids and need their full detail without one request per Invoice.
- Results follow the order of
ids. A repeated id is returned once, at its first position. - Ids the Entity does not hold are left out of the array rather than refused. Compare the returned
idvalues against the ids you sent. - Combining
idswithdirection,documentKind, or any other query parameter returns 400. - Sintropix reads every element from one consistent snapshot.
GET /invoices/pages
Paginated Invoice listing with richer filters. Use this instead of GET /invoices for UI-style pagination.
Query parameters
Response
Each element of
invoices is an Invoice row: every field of the Invoice object above, plus:
The type of
group.value depends on group.by:
pages is a fixed path segment, not an Invoice id: GET /invoices/pages always returns a page, never a single Invoice.isExpenseRendition=true:
erpThread=with:
POST /invoices
Create an Invoice. The same request can also book it (bookingEntryWithLines) and attach the DTE behind it (dte); all three are written together or not at all.
Body
Response (
201)
Automatic booking. When you omit
bookingEntryWithLines, Sintropix books a payable invoice (not a credit note) from its vendor’s Booking Template if the vendor has booksAutomatically on and the Invoice fits the template exactly. A tax_only template also makes the Invoice tax_only as it books it. The same applies to each element of POST /invoices/bulk and to Invoices the source-feed sync creates. An Invoice that does not fit is still created, unbooked, and the request does not fail.POST /invoices/bulk
Create up to 100 Invoices in a single request and transaction. Each element carries its own client-supplied id (required) so a retry collides on the primary key rather than double-inserting.
- Body:
BulkCreateInvoicesDto—{ invoices: CreateInvoice[] }(1 to 100 elements). - Response: array of
createInvoiceResultSchema, in input order.
The bulk routes are declared before
:invoiceId in the controller so Nest does not read bulk or book as an invoice id. Match that ordering when composing URLs.POST /invoices/book
Book up to 100 already-created Invoices in a single transaction. Each element carries an invoiceId plus the booking instructions used by the single-invoice endpoint, including the optional expectedBookingVersion.
- Body:
BulkBookInvoicesDto—{ bookings: (BookInvoice & { invoiceId })[] }(1 to 100 elements). - Response: array of
bookInvoiceResultSchema, in input order.
Bulk endpoints run as one database transaction. If any element fails validation or a domain rule, the whole batch is rejected and the response identifies the offending element by its zero-based
elementIndex (bulkElementFailureSchema). An INVOICE_BOOKING_CHANGED_ERROR on one element rolls back the whole request; retry the batch without that element (or with its refreshed expectedBookingVersion) to book its siblings.GET /invoices/:invoiceId
Return the full Invoice detail: the Invoice row (as listed by GET /invoices/pages, including dte), plus the fields below.
Ledger Entry detail (
bookingEntry)
Each element of
lines:
PATCH /invoices/:invoiceId
Partial update.
Body: any subset of the fields below. At least one is required. A dte key is ignored; use the /dte endpoints for that.
While a Payment Order names the Invoice, a change to
partnerId, currency, or documentKind is refused with 409 and INVOICE_PAYMENT_ORDERS_PIN_ERROR. Remove the orders first.
Response: { "invoice": <Invoice row>, "totalCount": integer, "unbookedCount": integer, "outstandingTotal": integer }.
DELETE /invoices/:invoiceId
Delete the Invoice. Booked Invoices are refused; unbook first. In the app, open the unbooked or excluded document and click the trash icon in its panel’s footer, then confirm. A booked document’s panel offers no delete. Deleting an Invoice also deletes the DTE behind it, unless another DTE still references that DTE (409).
Response: { "deletedInvoice": <Invoice row>, "totalCount": integer, "unbookedCount": integer, "outstandingTotal": integer }.
PUT /invoices/:invoiceId/dte
Attach or replace the DTE behind an Invoice. The body states the whole tax document: every field is sent on both a first attach and a correction, with null where the document says nothing. The DTE’s cross-references are managed on the SII page.
Body
Withheld IVA. On a factura de compra (type
46), the buyer withholds the IVA and pays it to the SII directly, so total leaves the withheld part out. State the whole IVA in taxAmount and the withheld part in withheldIvaAmount. A total withholding has withheldIvaAmount equal to taxAmount. For example, a 46 with a net of 1.000.000 CLP and a total withholding states "netAmount": 1000000, "taxAmount": 190000, "withheldIvaAmount": 190000, and "total": 1000000. DTEs that a Source Feed lands carry the amount the provider states, and a later Sync reports a changed amount as a divergence.
One issuer cannot hold two DTEs of the same type and folio within an Entity, and one Invoice holds at most one DTE; either collision is answered 409.
On a Sales Summary Invoice. An Invoice that lands a Sales Summary carries a DTE the SII owns, with no folio. Only a Sync writes it, so a write is refused with 409 and code DTE_HELD_BY_SALES_SUMMARY_ERROR.
On a booked Invoice. The write is still accepted, but the block must restate every field of the stored DTE and change only taxPeriod (the F29 / IVA month) and/or metadata (the Additional Information lines). A field the block leaves unstated keeps its stored value and counts as no change. Any other structural difference — dteTypeCode, folio, issuerRut, receiverRut, issuerName, receiverName, issueDate, currency, total, netAmount, exemptAmount, taxAmount, withheldIvaAmount, or externalRef — is answered 409 with code INVOICE_BOOKED_FIELDS_LOCKED_ERROR; unbook the Invoice first if you need to move one of them. The booking entry (its type, figures, and ledger lines) is not touched when only the period or the metadata changes.
Response: { "invoice": <Invoice row>, "totalCount": integer, "unbookedCount": integer, "outstandingTotal": integer }. The Invoice row’s dte carries the stored document:
DELETE /invoices/:invoiceId/dte
Remove the DTE from an Invoice. The Invoice stays; the tax document and the references it stated are deleted. Refused with 409 while another DTE still references it. Refused with 409 and code INVOICE_BOOKED_FIELDS_LOCKED_ERROR while the Invoice is booked; unbook first. Refused with 409 and code DTE_HELD_BY_SALES_SUMMARY_ERROR on an Invoice that lands a Sales Summary.
Response: { "invoice": <Invoice row>, "totalCount": integer, "unbookedCount": integer, "outstandingTotal": integer }.
POST /invoices/:invoiceId/book
Book an existing Invoice to a Ledger Entry using the supplied booking instructions (target counter account, dimensions, partner).
Body
Fields of
entry:
Each element of
newLedgerEntryLines:
The Control Line (the line whose
id is controlLineId) must sit on an open-item receivable account for a receivable Invoice or payable account for a payable one, name the Invoice’s Partner, and match the Invoice’s denomination. A regular invoice debits a receivable or credits a payable; a credit note posts the opposite side. Its account becomes the Invoice’s controlAccountId.
A tax_only document has no Control Line. Its entry must still balance, and none of its lines may sit on an account whose sub-type is payable, expense_rendition_payable, or receivable. For example, a factura de compra 46 of CLP 190,000 IVA for a purchase abroad:
200)
Domain errors
Detecting concurrent changes with expectedBookingVersion
Send expectedBookingVersion when the booking decision is derived from state you read earlier and any change to the Invoice or its DTE between the read and the write should abort the booking. Typical cases: a scheduled workflow that batches unbooked Invoices, an integration that classifies an Invoice from its stored DTE amounts, or a UI that stages a booking for review.
GET /invoices/:invoiceIdand rememberbookingVersion.- Build the entry from the values you just read.
POST /invoices/:invoiceId/book(orPOST /invoices/book) withexpectedBookingVersionset to that token.- On
409 INVOICE_BOOKING_CHANGED_ERROR, re-read the Invoice and decide whether to rebuild the entry.
Deferrals
Some invoices cover several months: an annual software subscription paid up front, or a four-month service billed on one sales invoice. A Deferral spreads one line of the booking over whole months, so the result is recognized month by month instead of all in the invoice month.- On a payable Invoice, the deferred line debits a prepaid expense account (an
assetwith sub-typeprepaid_expense). - On a receivable Invoice, the deferred line credits a deferred revenue account (a
liabilitywith sub-typedeferred_revenue). - Each month, a Release moves that month’s share from the deferral account to the Recognition Account: the expense or income account the line would have posted to had it not been deferred.
Defer a line when you book
You defer at booking time only. Adddeferral to the line in entry.newLedgerEntryLines on POST /invoices/:invoiceId/book. The same field works in POST /invoices/book and in bookingEntryWithLines on POST /invoices. There is no separate endpoint to defer an Invoice that is already booked; unbook it and book it again with the terms.
Fields of deferral:
These rules apply:
- The whole line is deferred. You can’t defer part of a line.
- The line’s account must have the sub-type of the Invoice’s direction, on the matching side: a
debitonprepaid_expensefor a payable Invoice, acreditondeferred_revenuefor a receivable one. - Credit notes and
tax_onlydocuments can’t be deferred. - Only the functional amount is deferred. A foreign-currency line is released at the amount booked on the Invoice, and the Releases carry no transaction currency.
- Defer the net line only. The IVA line and the control line follow the Invoice as usual.
- Several lines of the same booking can each carry their own
deferral.
The Release schedule
Booking a deferred line creates one Release per month:- Straight-line amounts. Each Release carries an equal share of the line’s functional amount. The last month absorbs the rounding.
- Month-end dates. Each Release is dated the last day of its month.
- Past and future months. A Release dated today or earlier is booked right away with status
active. A later one is a Scheduled Entry with statusscheduled, and Sintropix books it on its date. - Closed months. If a Release would fall on or before the ledger lock date, the whole booking is refused with
LEDGER_ENTRY_DATE_LOCKED_ERROR. - Partner and Dimensions. Both lines of each Release carry the Invoice’s Partner. The deferred line’s
dimensionValueIdsgo on the recognition line of each Release, not on the deferral account line of the booking.
PATCH /ledger/entries/:entryId or by rescheduling it, is refused with DEFERRAL_RELEASE_LOCKED_ERROR. Moving one of its lines to another account is refused with LEDGER_ENTRY_LINE_ACCOUNT_PINNED_ERROR.
Example: a bill of CLP 1,428,000 (1,200,000 net plus 19% VAT) for a 12-month software subscription starting in July 2026. The net line debits the prepaid expense account and releases 100,000 a month into the software expense account.
deferrals on GET /invoices/:invoiceId.
Reading a Deferral
Each element ofdeferrals on the Invoice detail:
Each element of
releases:
A Deferral has no status of its own. Read its state from its Releases.
Undoing a Deferral
A Deferral follows its booking:- Unbook the booking entry (
DELETE /ledger/entries/:entryId): theactiveReleases are unbooked and thescheduledones are cancelled. - Reverse the booking entry (
POST /ledger/entries/:entryId/reverse): theactiveReleases are reversed on the same reversal date and thescheduledones are cancelled. - If an
activeRelease sits on or before the ledger lock date, the whole Unbook is refused withLEDGER_ENTRY_UNBOOK_FROZEN_ERRORand the whole Reverse withLEDGER_ENTRY_FROZEN_ERROR.
POST /deferrals/:deferralId/cancel
Cancel every scheduled Release of the Deferral. Releases already booked stay as they are. In the app, this is Cancelar lo pendiente on the Invoice. The call has no body.
Response (200): the Deferral, in the shape of an element of deferrals, with the cancelled Releases now cancelled.
Domain errors
Exclude an invoice from the books
Some documents land but should never be booked: for example, an annulled boleta or a factura you claimed at the SII. Deleting one doesn’t help, because the next Source Feed sync lands it again. Exclude it instead. An Exclusion records, with a reason, that the Invoice will never be booked. It takes the Invoice out of the pending work, and you can lift it at any time. An excluded Invoice:- Is still listed by
statusFilter=all, the default, and the app crosses those rows out. It’s left out ofstatusFilter=notExcludedandstatusFilter=unbooked, ofunbookedCountandoutstandingTotal, of the home dashboard’ssalesToBook,purchasesToBook, andexpenseRenditionsToBook, and of match candidates. - Can’t be booked, single or bulk, and can’t hold a Follow-up. Excluding the Invoice clears its Follow-up.
- Owes nothing to plan in a Payment Batch.
- Stays landed. A sync skips it, but still writes the Standing and Tax Period of its DTE.
viewer is refused with 403. Each Exclusion and Inclusion appears in the Invoice’s audit trail.
PUT /invoices/:invoiceId/exclusion
Exclude an unbooked Invoice from the books.
Body
Response:
{ "invoice": <Invoice row>, "totalCount": integer, "unbookedCount": integer, "outstandingTotal": integer }, with excludedAt and exclusionReason set.
DELETE /invoices/:invoiceId/exclusion
Include an excluded Invoice in the books again. It goes back to unbooked.
Response: { "invoice": <Invoice row>, "totalCount": integer, "unbookedCount": integer, "outstandingTotal": integer }, with excludedAt and exclusionReason null.
Exclusion errors
A blank
reason, or one longer than 1,000 characters, is refused with 400.

