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

# Audit logs workflow

> Read who created, changed, cancelled, or deleted each operation through the public API.

# Audit logs workflow

`GET /api/audit-logs` is the read-only integrator trail for one company. It answers who touched a record, through which channel we can prove, and what changed.

Authenticate with `x-api-key` (company key) or a personal employee key / session. Employee keys need `audit.view` (Administrador by default). Company keys follow the rest of the public API: full-company access, no RBAC list on the key itself.

## Tenant scope

Every row is filtered by `audit_logs.company_id` on that row (FS-03). The actor email is never used to pick the tenant. Rows with a null `company_id` (unresolved / pre-remediation) are hidden. `internal_impersonation` is hidden.

A `companyId` query or body parameter is ignored.

## Filters

| Query | Meaning |
| - | - |
| `entity` | Table name or alias (`operation`, `transaction`, `account`, `client`, `mesa`, `cheque`, `invoice`, …) |
| `entityId` | `audit_logs.record_id` |
| `dateFrom` / `dateTo` | Inclusive. Date-only values are that calendar day in ART (`America/Argentina/Buenos_Aires`, UTC-3) |
| `userId` | Employee id of this company, resolved to `Employee.email` and matched to `changed_by` |
| `action` | `create`, `update`, `cancel`, `revert`, `delete` |
| `page` / `pageSize` | Default 20, max 100 |

When the caller has a scoped client role, only `Operation` and `Transaction` rows that pass the same visibility rules as `GET /api/operations` are returned. Other entities are omitted.

```bash theme={null}
curl --request GET \
  --url "https://api.finsync.ar/api/audit-logs?entity=operation&entityId=88&dateFrom=2026-04-01&dateTo=2026-04-09&page=1&pageSize=20" \
  --header "x-api-key: YOUR_API_KEY"
```

## What each field is derived from

| Field | Source |
| - | - |
| `entity` / `entity_id` | `audit_logs.table_name` / `record_id` |
| `action` | Normalized from `INSERT` / `UPDATE` / `DELETE` plus status and `deleted_at` |
| `raw_action` | Stored enum |
| `actor` | `changed_by` email resolved to `Employee` `{id, name, email}` of this company |
| `channel` | `whatsapp` or `twin` when a `WhatsAppOperationDraft` exists for the parent operation |
| `channel_detail.requested_by` | Draft contact, phone, employee or client |
| `before` / `after` / `changes` | Redacted `old_data` / `new_data` (secrets, tokens, hashes, auth fields stripped) |
| `timestamp` | `changed_at` |

### Action mapping

* `create`: `INSERT`
* `delete`: `DELETE`, or `UPDATE` that sets `deleted_at`
* `cancel`: status became `adjusted` from a status other than `completed` (typical pending cancel)
* `revert`: status `completed` → `adjusted`
* `update`: any other `UPDATE`

Creating a counterpart operation (public `POST /api/operations/{id}/revert`) is not a first-class audit action. It shows up as `create` on the counterpart and, when legs are adjusted, `cancel` / `revert` on those transactions.

## Operations list and detail

`GET /api/operations` and `GET /api/operations/{id}` add:

* `created_by`: actor of the `Operation` `INSERT` audit row
* `cancelled_by`: actor of the latest transaction status change to `adjusted`
* `channel` / `channel_detail`: same draft lookup as the audit list

Those lookups are batched by the page of operation ids (not N+1). The list still reads BigQuery; the actors come from Postgres.

## Mesas actors

`opened_by`, `closed_by`, and `created_by` stay integer employee ids (null for company API-key writes). When the id is present, the response also includes `opened_by_actor` / `closed_by_actor` / `created_by_actor` as `{ id, name }`.

## Known gaps

* **Channel** is not a column on `audit_logs`. Dashboard vs personal API key vs company key vs cron cannot be distinguished on historical rows. Those values stay `null` instead of being guessed.
* **Company `x-api-key` writes** usually have `changed_by = null` (no audit email on the request).
* **Email** is the only stored actor key. It is returned so integrators can map people. It is the business employee email, not an AuthUser password, token, or session field.
* Rows written before schema 62 may have a null `company_id` until the hand-run remediation in `schema/62_audit_logs_remediate_company_id.sql` is applied.

## Permission

`audit.view` is seeded to Administrador only (`schema/67_public_audit_permission.sql`). Operators do not receive it unless an admin grants it.


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