Mesas workflow
A mesa is a per-currency trading desk. It tracks inventory of alocal_currency (for example ARS) priced in a quote_currency (typically USD).
Quote perspective (default for new desks): the operator thinks in the quoted currency. Compra = quote in = engine sell (today’s sell local). Venta = quote out = engine buy. The engine stays a local-currency book; only labels and the blotter remap.
Manual feed (default for new desks): the blotter starts empty. Add rows with POST /api/mesas/{id}/lines or confirm “Sugerir desde operaciones”.
Auto feed: same matching engine as Sugerir / line linking (exact → fx_close → net_quote → usd_leg). On each open-period read (report, blotter, candidates, suggestions) — and after operation approve/cancel/revert — the desk applies high-confidence matches (exact, fx_close) as fabricated blotter lines plus display-only MesaLineOperationLink rows. Low-confidence leftovers stay in Sugerir. Links never write Operation/Transaction. If a linked op is later adjusted, the next reconcile drops that blotter line only.
Pending operations (include_pending, default off): existing desks stay completed-only. When on, Sugerir / auto-feed / infer / netting also count pending legs. Cancel (pending → adjusted) heals that blotter line off so position, blotter, and day P&L lose the exposure. Approve (pending → completed) keeps the same MesaLineOperationLink (no second line). A pending amount edit does not create a duplicate; the existing line stays.
Work happens inside periods (one day): open with opening stock in quote + FX (for example 47500 USD @ 1540), load compras/ventas, then close. Close only cuts the day (WAC / spray / remanente freeze).
pnlQuote default: pnlLocal / sellFx (WAC of the covered short). Not configurable yet.
Authenticate with a company x-api-key. The company is taken from the key. Company-key calls skip role checks, and writes store created_by / opened_by / closed_by as null.
A personal employee key (fsync_emp_…) uses the same paths via x-api-key or Authorization: Bearer. It is the employee: writes record that employee, reads require mesas.view (or mesas.manage), and creates, edits, and closes require mesas.manage.
The dashboard uses the same /api/mesas paths with an employee session and those same permissions. This page is the integrator sequence.
Prerequisite: a superadmin must set Company.mesas_enabled first. There is no public PATCH for that flag. If the module is off, every /api/mesas call returns 403 with code: MESAS_DISABLED.
1. Resolve internal accounts
account_ids must be internal accounts (Client.type = 0) that belong to the API-key company and are not already linked to another mesa. Currencies on the mesa must differ.
2. Create a mesa
costing_method is fifo or wac. A 201 body is the mesa (id, currencies, account_ids, created_by: null, …).
GET /api/mesas lists company mesas. GET /api/mesas/previews?date=YYYY-MM-DD returns blotter preview numbers (status, opening, volume, WAC, spray) for each mesa on that ART day. GET /api/mesas/{id} returns one. PATCH /api/mesas/{id} updates name, currencies, costing method, or account_ids. DELETE /api/mesas/{id} is a soft-delete (same as the dashboard) and frees the linked accounts.
3. Open a period
Trades only derive against an open period. At most one period per mesa can be open.opening_quote is signed blotter inventory (USD leftover), not a physical cash count. Positive is typical ventanilla remanente; negative (for example -7016) is a net long quote carried into the next day. opening_local still works for local-perspective desks. When either is not zero, opening_fx is required (local units per 1 quote). Valor fijo / Toscas-style physical stock can stay >= 0 in the UI. opened_at defaults to 05:00 ART (America/Argentina/Buenos_Aires) of today — 08:00Z, not midnight. 00:00Z is 21:00 ART the previous calendar day and 00:00 ART (03:00Z) sits on the UTC/ART boundary.
4. Add blotter lines (manual desk)
quote_side is compra or venta. Supply quote_amount plus fx or local_amount. PATCH / DELETE /api/mesas/{id}/lines/{lineId} edit or remove a row. Lines do not write treasury or operations.
GET /api/mesas/{id}/report includes blotter (compras | ventas, pile WAC, remaining, day P&L, spray). blotter.spray is { quote, local, buyWac, sellWac, buyQuote }: day-only averages of compras/ventas lines. local = (sellWac − buyWac) × buyQuote; quote = local / sellWac. If compras volume or ventas volume is 0, quote and local are 0. The dashboard blotter shows WAC + Spray + Remanente only.
GET /api/mesas/{id}/periods returns { mesa, periods, activePeriod }.
While the period is open, PATCH /api/mesas/{id}/periods/{periodId} changes opening stock used by the blotter (opening_quote + opening_fx on quote desks, opening_local on local desks). Live WAC / remanente / spray replay from the new opening plus current lines. Does not rewrite other periods or touch treasury. 409 PERIOD_CLOSED if you send opening fields on a closed day. On an already-closed period the same PATCH accepts closed_at only (late-close correction; does not rewrite the frozen blotter).
5. Let operations feed the desk (feed_mode=auto)
Create or approve operations as usual (POST /api/operations, POST /api/operations/{id}/approve). Auto desks use the same company-wide currency scorer as GET /api/mesas/{id}/day-entry-suggestions (not the MesaAccount allowlist). High-confidence ART-day matches become blotter lines progressively. Approving, cancelling, or reverting an operation triggers the same reconcile for open auto (and include_pending) desks of that company. Revert/adjust never mutates counterpart ops; the mesa only unlinks its own blotter contribution.
6. Review candidates and apply overrides
side, local_amount, fx, and trade_date together):
action is include, exclude, or null (clear the override; requires operation_id or override_id). DELETE /api/mesas/{id}/overrides/{overrideId} removes a row. Auto desks prefer POST /day-entry-suggestions so P&L stays on fabricated blotter lines.
Optional helpers:
GET/POST /api/mesas/{id}/infer?operation_id=suggests a trade from an operation’s legs.GET /api/mesas/{id}/day-entry-suggestionslists unmatched ART-day operations as proposed Compra/Venta rows (same exact / fx_close / net_quote / usd_leg ranks as line linking). Onfeed_mode=autothe read first reconciles high-confidence matches (exact,fx_close) so this list is leftovers. Closed day:409.POST /api/mesas/{id}/day-entry-suggestionswith{ "operation_ids": [88] }infers each op, creates a blotter line (this does change P&L), then attaches display-only links. Does not post treasury movements.POST /api/mesas/{id}/simulatewith{ "side": "sell", "localAmount": 500, "fx": 1250 }previews P&L against the open period.
7. Read the report (open day or closed snapshot)
Same query as the dashboard:page, pageSize, search, side, status, inclusion, sortKey, sortDir. Add period_id to open a closed day from its frozen snapshot (two-column blotter, WAC, leftover, P&L, lines). Omit period_id for the live open day (409 NO_OPEN_PERIOD when none is open). A closed period_id is not replayed; 409 SNAPSHOT_MISSING if closing_report is unusable. readOnly is true for closed days. Do not PATCH a closed snapshot — POST /api/mesas/{id}/periods/{periodId}/reopen first.
{ mesa, period, report, blotter, trades, pagination, readOnly }. Aggregates and blotter are for the full period; report.perTrade and trades are the current page. blotter is the two-column compras | ventas view plus spray. Each blotter row may include links[] (display-only operation references; they do not change P&L). Closing freezes spray on the blotter snapshot.
8. Close the period
report + trades + blotter) on the period. View that frozen day with GET /api/mesas/{id}/report?period_id= — the desk UI uses the same two-column blotter, read-only, until you reopen. The dashboard close form does not capture counts or arqueo.
Optional legacy count_quote / count_local / close_fx are still accepted on the API for older clients; they are not shown on the desk close dialog. closed_by is null for API-key closes.
Optional closed_at (ISO) defaults to 23:00 ART of the period’s opened ART day (02:00Z the next UTC calendar day) if omitted — not now. One open and one close per ART calendar day. It must be >= that period’s opened_at and must not overlap another period on the same mesa. Windows are [opened_at, closed_at): the next period may open at exactly this closed_at. The desk close dialog prefills 23:00 ART; override the time only for the rare late or early close (400 if opened_at would overlap).
To move closed_at after a late close without rewriting the frozen blotter:
POST /api/mesas/{id}/periods with opened_at equal to that closed_at succeeds.
To correct opening stock or lines on a closed day, reopen it (do not PATCH the snapshot):
closing_report is cleared and is no longer the source of truth. Lines and opening are editable. Close again to write a new snapshot. 409 if this period is already open, or if another period is already open (one open day per mesa).
GET /api/mesas/{id}/analysis aggregates closed periods. Pass period_ids=4,5 or from/to (YYYY-MM-DD, filter by opened_at). A range with no closed days returns an empty analysis (not 400). Optional include_open=true appends today’s live day (not cached). Each row includes blotter/spread/remaining/arqueo: closing_report.blotter when present, otherwise assembled from frozen trades + position. Range rollups: avgDailyPnlQuote, bestDay, worstDay, winDays, cumulativePnl. Use report?period_id= for the frozen two-column blotter of one day.
Related endpoints
GET/POST /api/mesasGET/PATCH/DELETE /api/mesas/{id}GET/POST /api/mesas/{id}/periodsPATCH /api/mesas/{id}/periods/{periodId}— opening fields on an open day (409if closed);closed_atlate-close correction on a closed dayPOST /api/mesas/{id}/periods/{periodId}/reopen— closed day becomes open; snapshot is no longer source of truthPOST /api/mesas/{id}/periods/{periodId}/close— optionalclosed_at(defaults to 23:00 ART of the opened desk day) andclose_fx(defaultopening_fx)GET /api/mesas/{id}/reportGET /api/mesas/{id}/candidatesPOST /api/mesas/{id}/overridesDELETE /api/mesas/{id}/overrides/{overrideId}POST /api/mesas/{id}/linesPATCH/DELETE /api/mesas/{id}/lines/{lineId}GET /api/mesas/{id}/lines/{lineId}/link-suggestionsPUT /api/mesas/{id}/lines/{lineId}/links— display-only references; does not change P&L. The same operation may sit on several lines (the UI warns before confirming).GET/POST /api/mesas/{id}/inferPOST /api/mesas/{id}/simulateGET /api/mesas/{id}/analysis