Skip to main content
Invoices are Sintropix’s model for both AR (issued to customers) and AP (received from suppliers). Each Invoice can be booked to a Ledger Entry and, in Chile, linked to a Documento Tributario Electrónico (DTE) issued through the SII. This page covers listing, creating, editing, booking, and DTE-linking Invoices, individually or in bulk.

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.
Open in the app. This resource has a page in the Sintropix web app at https://app.sintropix.com/{entityId}/invoices?open={invoiceId}, where each id in braces is the same UUID as in the API route or response. That page is for direction receivable. For direction payable use /{entityId}/bills?open={invoiceId}, and for an Expense Rendition use /{entityId}/expense-renditions?open={invoiceId}. When you mention a record to a user, include that link. See Link to the App for the full URL grammar.

Document kinds

Every Invoice has a documentKind. It says what the booking does with the Invoice’s total:

Tax-only documents

Use tax_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: null when you book it. Its controlLineId and controlAccountId stay null, so read bookingEntryId to tell whether it’s booked.
  • Can’t have a line on an account whose sub-type is payable, expense_rendition_payable, or receivable. A claim there would be an open item that no Invoice owns. The booking is refused with 409 and TAX_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, and openAmount are null. 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.
Both directions accept tax_only. A vendor’s Booking Template can also book its new 46s as tax_only automatically.

Switch the kind

To make an unbooked Invoice tax_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 (ONTF and OMF).
The Entity also needs a login Credential for Ontop.

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.
  1. Create. Erwin creates each invoice whose number no Invoice of the Partner of Ontop has yet, as an AP Invoice in USD:
  2. Book. Erwin books each new ONTW to the account of its contractor’s category, and each ONTF and OMF to 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 a question Follow-up that offers one option per category. He never guesses a category from the contractor’s role.
  3. Match. Erwin matches each payment to its invoices once all of them are booked and open:
    • A PAYMENT of 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.
    He matches the Movement to the first invoice, assigns the others from the Suspense Account, and books what’s left to the exchange difference account on the last invoice. If an invoice of the payment isn’t booked yet, the Movement waits unbooked with a waiting Follow-up that names the invoices. If two payments fit, he pairs none and leaves a question Follow-up.
The other wallet movements get their own treatment:
  • 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 question Follow-up.
  • A cashback gets a proposal Follow-up that books it to the account the wiki gives, or a question one when the wiki gives none.
Erwin never changes or books again an Invoice that was already in Sintropix, and never deletes an Invoice or a Movement. He reports the invoices he created and booked, the payments he matched, each Follow-up he left, the contractors the wiki doesn’t have yet, and the wallet’s balance in Ontop next to its Bank Account’s balance.
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 a payable 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.
The Rindegastos company must keep its accounts in the Entity’s functional currency. If it doesn’t, Erwin stops. Every Invoice of a load is in that currency. Each load reads every report, because a report can close months after it was sent. Erwin loads each approved expense of a closed report that isn’t in Sintropix yet. He skips rejected expenses and the expenses of reports still in process. For each new expense, Erwin creates one Invoice and attaches its receipt: 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 the Rindegastos 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 question Follow-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 question Follow-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 total as it is.
  • Not reimbursable, or a fund. When the expense is marked Reembolsable: No or spends a Fondo, Erwin leaves it unbooked with a question Follow-up that asks how to book it.
The debit to a supplier’s payable is an advance until the supplier’s factura is booked. On each run, Erwin matches each open advance to the Control Line of a booked factura of the same supplier, with the same total, within 10 days of the expense. When two facturas fit, he matches neither and reports both. When the amounts differ, the smaller one settles and the rest stays open. He reports each advance still without a factura 30 days after the expense. Erwin never changes or books again an Invoice that was already booked. He reports how many renditions he booked to an expense account and how many against a factura, the advances he matched and any rest that stayed open, the advances without a factura after 30 days, and each Follow-up he left.

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 pass ids, 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 id values against the ids you sent.
  • Combining ids with direction, 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.
To list the Expense Renditions still to book, with their own totals, pass isExpenseRendition=true:
To find the payable documents that someone has written about in their ERP Thread, pass 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.
Example response:

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:
Other lines of the entry can post to the same account as the Control Line. For example, net an advance or a retention of the same Partner in the Invoice’s own entry, or split a receivable into one line per due date. Only the Control Line is the Invoice’s: the payment status derives from it alone. Any other line on that account is an ordinary open item of the Partner until an Allocation settles it. Example body for a CLP purchase invoice of 1,190,000 (1,000,000 net plus 19% VAT):
Response (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.
  1. GET /invoices/:invoiceId and remember bookingVersion.
  2. Build the entry from the values you just read.
  3. POST /invoices/:invoiceId/book (or POST /invoices/book) with expectedBookingVersion set to that token.
  4. On 409 INVOICE_BOOKING_CHANGED_ERROR, re-read the Invoice and decide whether to rebuild the entry.
The check runs after the server acquires the Invoice lock, so a booking that races a concurrent DTE edit either sees the edit and refuses, or wins the lock and books the pre-edit state without either write being lost. Omit the field when no earlier read exists and any current state is acceptable.

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 asset with sub-type prepaid_expense).
  • On a receivable Invoice, the deferred line credits a deferred revenue account (a liability with sub-type deferred_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.
Create the two accounts in the Chart of Accounts before you defer a line. In the app, turn on Diferir on a line when you classify the Invoice.

Defer a line when you book

You defer at booking time only. Add deferral 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 debit on prepaid_expense for a payable Invoice, a credit on deferred_revenue for a receivable one.
  • Credit notes and tax_only documents 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 status scheduled, 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 dimensionValueIds go on the recognition line of each Release, not on the deferral account line of the booking.
Each Release has two lines: the Recognition Account and the deferral account. For a prepaid expense the Release debits the Recognition Account and credits the prepaid account. For deferred revenue it debits the deferred revenue account and credits the Recognition Account. A Release keeps the plan it was created with. Changing its date, through 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.
The booking response doesn’t list the Releases. Read them from deferrals on GET /invoices/:invoiceId.

Reading a Deferral

Each element of deferrals 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): the active Releases are unbooked and the scheduled ones are cancelled.
  • Reverse the booking entry (POST /ledger/entries/:entryId/reverse): the active Releases are reversed on the same reversal date and the scheduled ones are cancelled.
  • If an active Release sits on or before the ledger lock date, the whole Unbook is refused with LEDGER_ENTRY_UNBOOK_FROZEN_ERROR and the whole Reverse with LEDGER_ENTRY_FROZEN_ERROR.
In the app, the Unbook and Reverse dialogs on the Invoice and on the entry page list these effects before you confirm. To stop a Deferral but keep what’s already recognized, cancel its pending Releases.

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 of statusFilter=notExcluded and statusFilter=unbooked, of unbookedCount and outstandingTotal, of the home dashboard’s salesToBook, purchasesToBook, and expenseRenditionsToBook, 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.
A booked Invoice can’t be excluded: unbook it first. Anyone who can book an Invoice can exclude or include it. A 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.

Status codes