AndroMoney cloud ledger API for shared books
AndroMoney is KPMoney's personal expense ledger (package com.kpmoney.android; paid sibling com.andromoney.pro). Households log income, expenses and account-to-account transfers across cash, EasyCard (悠遊卡) and bank payments, attach receipt photos, schedule periodic salary and rent rows, convert foreign amounts against a main currency, and share a book or a single record with family via a link that opens in the app or on the AndroMoney website. Taiwan users can sign into the Ministry of Finance e-invoice portal, scan invoice QR codes, check winning invoices, run an income-tax estimate and a loan calculator, then drop matching receipts onto the ledger. Cloud sync plus Google Drive and Dropbox (folder Apps/_AndroMoney_) keep the on-device SQLite book on other phones; Plus and Pro sell as andromoney.plus.monthly / yearly and andromoney.pro.monthly / yearly on Google Play Billing. It sits with other money-manager apps for people who want one household book—including Taiwan e-invoice ingest—rather than a bank's own app.
Signed ledger amounts sit in mount next to the main-currency amount_to_main, with hash_key identity, sub_category, receipt_num, and the payment hashes in_payment / out_payment. Cloud pages ship table objects whose delete and update arrays refill record_table, payment_table (payment_name, card_number, bill_date, pay_date), carrier_table keyed by card_no, and receipt images under record_hash_key. Shared books expose url_id, entity_hash_key, entity_type (Record or Project), secrecy, permission and expired_at; comments hang off entity_hash_key with content and user_email. Entitlements report plan, record_limit, image_limit, update_limit and usage.record against free/plus/pro all_limits. Taiwan carrier invoices return invNum, sellerBan, sellerName, header amount and line details (description, quantity, unitPrice); currency-pair lookups such as TWD_USD return a rate string.
Family-office jobs can rebuild P&L from mount and amount_to_main; household bots can watch shared-link url_id and comment threads; quota monitors can compare usage.record to record_limit; tax ingest can turn invNum and sellerBan into ledger rows. openData Studio turns that surface into callable open data.
Screenshots
API surface
The endpoints and request/response examples below are reconstructed from the app's interface — illustrative, not a live capture.
Start cloud sync
POST
/v1/ledger-sync/sessions/openopenfinanceOpens a cloud-sync session for the household ledger. The client then pages the cloud book down, posts its local changes up, and closes the session. The response confirms with a message and a sync_start_time stamp.
Auth: Device-bound session sent as JSON body fields user, device and token (no Authorization header). Accept: application/json. device_id may be null; mobile_type defaults to 0.
- user
- device
- token
- now_utc
- device_id
- device_type
- mobile_type
- message
- sync_start_time
POST /v1/ledger-sync/sessions/open HTTP/1.1 Accept: application/json Content-Type: application/json { "body": { "user": "[email protected]", "device": "a1b2c3d4e5f6", "token": "YOUR_SYNC_TOKEN", "now_utc": "2026-10-21 08:15:00", "device_id": null, "device_type": "Pixel 8", "mobile_type": "0" } }{ "message": "ok", "sync_start_time": "2026-10-21 08:15:01" }Download ledger pages
GET
/v1/ledger-sync/pagesopenfinancePages the cloud book after a session opens. The response carries total_pages plus per-table objects: single-page tables (including carrier_table keyed by card_no) are applied first, then the multi-page record_table and image_table, whose delete and update arrays upsert the on-device ledger. The signed amount's wire name is mount.
Auth: Device-bound session as query params user, device and token (no Authorization header). Also sends device_type, page, the last-sync timestamp for the ledger table as sync_time, and now_utc. Accept: application/json.
- total_pages
- record_table
- image_table
- payment_table
- carrier_table
- delete
- update
- hash_key
- mount
- amount_to_main
- currency_code
- date
- category
- sub_category
- in_payment
- out_payment
- receipt_num
- payment_name
- card_no
- card_type
- card_encrypt
- payment_hash_key
- record_hash_key
- url
GET /v1/ledger-sync/pages?user=ada%40example.com&device=a1b2c3d4e5f6&device_type=Pixel%208&token=YOUR_SYNC_TOKEN&page=1&sync_time=2026-10-01%2000:00:00&now_utc=2026-10-21%2008:15:00 HTTP/1.1 Accept: application/json{ "total_pages": 2, "record_table": { "delete": [ {"hash_key": "rec_del_01"} ], "update": [ { "hash_key": "rec_a1", "mount": "-320.00", "amount_to_main": "-320.00", "currency_code": "TWD", "date": "2026-10-20 12:30:00", "category": "cat_food", "sub_category": "sub_lunch", "in_payment": null, "out_payment": "pay_cash", "in_amount": null, "out_amount": "320.00", "in_currency": null, "out_currency": "TWD", "payee": "payee_cafe", "project": null, "period": null, "fee": "0", "remark": "Lunch", "receipt_num": "AB12345678", "status": "20", "update_time": "2026-10-20 12:31:00" } ] }, "image_table": { "delete": [], "update": [ { "hash_key": "img_01", "record_hash_key": "rec_a1", "url": "https://images.example.com/rec_a1/img_01.jpg" } ] }, "payment_table": { "delete": [], "update": [ { "hash_key": "pay_cash", "payment_name": "Cash", "currency_code": "TWD", "kind": 0, "card_number": null, "bill_date": null, "pay_date": null, "total": "12000.00" } ] }, "carrier_table": { "delete": [], "update": [ { "card_no": "/ABC+123", "card_type": "3J0002", "card_name": "Phone barcode", "card_encrypt": "****", "payment_hash_key": "pay_cash", "auto_sync": 1 } ] } }Upload ledger mutations
POST
/v1/ledger-sync/changesopenfinancePushes local inserts, updates and deletes. body.record_table holds ledger-row changes (mount, amount_to_main, sub_category) and body.images holds receipt-photo inserts and deletes. The response is a plain message.
Auth: Device-bound session sent as JSON body.body fields user, device and token (no Authorization header). Top-level device_id repeats the device uuid. Accept: application/json.
- device_id
- body
- other_count
- record_table
- images
- count
- insert
- update
- delete
- hash_key
- mount
- amount_to_main
- sub_category
- receipt_num
- payment_user_email
- record_hash_key
- url
- order_no
- message
POST /v1/ledger-sync/changes HTTP/1.1 Accept: application/json Content-Type: application/json { "device_id": "a1b2c3d4e5f6", "body": { "user": "[email protected]", "device": "a1b2c3d4e5f6", "token": "YOUR_SYNC_TOKEN", "now_utc": "2026-10-21 08:16:00", "device_id": "a1b2c3d4e5f6", "device_type": "Pixel 8", "mobile_type": "0", "other_count": 1, "record_table": { "count": 2, "insert": [ { "hash_key": "rec_new_01", "mount": "-88.00", "amount_to_main": "-88.00", "currency_code": "TWD", "date": "2026-10-21 08:10:00", "category": "cat_food", "sub_category": "sub_coffee", "in_payment": null, "out_payment": "pay_cash", "in_amount": null, "out_amount": "88.00", "in_currency": null, "out_currency": "TWD", "payee": "payee_cafe", "project": null, "period": null, "fee": "0", "remark": "Coffee", "receipt_num": "", "status": "20", "payment_user_email": "[email protected]", "update_time": "2026-10-21 08:16:00" } ], "update": [], "delete": [ { "hash_key": "rec_del_01", "in_payment": null, "out_payment": "pay_cash", "payment_user_email": "[email protected]", "update_time": "2026-10-21 08:16:00" } ] }, "images": { "count": 1, "insert": [ { "hash_key": "img_02", "record_hash_key": "rec_new_01", "url": "https://images.example.com/rec_new_01/img_02.jpg", "order_no": "1", "payment_user_email": "[email protected]", "update_time": "2026-10-21 08:16:00" } ], "delete": [] } } }{ "message": "ok" }End cloud sync
POST
/v1/ledger-sync/sessions/closeopenfinanceCloses the sync session opened earlier. The request adds a sync_time stamp, which the client stores as the last-sync marker for the ledger table so the next pass only fetches newer changes.
Auth: Device-bound session sent as JSON body.body fields user, device and token (no Authorization header). Accept: application/json.
- user
- device
- token
- now_utc
- device_id
- device_type
- sync_time
- message
POST /v1/ledger-sync/sessions/close HTTP/1.1 Accept: application/json Content-Type: application/json { "body": { "user": "[email protected]", "device": "a1b2c3d4e5f6", "token": "YOUR_SYNC_TOKEN", "now_utc": "2026-10-21 08:17:00", "device_id": null, "device_type": "Pixel 8", "mobile_type": "0", "sync_time": "2026-10-21 08:17:00" } }{ "message": "ok" }Load shared records
GET
/v1/shared-books/{linkId}/entriesopenfinanceLoads one page of a shared book. The response returns share_link (url_id, entity_type Record or Project, secrecy, permission) plus nested records with record.mount and amount_to_main, joined to category, subcategory, payments, currency, comments and images. user_email marks which rows belong to the signed-in user.
Auth: Device-bound session as query params user, device and token (no Authorization header). The client requests 30 rows per page via per_page. Accept: application/json.
- message
- share_link
- url_id
- entity_type
- entity_hash_key
- permission
- secrecy
- expired_at
- title
- records
- user_email
- mount
- amount_to_main
- hash_key
- sub_category
- receipt_num
- payment_name
- subcategory
- id_category
GET /v1/shared-books/abcUrlId01/entries?device=a1b2c3d4e5f6&user=ada%40example.com&token=YOUR_SYNC_TOKEN&per_page=30&page=1 HTTP/1.1 Accept: application/json{ "message": null, "share_link": { "id": 4412, "url_id": "abcUrlId01", "title": "October household", "entity_type": "Record", "entity_hash_key": "rec_a1", "entity_user_id": 1001, "user_id": 1001, "permission": 1, "secrecy": 0, "expired_at": "2026-12-31 23:59:59", "created_at": "2026-10-01 09:00:00", "updated_at": "2026-10-20 12:00:00" }, "records": [ { "user_email": "[email protected]", "record": { "id": 88, "hash_key": "rec_a1", "mount": "-320.00", "amount_to_main": "-320.00", "currency_code": "TWD", "date": "2026-10-20 12:30:00", "category": "cat_food", "sub_category": "sub_lunch", "in_payment": null, "out_payment": "pay_cash", "in_amount": null, "out_amount": "320.00", "receipt_num": "AB12345678", "remark": "Lunch", "status": 20, "is_delete": false }, "category": { "hash_key": "cat_food", "category": "Food", "type": 20 }, "subcategory": { "hash_key": "sub_lunch", "subcategory": "Lunch", "id_category": "cat_food" }, "out_payment": { "hash_key": "pay_cash", "payment_name": "Cash", "currency_code": "TWD", "kind": 0, "total": "12000.00" }, "currency": { "currency_code": "TWD", "rate": "1" }, "comment": [], "image": [] } ] }List share links
GET
/v1/shared-books/linksopendataLists links the user owns (share_links) and links shared to them (links_share_to_me). The client queries entity_type Record and Project and merges both into one share-links screen. Opening a shared link launches the book inside the app.
Auth: Device-bound session as query params user, device and token (no Authorization header). entity_type is Record or Project; the client merges both lists. Accept: application/json.
- share_links
- links_share_to_me
- id
- url_id
- title
- entity_type
- entity_hash_key
- entity_user_id
- user_id
- permission
- secrecy
- expired_at
- created_at
- updated_at
GET /v1/shared-books/links?device=a1b2c3d4e5f6&user=ada%40example.com&token=YOUR_SYNC_TOKEN&entity_type=Record HTTP/1.1 Accept: application/json{ "share_links": [ { "id": 4412, "url_id": "abcUrlId01", "title": "October household", "entity_type": "Record", "entity_hash_key": "rec_a1", "entity_user_id": 1001, "user_id": 1001, "permission": 1, "secrecy": 0, "expired_at": "2026-12-31 23:59:59", "created_at": "2026-10-01 09:00:00", "updated_at": "2026-10-20 12:00:00" } ], "links_share_to_me": [ { "id": 5520, "url_id": "xyzUrlId99", "title": "Trip Kyoto", "entity_type": "Project", "entity_hash_key": "proj_kyoto", "permission": 0, "secrecy": 1, "expired_at": null } ] }Create share link
POST
/v1/shared-books/publishopendataCreates a share link for the given record hash_keys and returns its url_id. The client then sets a title on the new link. Sharing variants cover a record set, a project, or a single record.
Auth: Device-bound session sent as JSON body.body fields user, device and token (no Authorization header). Accept: application/json.
- user
- device
- token
- hash_keys
- msg
- success
- url_id
POST /v1/shared-books/publish HTTP/1.1 Accept: application/json Content-Type: application/json { "body": { "user": "[email protected]", "device": "a1b2c3d4e5f6", "token": "YOUR_SYNC_TOKEN" }, "hash_keys": ["rec_a1", "rec_b2"] }{ "msg": null, "success": true, "url_id": "abcUrlId01" }List users who share a payment
GET
/v1/accounts/collaboratorsopendataLists collaborators the owner has shared a payment account with. Each entry carries email, a numeric id, is_approved and permission; payment_hash_key picks the account.
Auth: Device-bound token as query param token plus owner_user (no Authorization header). Accept: application/json.
- owner_user
- payment_hash_key
- token
- id
- is_approved
- permission
GET /v1/accounts/collaborators?owner_user=ada%40example.com&payment_hash_key=pay_cash&token=YOUR_SYNC_TOKEN HTTP/1.1 Accept: application/json[ { "email": "[email protected]", "id": 2002, "is_approved": true, "permission": 1 } ]List record comments
GET
/v1/entry-notesopendataPages comments on shared payment records. The response wraps data plus success and total_pages; each comment carries entity_hash_key, entity_type Record and user_email. The client walks pages of 10 until the last one.
Auth: Device-bound session as query params user, device and token (no Authorization header). The client pages 10 rows at a time until total_pages.
- success
- total_pages
- data
- id
- hash_key
- content
- entity_hash_key
- entity_type
- entity_user_id
- root_entity_hash_key
- root_entity_type
- replies_count
- is_delete
- user_email
- user_name
- created_at
- updated_at
GET /v1/entry-notes?user=ada%40example.com&device=a1b2c3d4e5f6&token=YOUR_SYNC_TOKEN&start_date=2026-10-01&end_date=2026-10-22&page=1&per_page=10&payment_user_email=ada%40example.com&payment_hash_key=pay_cash HTTP/1.1{ "success": true, "total_pages": 1, "data": [ { "id": 91, "hash_key": "cmt_01", "content": "Split this with Bob", "entity_hash_key": "rec_a1", "entity_type": "Record", "entity_user_id": "1001", "root_entity_hash_key": "rec_a1", "root_entity_type": "Record", "root_entity_user_id": "1001", "replies_count": 0, "is_delete": false, "user_email": "[email protected]", "user_name": "Ada", "user_id": 1001, "created_at": "2026-10-20 13:00:00", "updated_at": "2026-10-20 13:00:00", "update_time": "2026-10-20 13:00:00" } ] }Post a record comment
POST
/v1/entry-notes/addopendataPosts a comment on a Record. The entity type is fixed to Record and the comment text travels in value; the result reuses the comment-list envelope.
Auth: Device-bound session sent as JSON body fields user, device and token (no Authorization header).
- user
- device
- token
- entity_hash_key
- entity_type
- entity_user_email
- value
- success
- data
- hash_key
- content
- user_email
- user_name
POST /v1/entry-notes/add HTTP/1.1 Content-Type: application/json { "user": "[email protected]", "device": "a1b2c3d4e5f6", "token": "YOUR_SYNC_TOKEN", "entity_hash_key": "rec_a1", "entity_type": "Record", "entity_user_email": "[email protected]", "value": "Split this with Bob" }{ "success": true, "total_pages": 1, "data": { "id": 91, "hash_key": "cmt_01", "content": "Split this with Bob", "entity_hash_key": "rec_a1", "entity_type": "Record", "user_email": "[email protected]", "user_name": "Ada" } }Get subscription entitlements
GET
/v1/plan/entitlementsopendataReads Plus/Pro entitlements after a Play Billing purchase. The response stores plan, expires_at, per-resource limits and usage; all_limits lists the free, plus and pro tiers. A companion purchase-report call submits the platform, product id, package name and purchase token.
Auth: Device-bound session as query params user, device and token (no Authorization header). Accept: application/json.
- plan
- platform
- expires_at
- message
- record_limit
- image_limit
- update_limit
- usage
- usage.record
- usage.image
- usage.update
- all_limits
- all_limits.free
- all_limits.plus
- all_limits.pro
GET /v1/plan/entitlements?user=ada%40example.com&device=a1b2c3d4e5f6&token=YOUR_SYNC_TOKEN HTTP/1.1 Accept: application/json{ "plan": "plus", "platform": "android", "expires_at": "2027-10-21T08:00:00Z", "message": null, "record_limit": 20000, "image_limit": 5000, "update_limit": 20000, "usage": { "record": 4120, "image": 88, "update": 3900 }, "all_limits": { "free": {"record": 500, "image": 50, "update": 500}, "plus": {"record": 20000, "image": 5000, "update": 20000}, "pro": {"record": 100000, "image": 20000, "update": 100000} } }Carrier invoice detail
POST
/v1/invoices/carrier/detailopenfinanceFetches one Taiwan carrier e-invoice. The app maps invNum, sellerBan and amount onto a ledger row (receipt_num, mount). A sister lookup covers barcode and QR scans; donateMark 1 means the invoice was donated.
Auth: Taiwan MOF e-invoice appID plus carrier cardNo, cardEncrypt and cardType. Also uuid, timeStamp and expTimeStamp. Sent as a form body, not the AndroMoney user/device/token.
- appID
- cardEncrypt
- cardNo
- cardType
- invDate
- invNum
- code
- msg
- v
- invPeriod
- invStatus
- invoiceTime
- amount
- sellerBan
- sellerName
- sellerAddress
- details
- description
- quantity
- unitPrice
- rowNum
POST /v1/invoices/carrier/detail HTTP/1.1 Content-Type: application/x-www-form-urlencoded appID=<mof-app-id>&cardEncrypt=****&cardNo=%2FABC%2B123&cardType=3J0002&invDate=2026%2F10%2F20&expTimeStamp=2147483647&timeStamp=1761034500&uuid=device-uuid-01&invNum=AB12345678{ "code": "200", "msg": "執行成功", "v": "0.5", "invNum": "AB12345678", "invDate": "2026/10/20", "invPeriod": "11510", "invStatus": "2", "invoiceTime": "12:30:00", "amount": "320", "sellerBan": "12345678", "sellerName": "Cafe Example", "sellerAddress": "Taipei", "details": [ { "rowNum": "1", "description": "Latte", "quantity": "1", "unitPrice": "120", "amount": "120" }, { "rowNum": "2", "description": "Sandwich", "quantity": "1", "unitPrice": "200", "amount": "200" } ] }Convert currency pair
GET
/v1/rates/pairopendataLooks up a rate for a {from}_{to} currency pair. The reply is an object whose key equals the requested pair and whose value is the rate string; the app stores it and recomputes amount_to_main when the main currency changes.
Auth: Provider apiKey as a query param (substitute your own). Separate from the AndroMoney user/device/token session. compact=ultra.
- q
- compact
- apiKey
GET /v1/rates/pair?compact=ultra&apiKey=<your-api-key>&q=TWD_USD HTTP/1.1{ "TWD_USD": "0.0312" }
Data categories
- transactions
- accounts
- share-links
- comments
- subscriptions
- fx-rates
- e-invoices
Where teams use this data
Rebuild household P&L from cloud pages
A tax or family-office job pages the cloud book, sums record_table.mount by category and date, and cross-checks amount_to_main so annual filings match the on-device ledger after a main-currency change.
Operate shared-book links
A household bot lists share_links url_id / entity_hash_key / expired_at, loads nested records for each link, and posts comments on entity_hash_key so roommates can split a receipt without exchanging the whole SQLite file.
Watch Plus/Pro quota
A sync watchdog reads plan, record_limit and usage.record against all_limits.plus / all_limits.pro and pauses upload insert batches before the cloud rejects new rows.
Ingest Taiwan carrier invoices
A receipt pipeline pulls carrier invNum, sellerBan, sellerName and details.amount, then writes receipt_num and mount onto the matching out_payment so the household book stays aligned with MOF invoices.
Frequently asked questions
What sits in an AndroMoney cloud download?
A paged JSON object with total_pages plus table keys such as record_table, image_table, payment_table and carrier_table. Each table has delete and update arrays. Ledger rows use the wire name mount (not amount) plus amount_to_main, hash_key, sub_category, receipt_num, in_payment and out_payment. carrier_table is keyed by card_no.
How do shared books work?
Creating a link posts hash_keys and returns url_id. Listing returns share_links you own and links_share_to_me. Loading a link pages nested records with category, subcategory, payments, currency, comments and images. entity_type is Record or Project; secrecy and permission control visibility. Opening a shared link launches the book in the app.
Does AndroMoney connect to a bank?
No bank connection is used. Payments are local accounts (cash, EasyCard, bank nicknames) synced as payment_table. Taiwan e-invoices come from the Ministry of Finance carrier and barcode services (invNum, sellerBan). FX rates come from a currency-pair lookup. Plus/Pro status is plan, record_limit and usage on the AndroMoney subscription object.
Which fields identify a ledger row?
hash_key is the stable id across devices. mount is the signed amount; amount_to_main is that amount in the book’s main currency. receipt_num stores a Taiwan invoice number when the row was ingested from a carrier or QR scan. status 10/20/30 are income, expense and transfer.
Apps similar to AndroMoney
- Money Manager Expense & Budget — Money Manager Expense & Budget by Realbyte records income and expenses with double-entry bookkeeping, graphs budget versus spending, and tracks credit-card settlement dates.
- Bluecoins — Bluecoins is an expense-and-budget app that tracks income and expenses, plans budgets, and syncs a shared book over Google Drive, Dropbox or OneDrive.
- Money Lover — Money Lover is an expense-tracker and money-manager app that records daily spending and income by category, sets budgets, and syncs across devices, with recurring bills and multi-currency support.
- Monefy — Monefy is an expense tracker and household budget planner that logs purchases, charts spending, supports multiple accounts and currencies, and can share a budget across devices.
- Wallet: Budget Expense Tracker — Wallet by BudgetBakers is a personal-finance manager that connects bank accounts to track expenses, sets custom budgets, and can share accounts with family.
- YNAB — YNAB (You Need A Budget) is a personal-budgeting app for iOS, Android and the web that lets people assign money to categories before they spend and can import transactions by linking financial accounts.
- Spendee — Spendee is a money-management app that tracks cash, bank accounts, e-wallets and crypto wallets, sets category budgets, and offers shared wallets for couples, families and roommates.
- Cashew — Cashew is an expense and budget tracker that logs income and spending, supports weekly or monthly budgets and multiple currencies, and can back up the on-device book to Google Drive.
Topics
- AndroMoney API
- record_table mount
- amount_to_main
- share_links url_id
- Taiwan e-invoice invNum
- KPMoney ledger
- 電子發票
- hash_key
Need this app's data API integrated?
We deliver scoped integrations for any named app — from USD 500 with source-code handoff, or hosted access billed per call. Tell us the data you need.
- NDA + SOW on every engagement
- Delivery in 3–7 days
- Payment only after acceptance
- Work scoped to authorized use