API reference
Every screen is already an endpoint
The app is server-rendered, so there is no second API to keep in sync. A personal token authenticates a script against the same routes your browser uses.
How it works
Authentication
The host in every example below is your own instance — the machine you self-host on, or your hosted subdomain. The public demo at demo.pathayam.app signs everyone in automatically and issues no tokens, so these examples will not run against it.
One header on every request: Authorization: Bearer bgt_…. Tokens are minted in Settings, scoped read or read-write, and shown exactly once.
Idempotency
Every write accepts Idempotency-Key. Replaying a key returns the original result instead of acting twice, so a timeout you never saw is safe to retry.
Responses
Reads return HTML — this is the same app your browser talks to. /export.json, the CSVs and /healthz return data. Writes redirect on success.
YYYY-MM-DD and months are YYYY-MM, both in IST.
Budget & money
Everything that moves money between envelopes. These are the routes that change what your budget says.
GET / The month grid
The budget screen for a month: groups, categories, assigned, activity and available, with Ready to Assign and the digest.
Scope
read or higher
Parameters
| Name | Type | Description | |
|---|---|---|---|
month | string | optional | YYYY-MM. Defaults to the current month in IST. |
Responses
- 200 — The rendered screen, as HTML.
- 401 — No token, or a token that has been revoked.
- 429 — Rate limit exceeded. Retry after the interval in the response.
Example
curl -sS https://pathayam.yourdomain.com/?month=2026-09 \
-H "Authorization: Bearer bgt_…"
POST /assign Assign to a category
Set the amount assigned to one category in one month. This is an absolute value, not a delta — sending 4500 makes the assignment 4500, whatever it was before.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
month | string | required | YYYY-MM. |
category_id | string | required | The category receiving the money. |
amount | integer | required | Rupees. Accepts an expression such as 450+120. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/assign \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d "month=2026-09&category_id=cat_7f3&amount=4500"
POST /move Move between envelopes
Move money from one category to another within the same month. Ready to Assign is unaffected — the total assigned does not change, only where it sits. This is how you cover an overspend.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
month | string | required | YYYY-MM. |
from_category_id | string | required | Where the money comes from. |
to_category_id | string | required | Where it goes. |
amount | integer | required | Rupees. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/move \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d "month=2026-09&from_category_id=cat_hol&to_category_id=cat_eat&amount=1320"
POST /hold Hold income for next month
Take an amount out of this month's Ready to Assign and place it at the top of next month's. Send 0 to release everything currently held.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
month | string | required | The month holding the money back. |
amount | integer | optional | Rupees. Empty or 0 releases the hold. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/hold \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d "month=2026-09&amount=139900"
GETPOST /auto-assign Fund every target
GET previews what funding all targets would do. POST commits it. Money is spent down the budget in your own order, so a short month funds the top of the list first.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
month | string | required | YYYY-MM. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS https://pathayam.yourdomain.com/auto-assign?month=2026-09 \
-H "Authorization: Bearer bgt_…"
GET /explain/ready-to-assign Why that number
The events that produced Ready to Assign for a month, in order — income, assignments, holds, and anything carried in.
Scope
read or higher
Parameters
| Name | Type | Description | |
|---|---|---|---|
month | string | optional | YYYY-MM. |
Responses
- 200 — The rendered screen, as HTML.
- 401 — No token, or a token that has been revoked.
- 429 — Rate limit exceeded. Retry after the interval in the response.
Example
curl -sS https://pathayam.yourdomain.com/explain/ready-to-assign?month=2026-09 \
-H "Authorization: Bearer bgt_…"
Accounts & transactions
Where money actually arrives and leaves.
GET /accounts List accounts
Every account with its cleared, uncleared and working balances, grouped into budget, credit and tracking.
Scope
read or higher
Responses
- 200 — The rendered screen, as HTML.
- 401 — No token, or a token that has been revoked.
- 429 — Rate limit exceeded. Retry after the interval in the response.
Example
curl -sS https://pathayam.yourdomain.com/accounts \
-H "Authorization: Bearer bgt_…"
POST /add Record a transaction
Create one transaction. Money out must name a category — the requirement is enforced on the server, not only in the form. Money in is exempt, because its job is to land in Ready to Assign.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
account_id | string | required | The account it happened on. |
direction | string | required | in or out. |
amount | integer | required | Rupees, always positive. Direction carries the sign. |
date | string | required | YYYY-MM-DD. |
category_id | string | optional | Required when direction is out. |
payee | string | optional | Free text. Matched against known payees. |
memo | string | optional | A note to your future self. |
tags | string | optional | Comma-separated. |
cleared | boolean | optional | Whether the bank has confirmed it. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/add \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d "account_id=acc_hdfc&direction=out&amount=1240" \
-d "date=2026-09-12&category_id=cat_gro&payee=Zepto"
POST /transfer Move between your accounts
A transfer is not spending and is never counted as such. Paying a credit card is a transfer from a budget account to that card.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
from_account_id | string | required | Source account. |
to_account_id | string | required | Destination account. |
amount | integer | required | Rupees. |
date | string | optional | YYYY-MM-DD. Defaults to today. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/transfer \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d "from_account_id=acc_hdfc&to_account_id=acc_card&amount=11600"
GET /cards Cards by due date
Every credit account ordered by what falls due next, with what is owed, what the payment envelope holds, and anything unfunded.
Scope
read or higher
Responses
- 200 — The rendered screen, as HTML.
- 401 — No token, or a token that has been revoked.
- 429 — Rate limit exceeded. Retry after the interval in the response.
Example
curl -sS https://pathayam.yourdomain.com/cards \
-H "Authorization: Bearer bgt_…"
POST /transaction/:id/categorise File one transaction
Assign a category to an existing transaction. This is also the learning signal: filing the same payee twice proposes a rule.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
id | path | required | Transaction id. |
category_id | string | required | The envelope it belongs to. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/transaction/txn_9a2/categorise \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d "category_id=cat_gro"
POST /transaction/:id/delete Delete a transaction
Soft delete, recorded in the event log and reversible for thirty days from Activity.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
id | path | required | Transaction id. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/transaction/txn_9a2/delete \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)"
Import & review
Nothing posts itself. Everything arrives here first.
GET /review The review queue
Everything awaiting a decision: uncategorised spending, suspected duplicates, overspent categories, unfunded cards, proposed rules and money you are owed.
Scope
read or higher
Responses
- 200 — The rendered screen, as HTML.
- 401 — No token, or a token that has been revoked.
- 429 — Rate limit exceeded. Retry after the interval in the response.
Example
curl -sS https://pathayam.yourdomain.com/review \
-H "Authorization: Bearer bgt_…"
POST /import Import a CSV
Paste statement rows as CSV. Headers are detected and a per-bank column mapping can be saved for next time. Rows land staged, never posted.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
account_id | string | required | Which account these rows belong to. |
csv | string | required | The raw CSV text. |
profile_id | string | optional | A saved column mapping to apply. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/import \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
--data-urlencode "csv@statement.csv" -d "account_id=acc_hdfc"
POST /import/pdf Upload a statement PDF
Multipart upload of a bank statement. The bank is recognised from its own layout, the file is decrypted in-process if it is password-protected, and the parse is checked against the statement's closing balance.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
file | file | required | The PDF, as multipart form data. |
account_id | string | required | Which account it belongs to. |
password | string | optional | Omit it to let the app derive it from your saved identity. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/import/pdf \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@statement.pdf" -F "account_id=acc_hdfc"
POST /import/undo Undo an import batch
Reverses an entire import and every row it created, in one step.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
batch_id | string | required | From the import log. |
Responses
- 303 — Success. Follow
Locationto the updated screen. - 400 — A field was missing or could not be parsed; the message says which.
- 401 — No token, or a token without write scope.
- 409 — Replayed idempotency key with a different body.
Example
curl -sS -X POST https://pathayam.yourdomain.com/import/undo \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)" -d "batch_id=imp_41c"
Query, activity & export
Reading everything back out, including all of it at once.
GET /query Filter transactions
The filterable, groupable table. Totals are summed over every row that matched, not the page rendered — the response says how many matched.
Scope
read or higher
Parameters
| Name | Type | Description | |
|---|---|---|---|
from | string | optional | YYYY-MM-DD. |
to | string | optional | YYYY-MM-DD. |
category_id | string | optional | Restrict to one envelope. |
account_id | string | optional | Restrict to one account. |
payee | string | optional | Substring match on cleaned and raw text. |
group_by | string | optional | category, payee, month or account. |
Responses
- 200 — The rendered screen, as HTML.
- 401 — No token, or a token that has been revoked.
- 429 — Rate limit exceeded. Retry after the interval in the response.
Example
curl -sS "https://pathayam.yourdomain.com/query?from=2025-10-01&to=2026-09-30&group_by=category" \
-H "Authorization: Bearer bgt_…"
GET /query.csv Query results as CSV
The same filter, as CSV, uncapped. The figures match what the screen showed.
Scope
read or higher
Parameters
| Name | Type | Description | |
|---|---|---|---|
… | optional | Accepts every parameter /query does. |
Responses
- 200 —
text/csv. - 401 — No token.
Example
curl -sS "https://pathayam.yourdomain.com/query.csv?from=2026-04-01" \
-H "Authorization: Bearer bgt_…" -o spend.csv
GET /activity Every change made
The append-only event log, newest first, with the undo for each entry. An undo is itself an entry — nothing is edited away.
Scope
read or higher
Parameters
| Name | Type | Description | |
|---|---|---|---|
limit | integer | optional | Entries per page. |
Responses
- 200 — The rendered screen, as HTML.
- 401 — No token, or a token that has been revoked.
- 429 — Rate limit exceeded. Retry after the interval in the response.
Example
curl -sS https://pathayam.yourdomain.com/activity \
-H "Authorization: Bearer bgt_…"
POST /activity/:id/undo Undo one event
Reverses a single recorded change within thirty days. Refuses when the record is load-bearing for a loan instalment, a portfolio lot or a reconciliation, and says which.
Scope
read-write
Parameters
| Name | Type | Description | |
|---|---|---|---|
id | path | required | Event id from Activity. |
Responses
- 303 — Undone.
- 409 — Refused — the response explains what depends on it.
- 410 — Older than the thirty-day window.
Example
curl -sS -X POST https://pathayam.yourdomain.com/activity/evt_88c/undo \
-H "Authorization: Bearer bgt_…" \
-H "Idempotency-Key: $(uuidgen)"
GET /export.json Export everything
The complete dataset in an open format: accounts, transactions, assignments, lots, loans and events. Not a summary and not a subset. The statement identity and any Gmail refresh token are excluded, and a test asserts their absence.
Scope
read or higher
Responses
- 200 —
application/json, sent as a download.
Example
curl -sS https://pathayam.yourdomain.com/export.json \
-H "Authorization: Bearer bgt_…" -o pathayam-backup.json
GET /healthz Health probe
For your uptime monitor. Returns the application state without revealing anything about the data.
Scope
public — no token needed
Responses
- 200 —
application/json—{"ok":true}. - 503 — Started but not healthy.
Example
curl -sS https://pathayam.yourdomain.com/healthz
What a token cannot reach
A deny-list of path prefixes rather than a note in a review checklist. A token that could mint tokens turns one leak into permanent access, and a token that could change the allow-list turns it into somebody else's household.
| Prefix | Why it is blocked |
|---|---|
/members | The household allow-list. A token must not change who is allowed in. |
/tokens | A token must not mint or revoke tokens, including itself. |
/impersonate | Impersonation is a session-only, logged, human action. |
/auth | Sign-in, SSO, and the development bypass state. |
/signout | There is nothing for a token to sign out of. |
Leaving should be as easy as arriving
One endpoint returns everything in an open format, and the database is a SQLite file you can open with anything. Nothing about the format needs this app to read it.
Read the guides