Ledgo API
With the REST API your own program, script or AI agent fully manages your Ledgo data: accounts, categories, contacts, income and expenses, debts, recurring payments, events and reports.
Getting started
- Open @ledgoapp → Settings → «API & AI agents».
- Name the token and tap «Create». Copy it right away — it is shown only once.
- Send the header with every request: Authorization: Bearer <token>.
Tokens
You can create several tokens, each with its own name. In Settings you can turn a token off and back on, or revoke it for good — a request with a revoked or disabled token returns 401. A token opens only /api/v1 and acts on your behalf; do not share it.
Transactions awaiting confirmation
Rules
- Money always has 2 decimals (x.xx); the currency always comes from the account — there is no conversion.
- Exchange between accounts in different currencies is type: transfer with amount (taken) and to_amount (received).
- Dates: ISO 8601 with a time zone offset (e.g. 2026-10-30T13:45:00+05:00). Your time zone is in GET /me.
- Income/expense says who it is for (contact_id or contact_ids) — the system contacts «Myself» and «Household» are contacts too.
- Errors: 401 — no token, or it is disabled or revoked; 403 — this path is not part of the API; 404 — not found; 422 — invalid data ({message, errors}); 429 — too many requests (120 per minute).
Example
curl https://ledgo.uz/api/v1/accounts \
-H "Authorization: Bearer $LEDGO_TOKEN"
curl -X POST https://ledgo.uz/api/v1/transactions \
-H "Authorization: Bearer $LEDGO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"expense","amount":45000,"account_id":12,"note":"Lunch"}'
# → 201 {"transaction": {"id": 345, "is_pending": true, "source": "api", ...}}
curl -X POST https://ledgo.uz/api/v1/transactions \
-H "Authorization: Bearer $LEDGO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"expense","amount":45000,"account_id":12,"contact_id":3,"auto_confirm":true}'
For AI agents
Give your agent the OpenAPI schema — it reads every path and field by itself. The agent's transactions await confirmation, so a mistake never lands in your balance unnoticed: you review and confirm.
https://ledgo.uz/api/v1/openapi.json
Profile and settings
GET /api/v1/me Who you are: profile, timezone and the number of pending transactions.
{"user": User (+ timezone), "pending_count": int}
PUT /api/v1/settings Changes the language and base currency.
| Name | Type | In | |
|---|---|---|---|
| locale | string uz | ru | en | auto |
body | Language; auto follows Telegram. |
| base_currency | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Base currency. |
{"user": User}
Accounts
GET /api/v1/accounts Accounts with their current balance.
{"accounts": Account[]}
POST /api/v1/accounts Creates an account or card.
Each account keeps its own currency; there is no conversion.
| Name | Type | In | |
|---|---|---|---|
| name * | string | body | Name (up to 60 characters). |
| type * | string cash | card | bank |
body | Type: cash, card or bank. |
| currency * | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Currency; cannot change once the account has transactions. |
| initial_balance | number | body | Opening balance, up to 2 decimals. |
| color | string | body | Color. |
| icon | string | body | Icon. |
| bank_name | string | body | Bank name. |
| last4 | string | body | Last 4 card digits (to match bank messages). |
| is_archived | boolean | body | Archived. |
| is_primary | boolean | body | Primary account (only one at a time). |
{
"name": "Humo",
"type": "card",
"currency": "UZS",
"initial_balance": 1250000.5,
"bank_name": "Kapitalbank",
"last4": "4821"
}
{"account": Account}
PUT /api/v1/accounts/{account} Updates an account.
Always send name, type and currency; on an account with transactions the currency is silently kept.
| Name | Type | In | |
|---|---|---|---|
| account * | integer | path | Account ID. |
| name * | string | body | Name (up to 60 characters). |
| type * | string cash | card | bank |
body | Type: cash, card or bank. |
| currency * | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Currency; cannot change once the account has transactions. |
| initial_balance | number | body | Opening balance, up to 2 decimals. |
| color | string | body | Color. |
| icon | string | body | Icon. |
| bank_name | string | body | Bank name. |
| last4 | string | body | Last 4 card digits (to match bank messages). |
| is_archived | boolean | body | Archived. |
| is_primary | boolean | body | Primary account (only one at a time). |
{
"name": "Humo",
"type": "card",
"currency": "UZS",
"is_primary": true
}
{"account": Account}
DELETE /api/v1/accounts/{account} Deletes an account (one with transactions returns 422; archive it instead).
| Name | Type | In | |
|---|---|---|---|
| account * | integer | path | Account ID. |
{"ok": true}
Categories
GET /api/v1/categories Lists categories.
{"categories": Category[]}
POST /api/v1/categories Creates a category.
| Name | Type | In | |
|---|---|---|---|
| type * | string income | expense |
body | Income or expense. |
| name * | string | body | Name (up to 40 characters). |
| icon | string | body | Icon. |
| color | string | body | Color. |
{"category": Category}
PUT /api/v1/categories/{category} Updates a category (its type cannot change).
| Name | Type | In | |
|---|---|---|---|
| category * | integer | path | Category ID. |
| name | string | body | Name (up to 40 characters). |
| icon | string | body | Icon. |
| color | string | body | Color. |
| is_archived | boolean | body | Archived. |
{"category": Category}
DELETE /api/v1/categories/{category} Deletes a category; its transactions become uncategorized.
| Name | Type | In | |
|---|---|---|---|
| category * | integer | path | Category ID. |
{"ok": true}
Contacts
GET /api/v1/contacts Lists contacts (system contacts "Myself" and "Household" first).
{"contacts": Contact[]}
POST /api/v1/contacts Creates a contact.
A contact tells who a transaction was for or paid to.
| Name | Type | In | |
|---|---|---|---|
| name * | string | body | Name (up to 80 characters). |
| type | string person | organization |
body | Person or organization. |
| phone | string | body | Phone number. |
| note | string | body | Note (up to 500 characters). |
| is_dependent | boolean | body | Your dependent (a family member). |
| telegram_ids | array<integer> | body | Telegram accounts to link (409 if linked to another contact). |
| main_telegram_id | integer | body | Main Telegram account. |
| no_telegram | boolean | body | Mark as having no Telegram. |
{
"name": "Sardor",
"type": "person",
"phone": "+998901234567",
"note": "Ishxonadan"
}
{"contact": Contact}
GET /api/v1/contacts/{contact} A contact with debts, the last 100 transactions and spend totals.
| Name | Type | In | |
|---|---|---|---|
| contact * | integer | path | Contact ID. |
| from | date | query | Spend period start (defaults to month start). |
| to | date | query | Spend period end (defaults to month end). |
{"contact": Contact, "net": [{currency, they_owe_me, i_owe, net}], "transactions": Transaction[], "debts": Debt[], "spend": {range, total, all_time, provided}}
PUT /api/v1/contacts/{contact} Updates a contact.
Always send name; a system contact keeps its name and type.
| Name | Type | In | |
|---|---|---|---|
| contact * | integer | path | Contact ID. |
| name * | string | body | Name (up to 80 characters). |
| type | string person | organization |
body | Person or organization. |
| phone | string | body | Phone number. |
| note | string | body | Note (up to 500 characters). |
| is_dependent | boolean | body | Your dependent (a family member). |
{
"name": "Sardor Karimov",
"phone": "+998901234567",
"is_dependent": false
}
{"contact": Contact}
DELETE /api/v1/contacts/{contact} Deletes a contact; records stay, only the link is removed.
| Name | Type | In | |
|---|---|---|---|
| contact * | integer | path | Contact ID. |
{"ok": true}
POST
/api/v1/contacts/{contact}/merge
Merges contact from into this one and deletes it.
| Name | Type | In | |
|---|---|---|---|
| contact * | integer | path | ID of the contact that stays. |
| from * | integer | body | ID of the contact merged in and removed. |
{"contact": Contact, "removed": int}
Transactions
GET /api/v1/transactions Transactions, newest first, 30 per page.
| Name | Type | In | |
|---|---|---|---|
| type | string income | expense | transfer |
query | Type. |
| account_id | integer | query | Account (incoming transfers included); also returns totals. |
| category_id | integer | query | Category. |
| activity_id | integer | query | Activity. |
| contact_id | integer | query | Contact, as consumer or provider. |
| from | date | query | From this day (in your timezone). |
| to | date | query | Up to this day (in your timezone). |
| q | string | query | Search in the note. |
| page | integer | query | Page number. |
| pending_card | boolean | query | Only pending ones (card and API records). |
| uncategorized_card | boolean | query | Legacy filter: card transactions without a category. |
{"transactions": Transaction[], "current_page": int, "last_page": int, "total": int, "has_more": bool, "totals": {income, expense}|null}
POST /api/v1/transactions Records income, an expense or a transfer.
With a developer token the record gets source: "api" and is pending (is_pending: true): confirm it in the app or via confirm; while pending a contact is optional. Currency comes from the account and amounts are rounded to 2 decimals; a cross-currency transfer uses to_amount.
| Name | Type | In | |
|---|---|---|---|
| type * | string income | expense | transfer |
body | Type. |
| amount * | number | body | Amount in the account currency, up to 2 decimals. |
| account_id * | integer | body | Account; the currency always comes from it. |
| note | string | body | Note (up to 500 characters). |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| category_id | integer | body | Category of the same type (ignored for transfers). |
| contact_ids | array<integer> | body | Who it was for (consumers), first is primary; needed for income/expense. |
| contact_id | integer | body | Single consumer (instead of contact_ids). |
| provider_id | integer | body | Paid to / received from (provider). |
| activity_ids | array<integer> | body | Activities (several). |
| activity_id | integer | body | Single activity. |
| to_account_id | integer | body | Receiving account for a transfer (required). |
| to_amount | number | body | Amount received for a transfer (required); forced to amount when currencies match. |
| auto_confirm | boolean | body | true creates it already confirmed (then income/expense needs a contact). |
{
"type": "expense",
"amount": 45000,
"account_id": 12,
"category_id": 3,
"contact_ids": [
1
],
"note": "Tushlik",
"occurred_at": "2026-10-30T13:15:00+05:00"
}
{"transaction": Transaction}
POST
/api/v1/transactions/confirm
Confirms pending transactions in bulk (all of them without ids).
| Name | Type | In | |
|---|---|---|---|
| ids | array<integer> | body | Transactions to confirm (up to 500). |
| contact_id | integer | body | Applied to income/expense without a contact; required if any exist. |
{"confirmed": int, "pending_count": int}
PUT /api/v1/transactions/{transaction} Updates a transaction — only the fields you send change (its type cannot change).
With a developer token omitted fields keep their value; send null (or [] for lists) to clear one. A pending API record can be edited without a contact. Debt and recurring mirror records cannot be edited here (422).
| Name | Type | In | |
|---|---|---|---|
| transaction * | integer | path | Transaction ID. |
| amount | number | body | Amount in the account currency, up to 2 decimals. |
| account_id | integer | body | Account; the currency always comes from it. |
| note | string | body | Note (up to 500 characters). |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| category_id | integer | body | Category of the same type (ignored for transfers). |
| contact_ids | array<integer> | body | Who it was for (consumers), first is primary; needed for income/expense. |
| contact_id | integer | body | Single consumer (instead of contact_ids). |
| provider_id | integer | body | Paid to / received from (provider). |
| activity_ids | array<integer> | body | Activities (several). |
| activity_id | integer | body | Single activity. |
| to_account_id | integer | body | Receiving account for a transfer (required). |
| to_amount | number | body | Amount received for a transfer (required); forced to amount when currencies match. |
{
"amount": 52000,
"account_id": 12,
"category_id": 3,
"contact_ids": [
1
],
"note": "Tushlik, choy bilan",
"occurred_at": "2026-10-30T13:15:00+05:00"
}
{"transaction": Transaction}
DELETE /api/v1/transactions/{transaction} Deletes a transaction (debt and recurring mirrors return 422).
| Name | Type | In | |
|---|---|---|---|
| transaction * | integer | path | Transaction ID. |
{"ok": true}
POST /api/v1/transactions/{transaction}/confirm Confirms a card or API transaction, optionally setting category and contact.
Income/expense without a contact cannot be confirmed (422); amount, account and date are untouched.
| Name | Type | In | |
|---|---|---|---|
| transaction * | integer | path | Transaction ID (card or API records only). |
| category_id | integer | body | Category (null removes it). |
| contact_ids | array<integer> | body | Who it was for, first is primary. |
| contact_id | integer | body | Single consumer. |
| provider_id | integer | body | Provider (null removes it). |
{"transaction": Transaction}
PUT /api/v1/transactions/{transaction}/parts Splits the amount into parts (an empty list removes the split).
| Name | Type | In | |
|---|---|---|---|
| transaction * | integer | path | Transaction ID. |
| parts * | array<object> | body | Parts (up to 30): amount, contact_ids (required), category_id, note; they must add up to the amount. |
{"transaction": Transaction}
Debts
GET /api/v1/debts Debts with their payments and additions.
{"debts": Debt[]}
POST /api/v1/debts Records a new debt.
The debt total is not stored: principal + additions − payments.
| Name | Type | In | |
|---|---|---|---|
| direction * | string they_owe_me | i_owe |
body | Who owes: they owe you, or you owe. |
| person_name * | string | body | Person name (up to 80 characters). |
| amount * | number | body | Initial amount, up to 2 decimals. |
| currency * | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Debt currency. |
| note | string | body | Note (up to 500 characters). |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| due_date | date | body | Due date. |
| account_id | integer | body | Money moves through this account (same currency); omit for no money movement. |
| category_id | integer | body | Category of the money movement (or the credit purchase). |
| contact_id | integer | body | Contact. |
| activity_id | integer | body | Activity. |
| on_credit | boolean | body | Credit purchase (i_owe only): expense now, money on payment; account_id is ignored. |
{
"direction": "they_owe_me",
"person_name": "Sardor",
"contact_id": 5,
"amount": 2000000,
"currency": "UZS",
"account_id": 12,
"due_date": "2026-12-01",
"note": "Telefon uchun"
}
{"debt": Debt}
PUT /api/v1/debts/{debt} Partially updates a debt (only the fields sent).
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
| direction | string they_owe_me | i_owe |
body | Direction (fixed once the debt moved money). |
| person_name | string | body | Person name. |
| amount | number | body | Corrects the initial amount (and its money movement). |
| currency | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Currency (fixed once the debt moved money). |
| note | string | body | Note (up to 500 characters). |
| due_date | date | body | Due date. |
| contact_id | integer | body | Contact (null unlinks). |
| activity_id | integer | body | Activity (null unlinks). |
{
"due_date": "2027-01-15",
"note": "Muddat uzaytirildi"
}
{"debt": Debt}
DELETE /api/v1/debts/{debt} Deletes a debt with all its payments and money movements.
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
{"ok": true}
POST /api/v1/debts/{debt}/payments Records a debt payment (not more than the remaining amount).
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
| amount * | number | body | Amount in the debt currency, up to 2 decimals. |
| note | string | body | Note (up to 300 characters). |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| account_id | integer | body | Money moves through this account (its currency must match the debt); omit to record the debt only. |
| category_id | integer | body | Category of the money movement (when an account is given). |
{
"amount": 500000,
"account_id": 12,
"occurred_at": "2026-10-30T18:00:00+05:00"
}
{"debt": Debt}
PUT /api/v1/debts/{debt}/payments/{payment} Replaces a payment in full.
Without account_id the money movement is removed.
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
| payment * | integer | path | Payment ID. |
| amount * | number | body | Amount in the debt currency, up to 2 decimals. |
| note | string | body | Note (up to 300 characters). |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| account_id | integer | body | Money moves through this account (its currency must match the debt); omit to record the debt only. |
| category_id | integer | body | Category of the money movement (when an account is given). |
{"debt": Debt}
DELETE /api/v1/debts/{debt}/payments/{payment} Deletes a payment and restores the balance.
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
| payment * | integer | path | Payment ID. |
{"debt": Debt}
POST /api/v1/debts/{debt}/additions Adds to a debt ("lent more").
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
| amount * | number | body | Amount in the debt currency, up to 2 decimals. |
| note | string | body | Note (up to 300 characters). |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| account_id | integer | body | Money moves through this account (its currency must match the debt); omit to record the debt only. |
| category_id | integer | body | Category of the money movement (when an account is given). |
{"debt": Debt}
PUT /api/v1/debts/{debt}/additions/{addition} Replaces an addition in full.
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
| addition * | integer | path | Addition ID. |
| amount * | number | body | Amount in the debt currency, up to 2 decimals. |
| note | string | body | Note (up to 300 characters). |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| account_id | integer | body | Money moves through this account (its currency must match the debt); omit to record the debt only. |
| category_id | integer | body | Category of the money movement (when an account is given). |
{"debt": Debt}
DELETE /api/v1/debts/{debt}/additions/{addition} Deletes an addition and restores the balance.
| Name | Type | In | |
|---|---|---|---|
| debt * | integer | path | Debt ID. |
| addition * | integer | path | Addition ID. |
{"debt": Debt}
Recurring payments
GET /api/v1/recurrings Lists recurring payments.
{"recurrings": Recurring[]}
POST /api/v1/recurrings Creates a recurring payment.
The schedule is not stored; it is derived from starts_on and the interval.
| Name | Type | In | |
|---|---|---|---|
| title * | string | body | Title (up to 80 characters). |
| type * | string income | expense |
body | Type: income or expense. |
| amount * | number | body | Amount per period, up to 2 decimals. |
| currency * | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Currency; cannot change after the first payment. |
| account_id | integer | body | Default account (same currency). |
| category_id | integer | body | Category. |
| contact_id | integer | body | Contact. |
| debt_id | integer | body | Pay off this debt in installments (same currency). |
| interval_unit * | string day | week | month | year |
body | Interval unit. |
| interval_count | integer | body | Every N units (1–60, default 1). |
| anchor_day | integer | body | Payment day of month (1–31). |
| starts_on * | date | body | Start date. |
| ends_on | date | body | End date (not before the start). |
| total_periods | integer | body | Total number of periods (1–600). |
| auto_amount | boolean | body | Next amount follows the last paid amount. |
| penalty_daily_amount | number | body | Daily late penalty (amount). |
| penalty_daily_percent | number | body | Daily late penalty, percent (0–100). |
| is_active | boolean | body | Active (false = paused). |
| note | string | body | Note (up to 500 characters). |
{
"title": "Internet",
"type": "expense",
"amount": 120000,
"currency": "UZS",
"account_id": 12,
"category_id": 7,
"interval_unit": "month",
"interval_count": 1,
"anchor_day": 5,
"starts_on": "2026-11-05"
}
{"recurring": Recurring}
PUT /api/v1/recurrings/{recurring} Partially updates a recurring payment.
| Name | Type | In | |
|---|---|---|---|
| recurring * | integer | path | Recurring payment ID. |
| title | string | body | Title (up to 80 characters). |
| type | string income | expense |
body | Type: income or expense. |
| amount | number | body | Amount per period, up to 2 decimals. |
| currency | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Currency; cannot change after the first payment. |
| account_id | integer | body | Default account (same currency). |
| category_id | integer | body | Category. |
| contact_id | integer | body | Contact. |
| debt_id | integer | body | Pay off this debt in installments (same currency). |
| interval_unit | string day | week | month | year |
body | Interval unit. |
| interval_count | integer | body | Every N units (1–60, default 1). |
| anchor_day | integer | body | Payment day of month (1–31). |
| starts_on | date | body | Start date. |
| ends_on | date | body | End date (not before the start). |
| total_periods | integer | body | Total number of periods (1–600). |
| auto_amount | boolean | body | Next amount follows the last paid amount. |
| penalty_daily_amount | number | body | Daily late penalty (amount). |
| penalty_daily_percent | number | body | Daily late penalty, percent (0–100). |
| is_active | boolean | body | Active (false = paused). |
| note | string | body | Note (up to 500 characters). |
{
"amount": 135000,
"is_active": true
}
{"recurring": Recurring}
DELETE /api/v1/recurrings/{recurring} Deletes a recurring payment with all its entries and money movements.
| Name | Type | In | |
|---|---|---|---|
| recurring * | integer | path | Recurring payment ID. |
{"ok": true}
POST /api/v1/recurrings/{recurring}/entries Marks one period as paid (one entry per period).
| Name | Type | In | |
|---|---|---|---|
| recurring * | integer | path | Recurring payment ID. |
| period_on * | date | body | Which period this pays (period date). |
| amount | number | body | Amount; required unless debt_payment_id or transaction_id is given. |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| account_id | integer | body | Money moves through this account (same currency); omit to only mark the period. |
| category_id | integer | body | Category (defaults to the recurring one). |
| note | string | body | Note (up to 300 characters). |
| debt_payment_id | integer | body | Close the period with an existing debt payment (no new money). |
| transaction_id | integer | body | Close the period with an existing transaction (e.g. a card one). |
{
"period_on": "2026-11-05",
"amount": 120000,
"account_id": 12
}
{"recurring": Recurring}
PUT /api/v1/recurrings/{recurring}/entries/{entry} Updates a period entry.
| Name | Type | In | |
|---|---|---|---|
| recurring * | integer | path | Recurring payment ID. |
| entry * | integer | path | Entry ID. |
| period_on * | date | body | Which period this pays (period date). |
| amount | number | body | Amount. |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| account_id | integer | body | Money moves through this account (same currency); omit to only mark the period. |
| category_id | integer | body | Category (defaults to the recurring one). |
| note | string | body | Note (up to 300 characters). |
{"recurring": Recurring}
DELETE /api/v1/recurrings/{recurring}/entries/{entry} Deletes a period entry.
| Name | Type | In | |
|---|---|---|---|
| recurring * | integer | path | Recurring payment ID. |
| entry * | integer | path | Entry ID. |
{"recurring": Recurring}
Activities
GET /api/v1/activities Lists activities with their totals.
{"activities": Activity[]}
POST /api/v1/activities Creates an activity.
| Name | Type | In | |
|---|---|---|---|
| title * | string | body | Title (up to 80 characters). |
| icon | string | body | Icon (emoji). |
| color | string | body | Color. |
| starts_on | date | body | Start date. |
| ends_on | date | body | End date. |
| is_closed | boolean | body | Closed. |
| note | string | body | Note (up to 500 characters). |
| parent_id | integer | body | Parent activity (not itself or its descendant). |
{"activity": Activity}
GET /api/v1/activities/{activity} An activity with transactions, debts, child activities and monthly totals.
| Name | Type | In | |
|---|---|---|---|
| activity * | integer | path | Activity ID. |
{"activity": Activity, "transactions": Transaction[], "debts": Debt[], "children": Activity[], "monthly": array}
PUT /api/v1/activities/{activity} Partially updates an activity.
| Name | Type | In | |
|---|---|---|---|
| activity * | integer | path | Activity ID. |
| title | string | body | Title (up to 80 characters). |
| icon | string | body | Icon (emoji). |
| color | string | body | Color. |
| starts_on | date | body | Start date. |
| ends_on | date | body | End date. |
| is_closed | boolean | body | Closed. |
| note | string | body | Note (up to 500 characters). |
| parent_id | integer | body | Parent activity (not itself or its descendant). |
{"activity": Activity}
DELETE /api/v1/activities/{activity} Deletes an activity; its transactions and debts stay.
| Name | Type | In | |
|---|---|---|---|
| activity * | integer | path | Activity ID. |
{"ok": true}
Purchases
GET /api/v1/purchases The latest 50 purchases.
{"purchases": Purchase[]}
POST /api/v1/purchases Records a purchase or split bill: payments, participants and debts at once.
| Name | Type | In | |
|---|---|---|---|
| title * | string | body | Title (up to 120 characters). |
| contact_id | integer | body | Seller (contact). |
| category_id | integer | body | Expense category. |
| currency * | string UZS | USD | EUR | RUB | KZT | KGS | TJS | GBP | TRY | AED | JPY | CNY | KRW | CAD | CHF |
body | Purchase currency. |
| occurred_at | date-time | body | Time, ISO 8601; send it with a timezone offset (defaults to now). |
| note | string | body | Note (up to 500 characters). |
| service_percent | number | body | Service charge percent (0–100). |
| items | array<object> | body | Items: title, amount, shared_with (participant keys: me or c<contact id>). |
| participants | array<object> | body | Participants: contact_id (empty means you), kind (mine | owed creates a debt). |
| participant_amounts | object | body | Shares when there are no items: key (me, c<id>) → amount. |
| payments | array<object> | body | Payments: account_id, amount; each becomes an expense. |
| deferred | object | body | {amount} paid later; becomes a debt to the seller. |
{"purchase": Purchase}
GET /api/v1/purchases/{purchase} A single purchase.
| Name | Type | In | |
|---|---|---|---|
| purchase * | integer | path | Purchase ID. |
{"purchase": Purchase}
DELETE /api/v1/purchases/{purchase} Deletes a purchase with its payments and debts.
| Name | Type | In | |
|---|---|---|---|
| purchase * | integer | path | Purchase ID. |
{"ok": true}
Statistics
GET /api/v1/stats Income and expenses for a period, per currency.
Transfers are excluded; currencies are never summed together.
| Name | Type | In | |
|---|---|---|---|
| from | date | query | Period start (defaults to month start). |
| to | date | query | Period end (defaults to month end). |
{"range": {from, to}, "expense": [{currency, category_id, total}], "income": [...], "daily": [{currency, type, date, total}], "totals": [{currency, income, expense}]}