# Pocket Clear read-only API (v1) — reference for AI assistants

You are reading this because a Pocket Clear user gave you a read-only access token (it starts
with `pcai_`). Use it to answer the user's questions about their own spending, and to build
summaries, tables, charts and custom views. **The API is read-only: every endpoint is a GET and
nothing you call can change the user's data.**

## How to call it

Base URL: `https://api.pocketclear.app/api/ai/v1`

Two equivalent ways to send the token — use whichever your tools support:

1. **Token in the path** (works with any tool that can fetch a URL):
   `GET https://api.pocketclear.app/api/ai/v1/t/<TOKEN>/summary?month=2026-10`
2. **Authorization header**:
   `GET https://api.pocketclear.app/api/ai/v1/summary?month=2026-10` with
   `Authorization: Bearer <TOKEN>`

All responses are JSON. Do not show the token back to the user, and do not put it in links you
show them.

**If your fetch tool drops query strings** (many do — everything after `?` is lost and you get the
current month again), use the path form of the range instead. It needs no `?` at all:

- `/summary/this-month`, `/summary/last-month`, `/summary/2026-09`
- `/summary/2026-09-01/2026-10-27` (from / to, inclusive)
- the same three shapes work for `/transactions/…` and `/budgets/…`

`/me` returns a `links` object with ready-made URLs (this month, last month, transactions,
budgets, categories, goals, accounts) under the same prefix you called, so you can follow them
directly.

**Start here:** call `/me` (currency, time zone, what the token can read, ready-made `links`),
then `/summary/this-month` (and `/summary/last-month` for a comparison), then answer the question.

## Conventions

- **Amounts** are plain numbers in the user's home currency (`currency` in `/me`, an ISO 4217
  code such as `USD`, `EUR`, `INR`). Always positive; `type` / `kind` says the direction.
- **Dates**: `from` and `to` are inclusive local dates `YYYY-MM-DD` in the user's time zone.
  `month=YYYY-MM` (or `this-month` / `last-month`) is a shortcut for a whole calendar month. With no dates, endpoints use the
  current calendar month. With only `from`, the range runs to today. `tz=<IANA zone>` overrides
  the time zone.
- **What counts as income and spending** (the same rules as the Pocket Clear app):
  - `moneyOut` = expenses. `moneyIn` = income + refunds. `net` = `moneyIn − moneyOut`.
  - Transfers between the user's own accounts, lend/borrow (loan) rows, and savings-goal
    set-asides are **never** income or spending. Each transaction carries `kind` and
    `countsInTotals` so you can tell.
  - Spending by category nets refunds against the category they refund.
- **Partner Mode**: only the token owner's own data is returned (including entries a partner
  logged on their behalf). A partner's own transactions are never shared through this API.
- **Notes** (free-text transaction notes) are only included when the user turned on "Include
  notes" for this token (`notesIncluded` in `/me`).
- **`truncated: true`** means the account is very large and the numbers cover only the most
  recently edited 50,000 transactions — tell the user the figures are partial.
- Category and payment-type names are the user's own labels, in their language.

## Endpoints

### GET /me

Profile and token facts. No email or name.

```json
{
  "app": "Pocket Clear",
  "apiVersion": "v1",
  "currency": "EUR",
  "secondaryCurrency": null,
  "timeZone": "Europe/Berlin",
  "periodSettings": { "viewPeriod": "monthly", "periodStartDay": 1, "note": "..." },
  "hasPartner": false,
  "dataScope": "own",
  "notesIncluded": false,
  "readOnly": true,
  "tokenExpiresAt": "2027-01-24T10:00:00.000Z",
  "docs": "https://pocketclear.app/developers/api.md"
}
```

`periodSettings` are the app's view period and budget start day at the moment the token was
created (they live on the phone). Use them to pick sensible ranges, e.g. a month starting on
the 25th.

### GET /summary?from&to (or ?month)

Totals for a range.

```json
{
  "currency": "EUR",
  "range": { "from": "2026-10-01", "to": "2026-10-31", "timeZone": "Europe/Berlin" },
  "totals": {
    "moneyIn": 3210.5, "moneyOut": 1874.2, "net": 1336.3,
    "refundsIncludedInMoneyIn": 10.5,
    "savingsSetAside": 200, "savingsWithdrawn": 0,
    "transactionCount": 64
  },
  "spendingByCategory": [ { "categoryId": "…", "category": "Groceries", "amount": 412.3, "share": 22 } ],
  "incomeByCategory":   [ { "categoryId": "…", "category": "Salary", "amount": 3200 } ],
  "byMonth": [ { "month": "2026-10", "moneyIn": 3210.5, "moneyOut": 1874.2, "net": 1336.3 } ],
  "excludedFromTotals": { "transfers": 3, "loanRows": 1 },
  "rules": "…",
  "truncated": false
}
```

`share` is the percentage of total spending. For a year view call
`/summary?from=2026-01-01&to=2026-12-31` and use `byMonth`.

### GET /transactions?from&to&category&type&q&limit&cursor

Individual entries, newest first.

| Param | Meaning |
|---|---|
| `from`, `to`, `month` | Range, as above. |
| `category` | A category **name** (case-insensitive, e.g. `Groceries`) or id. |
| `type` | `income`, `expense` or `transfer`. |
| `q` | Text search over payee, category, subcategory, payment type, tags, loan person (and notes, only when notes are included). |
| `limit` | Page size, 1–500, default 100 (larger values are capped at 500). |
| `cursor` | `nextCursor` from the previous page. |

