# API overview

All v1 endpoints at a glance.

The BennyBooks API v1 is RESTful, JSON-only, and org-scoped. All paths are relative to /api/v1.

## Read endpoints

### `GET /api/v1/transactions`

List ledger transactions for the token's organization. Results are ordered by date descending. Agents should keep limit≤50 unless exporting.

- operationId: `list_transactions`
- scope: `read`

**Parameters**

- `status` (enum, optional) — uncategorized, matched, receipt_missing, agent_suggested, reconciled
- `source` (enum, optional) — nordea, stripe, manual_csv
- `date` (date, optional) — Exact date (YYYY-MM-DD)
- `dateFrom` (date, optional) — Inclusive start date
- `dateTo` (date, optional) — Inclusive end date
- `category` (string, optional) — Exact category name, or __uncategorized__
- `description` (string, optional) — Case-insensitive substring on description
- `limit` (integer, optional) — Default 50 (agent-lean), max 500
- `offset` (integer, optional) — Default 0

**Response**

```json
{
  "data": [ { "id": "…", "date": "2026-08-28", "amount": -842, "currency": "EUR", … } ],
  "meta": { "totalCount": 42, "limit": 50, "offset": 0 }
}
```

### `GET /api/v1/transactions/{id}`

Full transaction with linked notes and receipts. Prefer this over list_notes for investigation.

- operationId: `get_transaction`
- scope: `read`

**Parameters**

- `id` (uuid, required) — Transaction ID

**Response**

```json
{
  "data": {
    "transaction": { … },
    "notes": [ { "id": "…", "content": "…", "createdAt": "…" } ],
    "receipts": [ { "id": "…", "fileName": "invoice.pdf", … } ]
  }
}
```

### `GET /api/v1/categories`

Chart of accounts with stable codes, synonyms, and parent links. Call before proposing categorizations. Prefer categoryCode over display names.

- operationId: `list_categories`
- scope: `read`

**Response**

```json
{
  "data": [
    {
      "code": "5100",
      "name": "Software & tools",
      "type": "expense",
      "parentCode": "5000",
      "synonyms": ["Software", "SaaS", "Subscriptions"],
      "inUse": true
    }
  ]
}
```

### `GET /api/v1/connectors`

Bank and payment connector sync status for the organization.

- operationId: `list_connectors`
- scope: `read`

**Response**

```json
{
  "data": [
    { "id": "…", "name": "Stripe", "provider": "stripe", "syncStatus": "ok", "lastSyncAt": "…" }
  ]
}
```

### `GET /api/v1/dashboard/summary`

Revenue, expenses, cash position, workload counts, monthly trend, and notifications.

- operationId: `get_dashboard_summary`
- scope: `read`

**Response**

```json
{
  "data": {
    "currency": "EUR",
    "revenue": 26500,
    "expenses": 8420,
    "cashPosition": 18080,
    "uncategorized": 4,
    "missingReceipts": 2,
    "agentSuggested": 2,
    "pendingReview": 3,
    "recentTransactions": [ … ],
    "monthlyTrend": [ … ],
    "notifications": [ … ]
  }
}
```

### `GET /api/v1/proposals`

Pending agent proposals awaiting human approval.

- operationId: `list_pending_proposals`
- scope: `read`

**Response**

```json
{
  "data": [ { "id": "…", "type": "categorization", "status": "pending", … } ],
  "meta": { "count": 2 }
}
```

### `GET /api/v1/proposals/{id}`

Single proposal by ID.

- operationId: `get_proposal`
- scope: `read`

**Parameters**

- `id` (uuid, required) — Proposal ID

**Response**

```json
{ "data": { "id": "…", "type": "categorization", "status": "pending", … } }
```

### `GET /api/v1/notes`

Notes attached to a transaction.

- operationId: `list_notes`
- scope: `read`

**Parameters**

- `transactionId` (uuid, required) — Transaction ID

**Response**

```json
{ "data": [ { "id": "…", "content": "…", "createdAt": "…" } ] }
```

### `GET /api/v1/receipts/search`

Search receipts by file name or OCR text. Returns up to 50 results.

- operationId: `search_receipts`
- scope: `read`

**Parameters**

- `q` (string, required) — Search query

**Response**

```json
{ "data": [ … ], "meta": { "count": 3, "limit": 50 } }
```

### `GET /api/v1/receipts/{id}`

Receipt metadata (not file bytes). Use session API to download files.

- operationId: `get_receipt`
- scope: `read`

**Parameters**

- `id` (uuid, required) — Receipt ID

**Response**

```json
{ "data": { "id": "…", "fileName": "invoice.pdf", "transactionId": null, … } }
```

## Write endpoints

> All writes require propose-write scope. Most writes create proposals — nothing commits until a human approves.

### `POST /api/v1/proposals/categorization`

Suggest a category for a transaction. Prefer categoryCode. Creates a pending proposal — the ledger is not updated until a human approves in Ledger → Approvals.

- operationId: `propose_categorization`
- scope: `propose-write`

**Body**

```json
{
  "transactionId": "uuid",
  "categoryCode": "5300",
  "confidence": 0.91,
  "reason": "Description matches counterparty AWS",
  "evidence": [
    { "type": "counterparty_match", "value": "aws" }
  ]
}
```

**Response**

```json
{ "data": { "id": "…", "status": "pending", "type": "categorization", … } }
```

### `POST /api/v1/proposals/journal-entry`

Propose a new manual ledger entry. Creates a pending proposal — a transaction is created only after human approval.

- operationId: `propose_journal_entry`
- scope: `propose-write`

**Body**

```json
{
  "date": "2026-08-28",
  "description": "Office supplies",
  "amount": -42.50,
  "currency": "EUR",
  "categoryCode": "5200",
  "source": "manual_csv",
  "confidence": 0.88,
  "reason": "One-off office purchase"
}
```

**Response**

```json
{ "data": { "id": "…", "status": "pending", "type": "journal_entry", … } }
```

### `POST /api/v1/proposals/receipt-link`

Suggest linking a receipt to a transaction. Approval required.

- operationId: `propose_receipt_link`
- scope: `propose-write`

**Body**

```json
{
  "transactionId": "uuid",
  "receiptId": "uuid",
  "confidence": 0.85,
  "reason": "Amount and date match"
}
```

**Response**

```json
{ "data": { "id": "…", "status": "pending", "type": "receipt_link", … } }
```

### `POST /api/v1/notes`

Create a proposal to attach a note. Approval required.

- operationId: `attach_note`
- scope: `propose-write`

**Body**

```json
{ "transactionId": "uuid", "content": "Client project: ACME-42" }
```

**Response**

```json
{ "data": { "id": "…", "status": "pending", "type": "note", … } }
```

### `PUT /api/v1/notes`

Directly create a note without approval. For trusted automation only.

- operationId: `create_note`
- scope: `propose-write`

**Body**

```json
{ "transactionId": "uuid", "content": "Client project: ACME-42" }
```

**Response**

```json
{ "data": { "id": "…", "content": "…", "createdAt": "…" } }
```
