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

# Conventions

> Date formats, money, the organization_id query parameter, write-request semantics, and shared error shapes.

These conventions hold across every Reporting API endpoint. The endpoint pages document only their own filters and column projections.

## Dates

All date parameters are `YYYY-MM-DD` and inclusive on both ends:

```
?from=2024-01-01&to=2024-12-31
```

The column that the `from` / `to` range filters on differs per report — the endpoint page documents which one. CRR filters on `effective_at`; NSFR filters on `effective_at`.

Times in response bodies are always returned in UTC.

## Money

All monetary fields are integers in **cents** (`amount_cents`, etc.). No currency conversion happens server-side — every Monterey account is denominated in USD.

<Note>
  Some historical rows have `amount_cents: null` — the field was added more recently than the data. Treat `null` as "unknown", not zero.
</Note>

## The `organization_id` query parameter

Optional on every endpoint. Two modes:

* **Omit it**: results cover every organization the key is scoped to. Each row's `organization_id` field tells you which org it belongs to.
* **Include it**: results are restricted to that single org. If the org isn't in the key's allowlist, you get `403`.

The `organization_id` is the ULID-shaped identifier Monterey assigns to each organization; the portal shows it next to each organization on the key creation screen.

## Error shape

Every non-2xx response uses the same JSON body:

```json theme={null}
{
  "detail": {
    "error_code": "organization_out_of_scope"
  }
}
```

| Status | `error_code`                | Cause                                                                                                                                                                      |
| ------ | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `invalid_date_range`        | `to` is earlier than `from`, or either is unparseable                                                                                                                      |
| `400`  | `invalid_limit`             | `limit` out of range when called outside FastAPI validation (detail includes `min_limit`, `max_limit`, `provided`)                                                         |
| `401`  | `missing_token`             | `Authorization` header missing or not Bearer                                                                                                                               |
| `401`  | `invalid_token`             | Key is unknown, revoked, expired, or has no party scopes                                                                                                                   |
| `403`  | `organization_out_of_scope` | `?organization_id=X` where X is outside the key's allowlist                                                                                                                |
| `404`  | *per endpoint*              | A path ID (`/accounts/{id}`, `/payment-methods/{id}`, `/autopay/{id}`, …) that doesn't exist or isn't reachable by the key — the two cases are indistinguishable by design |
| `409`  | *per endpoint*              | The request conflicts with resource state: deleting a payment method an active or paused autopay schedule still uses, or updating a cancelled schedule                     |
| `422`  | *various*                   | FastAPI parameter validation errors (also include a `loc` field); includes `limit` out-of-range on list endpoints                                                          |
| `501`  | `not_implemented`           | `POST /payments` — the contract is final but submission isn't live yet                                                                                                     |

## Response shape

Every list endpoint — reports and resources alike — returns an `items` array plus pagination metadata:

```json theme={null}
{
  "items": [
    {
      "transaction_id": "01J...",
      "organization_id": "01K...",
      "account_id": "01J...",
      "type": "payment",
      "amount_cents": 12500,
      "effective_at": "2024-03-15",
      "status": null
    }
  ],
  "page": {
    "next_cursor": null,
    "has_more": false,
    "limit": 100
  }
}
```

Single-resource reads (`GET /accounts/{id}`), creates (`POST /autopay`), and updates (`PATCH`) return the bare object — no envelope. Deletes return `204 No Content` with an empty body.

## Write requests

The payment-operations endpoints (`POST`, `PATCH`, `DELETE`) follow three conventions:

* **JSON bodies.** Send `Content-Type: application/json`. `POST` bodies require the fields the endpoint page marks required; everything else is optional.
* **`PATCH` is partial.** Send only the fields you want to change — omitted fields keep their current values. Each endpoint page lists which fields are editable; anything else is immutable (for example, a payment method's instrument data, or an autopay schedule's cadence and start date).
* **Deletes are soft and idempotent.** `DELETE /payment-methods/{id}` deactivates the record and `DELETE /autopay/{id}` cancels the schedule; both stay visible afterward (`is_active: false`, `status: cancelled`) so payment history remains auditable, and repeating the call returns `204` again. Cancelled schedules cannot be reactivated.

## Pagination

List endpoints accept `cursor` and `limit` query parameters. The response body includes a `next_cursor` field when more rows are available — pass that value back as `cursor` on the next call to fetch the next page. When `next_cursor` is null, you've reached the end of the result set.

```
GET /transactions?from=2024-01-01&to=2024-12-31&limit=500
GET /transactions?from=2024-01-01&to=2024-12-31&limit=500&cursor=<next_cursor>
```

### Limit

| Field   | Value |
| ------- | ----- |
| Default | 100   |
| Minimum | 1     |
| Maximum | 1000  |

Requests with `limit > 1000` are rejected with **422** before they reach the handler. The validation envelope (FastAPI's default shape) carries the violated constraint:

```json theme={null}
{
  "detail": [
    {
      "loc": ["query", "limit"],
      "msg": "Input should be less than or equal to 1000",
      "type": "less_than_equal",
      "ctx": {"le": 1000},
      "input": "1001"
    }
  ]
}
```

Narrow your page size and walk the result set with the returned `next_cursor` — there is no offset.