```json
{
  "currency": "EUR",
  "range": { "from": "2026-10-01", "to": "2026-10-31", "timeZone": "Europe/Berlin" },
  "count": 2,
  "transactions": [
    {
      "id": "…", "date": "2026-10-24", "dateTime": "2026-10-24T18:12:00.000Z",
      "amount": 42.9, "type": "expense", "kind": "expense", "countsInTotals": true,
      "category": "Eating out", "categoryId": "…", "subcategory": "Restaurants",
      "paymentType": "Visa", "payee": "Pizza Place", "tags": ["friends"]
    },
    {
      "id": "…", "date": "2026-10-20", "dateTime": "…",
      "amount": 300, "type": "expense", "kind": "transfer", "countsInTotals": false,
      "category": null, "categoryId": null, "subcategory": null,
      "paymentType": "Checking", "payee": null, "transferTo": "Savings account"
    }
  ],
  "nextCursor": "eyJzIjoi…",
  "truncated": false
}
```

`kind` is one of: `expense`, `income`, `refund`, `transfer`, `loan`, `loan_repayment`,
`savings_set_aside`, `savings_withdrawal`. Only `expense`, `income` and `refund` count toward
totals. Loan rows carry `loan: { "kind": "loan" | "repayment", "person": "Sam", "dueDate"? }`.
`note` appears only when notes are included. Keep calling with `cursor` until `nextCursor` is
`null` when you need every row; prefer `/summary` for totals.
A `category` that matches nothing returns an empty page with a `notice` — call `/categories` for
the exact names.

### GET /categories

```json
{ "categories": [
  { "id": "…", "name": "Groceries", "type": "expense", "icon": "🛒", "sharedByPartner": false,
    "subcategories": [ { "id": "…", "name": "Supermarket" } ] }
] }
```

### GET /budgets?from&to (or ?month)

```json
{
  "currency": "EUR",
  "range": { "from": "2026-10-01", "to": "2026-10-31", "timeZone": "Europe/Berlin" },
  "budgets": [
    { "id": "…", "kind": "category", "category": "Groceries", "subcategory": null,
      "period": "monthly", "limit": 400, "spentInRange": 412.3, "remaining": -12.3, "percentUsed": 103.08 }
  ],
  "note": "…",
  "truncated": false
}
```

`kind` is `overall`, `category` or `subcategory`. `spentInRange` is spending over exactly the
range you pass — for a `monthly` budget pass that month; for `weekly` pass the week. Monthly
budgets with per-month amounts use the amount for the month of `from`. Rollover is not included.

### GET /goals

```json
{ "currency": "EUR", "goals": [
  { "id": "…", "name": "Japan trip", "targetAmount": 3000, "currentAmount": 1250,
    "remaining": 1750, "percentComplete": 41.67, "deadline": "2027-04-01" }
] }
```

### GET /accounts

Payment types (bank accounts, cards, cash, wallets) with all-time balances.

```json
{ "currency": "EUR", "accounts": [
  { "id": "…", "name": "Checking", "sharedByPartner": false, "openingBalance": 1000,
    "moneyInAllTime": 15400, "moneyOutAllTime": 13120.5, "balance": 3279.5 }
], "note": "…", "truncated": false }
```

`balance` = opening balance + money in − money out; transfers count on both accounts; savings
set-asides are not counted (the app's default). A row named "No payment method" collects
entries without one.

## Errors

Errors use HTTP status codes and a JSON body `{ "error": "<code>", "message": "…", "docs": "…" }`.

| Status | `error` | What to tell the user |
|---|---|---|
| 400 | `bad_request` | A parameter is wrong — fix it and retry (the message says which). |
| 401 | `invalid_token` | The token is wrong or incomplete. Ask the user to copy the prompt again. |
| 401 | `token_expired` | Ask the user to create a new token in Pocket Clear → Settings → Connect an AI assistant. |
| 401 | `token_revoked` | The user revoked this token. |
| 403 | `pro_required` | The user's Pocket Clear Pro has lapsed; the token works again once Pro is renewed. |
| 409 | `cloud_sync_off` | Cloud Sync is off, so there is no data on the server. Ask the user to turn it on. |
| 404 | `not_found` | Unknown endpoint — the message lists the available ones. |
| 405 | `method_not_allowed` | Only GET is supported. |
| 429 | `rate_limited` | Wait for `Retry-After` seconds. Limits: 60 requests a minute, 2,000 a day per token. |
| 500 | `server_error` | Retry once, then tell the user. |

## MCP (optional, for clients that support remote MCP servers)

`POST https://api.pocketclear.app/api/ai/mcp` with `Authorization: Bearer <TOKEN>` (or
`POST https://api.pocketclear.app/api/ai/mcp/t/<TOKEN>`). Stateless Streamable HTTP, JSON
responses. Read-only tools: `get_profile`, `get_summary`, `list_transactions`,
`list_categories`, `get_budgets`, `list_goals`, `list_accounts` — the same
data and parameters as the endpoints above.

Example (Claude Code):
`claude mcp add --transport http pocket-clear https://api.pocketclear.app/api/ai/mcp --header "Authorization: Bearer <TOKEN>"`

## Good things to build

- "Build a dashboard of my spending by category this year" → `/summary?from=<Jan 1>&to=<today>`,
  chart `spendingByCategory` and `byMonth`.
- "Which subscriptions went up?" → `/transactions?category=Subscriptions` over several months, compared per payee.
- "Am I on track with my budgets?" → `/budgets` for the current month.
- "Where did my money go last month?" → `/summary?month=<last month>`, then
  `/transactions?month=<last month>&category=<top category>`.
- "How much do I have across my accounts?" → `/accounts`.

## Privacy

The user chose to share this data with you. It is read-only, the token expires, and the user
can revoke it any time in the app. Notes are private unless the user included them. Use the
data only to answer the user's questions.
