Each host has one route under its own path:
- Bank Movement:
/api/entities/:entityId/bank-accounts/:bankAccountId/movements/:movementId/recommendation - Invoice:
/api/entities/:entityId/invoices/:invoiceId/recommendation
…/recommendation for either one.Which records can hold a Recommendation
A record holds at most one Recommendation. Writing a new one replaces the current one.
Every Bank Movement and Invoice row carries a
recommendation field. It holds the Recommendation as written, or null when the record has none. To find records by Recommendation, filter GET /movements or GET /invoices/pages with recommendationStatus. It takes confident, needs_clarification, waiting, or none (records with no Recommendation).
Statuses
A
needs_clarification Recommendation with an empty options array is an open question. A person answers it in the record’s Comments, and the agent writes a new Recommendation from the answer.
A record that waits stays unbooked. For example, when only one leg of an internal transfer has arrived, leave that leg unbooked and mark it waiting. Do not book it to the bridge account. In the app, a person marks a record waiting from its side panel. The mark replaces any Recommendation on the record.
Plans
A Plan is an ordered list of acts that an approval runs. Each act matches one existing API route. It carries the path ids of that route and the same body that route takes.
A Plan needs at least one act. Keep these rules in mind when you build one:
- You choose every id. Each entry
idand each lineidin the Plan is a UUID you generate. See Ledger Entry Line ids. - A later act can name a line an earlier act creates. Use the line
idyou gave it. For example, one Plan can book an Invoice and then match a Movement to the Invoice’s Control Line. - An act can target a record other than the host. A Plan on one leg of an internal transfer can book both legs and match the bridge lines.
- An
assignclears the rest parked on the Suspense Account. Use it on a Bank Movement booked with a Suspense line. With alineId, it settles that open line. WithlineId: null, it books the amount to the accounts of its lines. It adds lines to the Ledger Entry that booked the Movement, so it names no entry of its own. - Sintropix validates the acts only on approval. A
PUTchecks the shape of the Plan, not whether each booking balances or each line is still open. Check the bodies before you write them.
PUT …/recommendation
Set or replace the Recommendation of a record. Setting one changes no books.
Body: one of three shapes, chosen by status.
A
needs_clarification Recommendation also posts question as a Comment on the record, authored by the caller. The Comment stays in the record’s audit trail after the Recommendation is gone.
Retrying a PUT with the same id and the same body changes nothing and posts no second Comment. Sending the current id with a different body answers 409 RECOMMENDATION_ALREADY_EXISTS_ERROR. An approval names a Recommendation by its id, so a new body always needs a new id.
Response (200): the host’s own update answer plus comment.
- Bank Movement:
{ movement, bankAccount, comment } - Invoice: the same answer as
PATCH /invoices/:invoiceId, pluscomment
comment is the Comment that asks the question, or null for other statuses and for a retry.
This example proposes matching a Movement to the Control Line of an open Invoice:
plan matches the Movement to a different Invoice:
GET …/recommendation
Read the Recommendation of a record with every record in its Plan named, so you can show it to a person without extra reads.
Response (200): { recommendation }, where recommendation is null when the record has none. Otherwise it has the fields written with PUT, except that each act in plan (and in each option’s plan) is replaced by its named detail:
movementnames the Movement with its date, amount, side, description, and Bank Account.invoicenames the Invoice with its document kind, number, direction, issue date, total, currency, and Partner.- Each item in
newLinesis a line the act creates, with itsaccountandpartnernamed. - Each
targetis a line to settle. Itskindisexisting(a line on the books, with its currentopenAmountand any Invoice it controls),new(a line an earlier act creates, with itsactIndex), ornotFound. Thetargetof anassignisnullwhen itslineIdisnull.
null when the Plan names a record the Entity does not hold. Approving that Plan fails.
POST …/recommendation/approve
Run the Plan of the current Recommendation, then clear it. Sintropix runs every act in order in one transaction, through the same checks as the act’s own route. If any act fails, nothing is written and the Recommendation stays.
Body
Response (
200): the host’s own update answer plus acts. acts holds one result per act, in Plan order. Each result carries its act and the same answer as the act’s own route.
DELETE …/recommendation
Discard the Recommendation of a record. Discarding changes no books, and any Comment a question posted stays.
Response (200): the host’s own update answer. For a Bank Movement, { movement, bankAccount }. For an Invoice, the same answer as PATCH /invoices/:invoiceId.
When Sintropix clears a Recommendation
A Recommendation advises on the record as it stood when it was written. Sintropix clears it when the record’s reconciliation changes:- Booking or matching the Bank Movement.
- Unbooking the Ledger Entry that booked the Bank Movement.
- Assigning or unassigning the Bank Movement’s Suspense line.
- Booking the Invoice.
- Creating or deleting an Allocation on a line of the Bank Movement or the Invoice.
409 RECOMMENDATION_STALE_ERROR. Read the record again and review its current Recommendation.
Domain errors
An act that fails on approval answers with the error of its own route, for example
LEDGER_ENTRY_LINE_ALREADY_EXISTS_ERROR or INVOICE_BOOKING_CHANGED_ERROR.

