> ## Documentation Index
> Fetch the complete documentation index at: https://docs.finsync.ar/llms.txt
> Use this file to discover all available pages before exploring further.

# Mesas workflow

> Operate FX trading desks (mesas) with an API key: create a mesa, open a period, feed operations, review candidates, override inclusion, read the live report, and close.

# Mesas workflow

A **mesa** is a per-currency trading desk. It tracks inventory of a `local_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.

```bash theme={null}
curl --request GET \
  --url "https://finsync.ar/api/accounts?type=0" \
  --header "x-api-key: YOUR_API_KEY"
```

Pick at least one account id (for example a local caja and a quote caja).

## 2. Create a mesa

```bash theme={null}
curl --request POST \
  --url "https://finsync.ar/api/mesas" \
  --header "Content-Type: application/json" \
  --header "x-api-key: YOUR_API_KEY" \
  --data '{
    "name": "ARS/USD desk",
    "local_currency": "ARS",
    "quote_currency": "USD",
    "costing_method": "wac",
    "perspective": "quote",
    "feed_mode": "manual",
    "include_pending": false,
    "account_ids": [10, 11]
  }'
```

`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.

```bash theme={null}
curl --request POST \
  --url "https://finsync.ar/api/mesas/1/periods" \
  --header "Content-Type: application/json" \
  --header "x-api-key: YOUR_API_KEY" \
  --data '{
    "opening_quote": 47500,
    "opening_fx": 1540,
    "opened_at": "2026-04-02T08:00:00.000Z"
  }'
```

`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)

```bash theme={null}
curl --request POST \
  --url "https://finsync.ar/api/mesas/1/lines" \
  --header "Content-Type: application/json" \
  --header "x-api-key: YOUR_API_KEY" \
  --data '{
    "quote_side": "compra",
    "quote_amount": 1000,
    "fx": 1545,
    "trade_date": "2026-04-02",
    "time": "09:15",
    "note": "mostrador"
  }'
```

`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).

```bash theme={null}
curl --request PATCH \
  --url "https://finsync.ar/api/mesas/1/periods/5" \
  --header "Content-Type: application/json" \
  --header "x-api-key: YOUR_API_KEY" \
  --data '{
    "opening_quote": 47000,
    "opening_fx": 1540
  }'
```

## 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

```bash theme={null}
curl --request GET \
  --url "https://finsync.ar/api/mesas/1/candidates" \
  --header "x-api-key: YOUR_API_KEY"
```

Include or exclude an operation (or fabricate a fully manual trade with `side`, `local_amount`, `fx`, and `trade_date` together):

```bash theme={null}
curl --request POST \
  --url "https://finsync.ar/api/mesas/1/overrides" \
  --header "Content-Type: application/json" \
  --header "x-api-key: YOUR_API_KEY" \
  --data '{
    "action": "include",
    "operation_id": 88,
    "note": "keep this FX"
  }'
```

`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-suggestions` lists unmatched ART-day operations as proposed Compra/Venta rows (same exact / fx\_close / net\_quote / usd\_leg ranks as line linking). On `feed_mode=auto` the read first reconciles high-confidence matches (`exact`, `fx_close`) so this list is leftovers. Closed day: `409`.
* `POST /api/mesas/{id}/day-entry-suggestions` with `{ "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}/simulate` with `{ "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.

```bash theme={null}
curl --request GET \
  --url "https://finsync.ar/api/mesas/1/report?page=1&pageSize=25&side=all&status=all&inclusion=all&sortKey=date&sortDir=desc" \
  --header "x-api-key: YOUR_API_KEY"
```

```bash theme={null}
curl --request GET \
  --url "https://finsync.ar/api/mesas/1/report?period_id=4&page=1&pageSize=25" \
  --header "x-api-key: YOUR_API_KEY"
```

The body is `{ 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

```bash theme={null}
curl --request POST \
  --url "https://finsync.ar/api/mesas/1/periods/5/close" \
  --header "Content-Type: application/json" \
  --header "x-api-key: YOUR_API_KEY" \
  --data '{ "closed_at": "2026-04-03T02:00:00.000Z" }'
```

Closing freezes inventory, average cost, P\&L, and the full report JSON (`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:

```bash theme={null}
curl --request PATCH \
  --url "https://finsync.ar/api/mesas/1/periods/5" \
  --header "Content-Type: application/json" \
  --header "x-api-key: YOUR_API_KEY" \
  --data '{ "closed_at": "2026-04-02T03:00:00.000Z" }'
```

Same overlap checks as close. After the correction, `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):

```bash theme={null}
curl --request POST \
  --url "https://finsync.ar/api/mesas/1/periods/5/reopen" \
  --header "x-api-key: YOUR_API_KEY"
```

The day becomes open again: `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/mesas`
* `GET` / `PATCH` / `DELETE /api/mesas/{id}`
* `GET` / `POST /api/mesas/{id}/periods`
* `PATCH /api/mesas/{id}/periods/{periodId}` — opening fields on an open day (`409` if closed); `closed_at` late-close correction on a closed day
* `POST /api/mesas/{id}/periods/{periodId}/reopen` — closed day becomes open; snapshot is no longer source of truth
* `POST /api/mesas/{id}/periods/{periodId}/close` — optional `closed_at` (defaults to 23:00 ART of the opened desk day) and `close_fx` (default `opening_fx`)
* `GET /api/mesas/{id}/report`
* `GET /api/mesas/{id}/candidates`
* `POST /api/mesas/{id}/overrides`
* `DELETE /api/mesas/{id}/overrides/{overrideId}`
* `POST /api/mesas/{id}/lines`
* `PATCH` / `DELETE /api/mesas/{id}/lines/{lineId}`
* `GET /api/mesas/{id}/lines/{lineId}/link-suggestions`
* `PUT /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}/infer`
* `POST /api/mesas/{id}/simulate`
* `GET /api/mesas/{id}/analysis`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.