Ledgo API Get a token

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.

Base URL https://ledgo.uz/api/v1 OpenAPI schema ↗

Getting started

  1. Open @ledgoapp → Settings → «API & AI agents».
  2. Name the token and tap «Create». Copy it right away — it is shown only once.
  3. 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

Income and expenses added through the API AWAIT CONFIRMATION by default (is_pending: true, source: "api") — just like transactions from card notifications. You confirm them in the confirmation list in the app, or with POST /transactions/{id}/confirm. While pending, a contact is optional. Send "auto_confirm": true to write an already confirmed transaction — then a contact (contact_id) is required for income/expense.

Rules

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.
Response

{"user": User (+ timezone), "pending_count": int}

PUT /api/v1/settings Changes the language and base currency.
Parameters
NameTypeIn
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.
Response

{"user": User}

Accounts

GET /api/v1/accounts Accounts with their current balance.
Response

{"accounts": Account[]}

POST /api/v1/accounts Creates an account or card.

Each account keeps its own currency; there is no conversion.

Parameters
NameTypeIn
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).
Request example
{
    "name": "Humo",
    "type": "card",
    "currency": "UZS",
    "initial_balance": 1250000.5,
    "bank_name": "Kapitalbank",
    "last4": "4821"
}
Response

{"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.

Parameters
NameTypeIn
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).
Request example
{
    "name": "Humo",
    "type": "card",
    "currency": "UZS",
    "is_primary": true
}
Response

{"account": Account}

DELETE /api/v1/accounts/{account} Deletes an account (one with transactions returns 422; archive it instead).
Parameters
NameTypeIn
account * integer path Account ID.
Response

{"ok": true}

Categories

GET /api/v1/categories Lists categories.
Response

{"categories": Category[]}

POST /api/v1/categories Creates a category.
Parameters
NameTypeIn
type * string
income | expense
body Income or expense.
name * string body Name (up to 40 characters).
icon string body Icon.
color string body Color.
Response

{"category": Category}

PUT /api/v1/categories/{category} Updates a category (its type cannot change).
Parameters
NameTypeIn
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.
Response

{"category": Category}

DELETE /api/v1/categories/{category} Deletes a category; its transactions become uncategorized.
Parameters
NameTypeIn
category * integer path Category ID.
Response

{"ok": true}

Contacts

GET /api/v1/contacts Lists contacts (system contacts "Myself" and "Household" first).
Response

{"contacts": Contact[]}

POST /api/v1/contacts Creates a contact.

A contact tells who a transaction was for or paid to.

Parameters
NameTypeIn
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.
Request example
{
    "name": "Sardor",
    "type": "person",
    "phone": "+998901234567",
    "note": "Ishxonadan"
}
Response

{"contact": Contact}

GET /api/v1/contacts/{contact} A contact with debts, the last 100 transactions and spend totals.
Parameters
NameTypeIn
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).
Response

{"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.

Parameters
NameTypeIn
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).
Request example
{
    "name": "Sardor Karimov",
    "phone": "+998901234567",
    "is_dependent": false
}
Response

{"contact": Contact}

DELETE /api/v1/contacts/{contact} Deletes a contact; records stay, only the link is removed.
Parameters
NameTypeIn
contact * integer path Contact ID.
Response

{"ok": true}

POST /api/v1/contacts/{contact}/merge Merges contact from into this one and deletes it.
Parameters
NameTypeIn
contact * integer path ID of the contact that stays.
from * integer body ID of the contact merged in and removed.
Response

{"contact": Contact, "removed": int}

Transactions

GET /api/v1/transactions Transactions, newest first, 30 per page.
Parameters
NameTypeIn
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.
Response

{"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.

Parameters
NameTypeIn
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).
Request example
{
    "type": "expense",
    "amount": 45000,
    "account_id": 12,
    "category_id": 3,
    "contact_ids": [
        1
    ],
    "note": "Tushlik",
    "occurred_at": "2026-10-30T13:15:00+05:00"
}
Response

{"transaction": Transaction}

POST /api/v1/transactions/confirm Confirms pending transactions in bulk (all of them without ids).
Parameters
NameTypeIn
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.
Response

{"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).

Parameters
NameTypeIn
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.
Request example
{
    "amount": 52000,
    "account_id": 12,
    "category_id": 3,
    "contact_ids": [
        1
    ],
    "note": "Tushlik, choy bilan",
    "occurred_at": "2026-10-30T13:15:00+05:00"
}
Response

{"transaction": Transaction}

DELETE /api/v1/transactions/{transaction} Deletes a transaction (debt and recurring mirrors return 422).
Parameters
NameTypeIn
transaction * integer path Transaction ID.
Response

{"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.

Parameters
NameTypeIn
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).
Response

{"transaction": Transaction}

PUT /api/v1/transactions/{transaction}/parts Splits the amount into parts (an empty list removes the split).
Parameters
NameTypeIn
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.
Response

{"transaction": Transaction}

Debts

GET /api/v1/debts Debts with their payments and additions.
Response

{"debts": Debt[]}

POST /api/v1/debts Records a new debt.

The debt total is not stored: principal + additions − payments.

Parameters
NameTypeIn
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.
Request example
{
    "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"
}
Response

{"debt": Debt}

PUT /api/v1/debts/{debt} Partially updates a debt (only the fields sent).
Parameters
NameTypeIn
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).
Request example
{
    "due_date": "2027-01-15",
    "note": "Muddat uzaytirildi"
}
Response

