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

# Cash register

> Open, record movements, close with cash count, and query cash register closings

| Permission | Endpoints |
| - | - |
| `cash_register` | `GET /cash-registers`, `GET /cash-registers/current`, `GET /cash-registers/{id}` |
| `cash_register.operate` | `POST /cash-registers/open`, `POST /cash-registers/{id}/movements`, `POST /cash-registers/{id}/close` |

> [!NOTE]
>
> These are **explicit** permissions: you must check them one by one in **External integrations**; existing integrations do not receive them automatically.

For third-party points of sale: open a branch's cash register, record income and expenses, and close it with a cash count. It uses the same logic as **Cash Register** in FileXpress, so closings look the same on the web.

* The cash register is opened per [point of sale](https://docs.filexpress.app/en/docs/api/puntos-de-venta.md): each point of sale has **only one open shift** at a time (per business environment). A branch with several points of sale can have several shifts open at the same time, one in each.
* Every cash register belongs to a **cashier**: a FileXpress user with access to the branch. This way closings are still reported by cashier.
* Each shift records **who opened it** (`opened_by_user`) and **who closed it** (`closed_by_user`), who can be different people (for example, a supervisor who performs the closing).
* Actions performed through the API are also recorded as `API: {integration name}` (in `opened_by` and `closed_by`).
* A shift's sales are issued with the numbering of its point of sale.

## Cash register closings

```http theme={null}
GET /cash-registers?branch_id={branch_id}&status=closed&start_date=2026-10-01&end_date=2026-10-06
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `branch_id` | string | Yes | Branch. |
| `status` | string | No | `open` or `closed`. |
| `start_date` | date | No | Opening date from (`YYYY-MM-DD`). |
| `end_date` | date | No | Opening date to; not earlier than `start_date`. |
| `per_page` | integer | No | 1 to 200. Default 50. |

```bash theme={null}
curl "https://api.filexpress.app/api/external/v1/cash-registers?branch_id=9d1c5a3e-...&status=closed" \
  -H "X-API-Key: fx_xxxxxxxx"
```

```json theme={null}
{
  "status": true,
  "data": {
    "data": [
      {
        "id": "4e81...",
        "branch_id": "9d1c5a3e-...",
        "point_of_sale": { "id": "7c20...", "code": "P001", "name": "Caja principal" },
        "opened_by_user": { "id": "1b7f...", "name": "Ana López" },
        "closed_by_user": { "id": "2c8a...", "name": "Luis Martínez" },
        "status": "closed",
        "cashier": { "id": "1b7f...", "name": "Ana López" },
        "opening_date": "2026-10-05T08:00:12-06:00",
        "closing_date": "2026-10-05T18:02:40-06:00",
        "opening_amount": 100.0,
        "declared_amount": 845.5,
        "difference_amount": -0.5,
        "total_sales": 1520.75,
        "total_cash_sales": 766.0,
        "total_card_sales": 690.75,
        "total_other_sales": 64.0,
        "total_expenses": 25.0,
        "total_other_incomes": 5.0,
        "notes": "Cierre sin novedad",
        "opened_by": "API: POS Sucursal Centro",
        "closed_by": "API: POS Sucursal Centro"
      }
    ],
    "pagination": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
  }
}
```

| Field | Description |
| - | - |
| `point_of_sale` | The shift's point of sale (`id`, `code`, `name`). `null` for shifts of branches without points of sale. |
| `opened_by_user` / `closed_by_user` | User who opened and user who closed the shift (`id`, `name`). `closed_by_user` is `null` while the shift is open. |
| `cashier` | FileXpress user who operates the cash register. |
| `difference_amount` | Declared amount − expected cash (negative = shortage). |
| `total_cash_sales` / `total_card_sales` / `total_other_sales` | Sales by payment method: code `01` (cash), `02`/`03` (debit/credit card), and the rest. |
| `total_other_incomes` / `total_expenses` | Income and expense movements of the cash register. |
| `opened_by` / `closed_by` | Text identifying the actor who recorded the action (for example, `API: POS Sucursal Centro`). `closed_by` is `null` while the cash register is open. |

Closing totals are stored at closing time; for an open cash register, use the live `summary`. Sorted from most recent to oldest opening. **Errors**: `404` `Branch not found`; `422` validation.

## Open cash register

```http theme={null}
GET /cash-registers/current?branch_id={branch_id}&point_of_sale_id={point_of_sale_id}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `branch_id` | string | Yes | Branch. |
| `point_of_sale_id` | string | No | Point of sale. If omitted, returns the branch's most recent open shift. |

Returns the open cash register with its live summary, or `data: null` if there is none.

```json theme={null}
{
  "status": true,
  "data": {
    "id": "4e93...",
    "branch_id": "9d1c5a3e-...",
    "point_of_sale": { "id": "7c20...", "code": "P001", "name": "Caja principal" },
    "opened_by_user": { "id": "1b7f...", "name": "Ana López" },
    "closed_by_user": null,
    "status": "open",
    "cashier": { "id": "1b7f...", "name": "Ana López" },
    "opening_date": "2026-10-06T08:01:05-06:00",
    "closing_date": null,
    "opening_amount": 100.0,
    "declared_amount": null,
    "difference_amount": null,
    "total_sales": null,
    "total_cash_sales": null,
    "total_card_sales": null,
    "total_other_sales": null,
    "total_expenses": null,
    "total_other_incomes": null,
    "notes": null,
    "opened_by": "API: POS Sucursal Centro",
    "closed_by": null,
    "summary": {
      "total_sales": 412.3,
      "total_cash": 210.0,
      "total_card": 180.3,
      "total_other": 22.0,
      "total_expenses": 15.0,
      "more_income": 0.0,
      "sales_count": 18,
      "cash_movements": 1,
      "expected_cash": 295.0
    }
  }
}
```

| Field | Description |
| - | - |
| `summary.expected_cash` | Expected cash = opening amount + cash sales + other income − expenses. |
| `summary.sales_count` | Processed sales of the cash register: those linked to it or, if no cash register is assigned, those issued at the branch between opening and closing (or now). |
| `summary.cash_movements` | Number of income/expense movements. |

**Errors**: `404` `Branch not found`.

## Closing detail

```http theme={null}
GET /cash-registers/{id}
```

Returns the cash register (same fields as the list), its `summary`, and its `movements` in the order they were recorded:

```json theme={null}
{
  "status": true,
  "data": {
    "id": "4e81...",
    "status": "closed",
    "...": "...",
    "summary": { "total_sales": 1520.75, "expected_cash": 846.0, "...": "..." },
    "movements": [
      {
        "id": "a310...",
        "type": "expense",
        "amount": 25.0,
        "description": "Compra de bolsas",
        "reference": "REC-221",
        "sale_id": null,
        "created_by": "API: POS Sucursal Centro",
        "created_at": "2026-10-05T12:30:00-06:00"
      }
    ]
  }
}
```

**Errors**: `404` `Cash register not found` (nonexistent or belonging to another business).

## Open a cash register

```http theme={null}
POST /cash-registers/open
```

| Field | Type | Required | Description |
| - | - | - | - |
| `branch_id` | string | Yes | Branch. |
| `user_id` | string | Yes | Cashier: FileXpress user assigned to the branch or to the company. Recorded as the person who opened the shift. |
| `point_of_sale_id` | string | Depends | [Point of sale](https://docs.filexpress.app/en/docs/api/puntos-de-venta.md) of the branch. Optional if the branch has only one active point of sale (that one is used); required if it has several. |
| `opening_amount` | number | Yes | Opening cash amount (greater than or equal to 0). |
| `notes` | string | No | Max. 500. |

```bash theme={null}
curl -X POST https://api.filexpress.app/api/external/v1/cash-registers/open \
  -H "X-API-Key: fx_xxxxxxxx" \
  -H "Idempotency-Key: apertura-centro-20261006" \
  -H "Content-Type: application/json" \
  -d '{"branch_id":"9d1c5a3e-...","user_id":"1b7f...","point_of_sale_id":"7c20...","opening_amount":100.00}'
```

**`201` response**: the cash register with `status: "open"` (same fields as the list, without `summary`).

**Errors**

| Code | Cause |
| - | - |
| `404` | `Branch not found`. |
| `409` | `Ya hay un turno de caja abierto en este punto de venta.` (for branches without points of sale: `There is already an open cash register for this branch.`) |
| `422` | `Seleccione el punto de venta.` (the branch has several active ones and `point_of_sale_id` was not sent) / `El punto de venta no pertenece a la sucursal o está inactivo.` / `The user does not exist or has no access to this branch.` / validation. |

## Record income or expense

```http theme={null}
POST /cash-registers/{id}/movements
```

| Field | Type | Required | Description |
| - | - | - | - |
| `type` | string | Yes | `income` (income) or `expense` (expense). |
| `amount` | number | Yes | Minimum 0.01. |
| `description` | string | Yes | Max. 255. |
| `reference` | string | No | Max. 100. |

```bash theme={null}
curl -X POST https://api.filexpress.app/api/external/v1/cash-registers/4e93.../movements \
  -H "X-API-Key: fx_xxxxxxxx" \
  -H "Idempotency-Key: gasto-centro-20261006-001" \
  -H "Content-Type: application/json" \
  -d '{"type":"expense","amount":15.00,"description":"Compra de bolsas","reference":"REC-230"}'
```

**`201` response**

```json theme={null}
{
  "status": true,
  "data": {
    "id": "a322...",
    "type": "expense",
    "amount": 15.0,
    "description": "Compra de bolsas",
    "reference": "REC-230",
    "summary": { "total_sales": 412.3, "expected_cash": 295.0, "...": "..." }
  }
}
```

**Errors**: `404` `Cash register not found`; `409` `Cash register is not open.`; `422` validation.

## Close a cash register

```http theme={null}
POST /cash-registers/{id}/close
```

| Field | Type | Required | Description |
| - | - | - | - |
| `declared_amount` | number | Yes | Cash counted at closing (greater than or equal to 0). |
| `user_id` | string | No | User who closes the shift (with access to the branch). If omitted, the shift's cashier is recorded. |
| `notes` | string | No | Max. 500. |
| `cash_count_breakdown` | object | No | Cash count by denomination. |
| `cash_count_breakdown.bills` | object | No | Number of bills per denomination: `hundred`, `fifty`, `twenty`, `ten`, `five`, `one` (same format as the web cash count). |
| `cash_count_breakdown.coins` | object | No | Number of coins: `quarter`, `dime`, `nickel`, `penny`. |
| `cash_count_breakdown.total` | number | Yes, with `cash_count_breakdown` | Must match `declared_amount` (tolerance 0.01). |

```bash theme={null}
curl -X POST https://api.filexpress.app/api/external/v1/cash-registers/4e93.../close \
  -H "X-API-Key: fx_xxxxxxxx" \
  -H "Idempotency-Key: cierre-centro-20261006" \
  -H "Content-Type: application/json" \
  -d '{"declared_amount":295.00,"cash_count_breakdown":{"bills":{"hundred":0,"fifty":0,"twenty":12,"ten":5,"five":0,"one":0},"coins":{"quarter":20,"dime":0,"nickel":0,"penny":0},"total":295.00}}'
```

**`200` response**: the cash register with `status: "closed"`, `closing_date`, `declared_amount`, `difference_amount`, and the closing totals.

**Rules**

* On closing, sales by payment method are calculated along with the **expected cash** = opening amount + cash sales + other income − expenses; `difference_amount` = declared − expected.
* Processed sales linked to the cash register are counted or, if they have no cash register, those issued at the branch between opening and closing.
* The cash count is stored as sent; FileXpress only validates its `total`.

**Errors**

| Code | Cause |
| - | - |
| `404` | `Cash register not found`. |
| `409` | `Cash register is not open.` (already closed). |
| `422` | `The cash count breakdown total does not match the declared amount.` / `The user does not exist or has no access to this branch.` / validation. |

> [!TIP]
>
> Opening, recording movements, and closing accept [`Idempotency-Key`](https://docs.filexpress.app/en/docs/api/introduccion#idempotency): if you retry after a network failure, the operation is not applied twice.

## Errors

| Code | When |
| - | - |
| `401` | Missing or invalid API key. |
| `403` | The integration does not have the `cash_register` permission (or `cash_register.operate` to operate). |
| `404` | Branch or cash register nonexistent or belonging to another business. |
| `409` | A shift is already open at the point of sale, the cash register is not open, or `Idempotency-Key` conflict. |
| `422` | Validation, point of sale not chosen, invalid or inactive, cashier without access to the branch, or a cash count that does not match the declared amount. |


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