{"debt": Debt}

DELETE /api/v1/debts/{debt} Deletes a debt with all its payments and money movements.
Parameters
NameTypeIn
debt * integer path Debt ID.
Response

{"ok": true}

POST /api/v1/debts/{debt}/payments Records a debt payment (not more than the remaining amount).
Parameters
NameTypeIn
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).
Request example
{
    "amount": 500000,
    "account_id": 12,
    "occurred_at": "2026-10-30T18:00:00+05:00"
}
Response

{"debt": Debt}

PUT /api/v1/debts/{debt}/payments/{payment} Replaces a payment in full.

Without account_id the money movement is removed.

Parameters
NameTypeIn
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).
Response

{"debt": Debt}

DELETE /api/v1/debts/{debt}/payments/{payment} Deletes a payment and restores the balance.
Parameters
NameTypeIn
debt * integer path Debt ID.
payment * integer path Payment ID.
Response

{"debt": Debt}

POST /api/v1/debts/{debt}/additions Adds to a debt ("lent more").
Parameters
NameTypeIn
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).
Response

{"debt": Debt}

PUT /api/v1/debts/{debt}/additions/{addition} Replaces an addition in full.
Parameters
NameTypeIn
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).
Response

{"debt": Debt}

DELETE /api/v1/debts/{debt}/additions/{addition} Deletes an addition and restores the balance.
Parameters
NameTypeIn
debt * integer path Debt ID.
addition * integer path Addition ID.
Response

{"debt": Debt}

Recurring payments

GET /api/v1/recurrings Lists recurring payments.
Response

{"recurrings": Recurring[]}

POST /api/v1/recurrings Creates a recurring payment.

The schedule is not stored; it is derived from starts_on and the interval.

Parameters
NameTypeIn
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).
Request example
{
    "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"
}
Response

{"recurring": Recurring}

PUT /api/v1/recurrings/{recurring} Partially updates a recurring payment.
Parameters
NameTypeIn
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).
Request example
{
    "amount": 135000,
    "is_active": true
}
Response

{"recurring": Recurring}

DELETE /api/v1/recurrings/{recurring} Deletes a recurring payment with all its entries and money movements.
Parameters
NameTypeIn
recurring * integer path Recurring payment ID.
Response

{"ok": true}

POST /api/v1/recurrings/{recurring}/entries Marks one period as paid (one entry per period).
Parameters
NameTypeIn
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).
Request example
{
    "period_on": "2026-11-05",
    "amount": 120000,
    "account_id": 12
}
Response

{"recurring": Recurring}

PUT /api/v1/recurrings/{recurring}/entries/{entry} Updates a period entry.
Parameters
NameTypeIn
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).
Response

{"recurring": Recurring}

DELETE /api/v1/recurrings/{recurring}/entries/{entry} Deletes a period entry.
Parameters
NameTypeIn
recurring * integer path Recurring payment ID.
entry * integer path Entry ID.
Response

{"recurring": Recurring}

Activities

GET /api/v1/activities Lists activities with their totals.
Response

{"activities": Activity[]}

POST /api/v1/activities Creates an activity.
Parameters
NameTypeIn
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).
Response

{"activity": Activity}

GET /api/v1/activities/{activity} An activity with transactions, debts, child activities and monthly totals.
Parameters
NameTypeIn
activity * integer path Activity ID.
Response

{"activity": Activity, "transactions": Transaction[], "debts": Debt[], "children": Activity[], "monthly": array}

PUT /api/v1/activities/{activity} Partially updates an activity.
Parameters
NameTypeIn
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).
Response

{"activity": Activity}

DELETE /api/v1/activities/{activity} Deletes an activity; its transactions and debts stay.
Parameters
NameTypeIn
activity * integer path Activity ID.
Response

{"ok": true}

Purchases

GET /api/v1/purchases The latest 50 purchases.
Response

{"purchases": Purchase[]}

POST /api/v1/purchases Records a purchase or split bill: payments, participants and debts at once.
Parameters
NameTypeIn
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.
Response

{"purchase": Purchase}

GET /api/v1/purchases/{purchase} A single purchase.
Parameters
NameTypeIn
purchase * integer path Purchase ID.
Response

{"purchase": Purchase}

DELETE /api/v1/purchases/{purchase} Deletes a purchase with its payments and debts.
Parameters
NameTypeIn
purchase * integer path Purchase ID.
Response

{"ok": true}

Statistics

GET /api/v1/stats Income and expenses for a period, per currency.

Transfers are excluded; currencies are never summed together.

Parameters
NameTypeIn
from date query Period start (defaults to month start).
to date query Period end (defaults to month end).
Response

{"range": {from, to}, "expense": [{currency, category_id, total}], "income": [...], "daily": [{currency, type, date, total}], "totals": [{currency, income, expense}]}