开发者

API v1 参考

REST · JSON · OAuth 2.0 Bearer https://api.manilo.app 稳定——v1 内不会有破坏性变更

概览

Manilo API 是一套 JSON REST API,用于以编程方式管理你在 iOS 应用里使用的同一份账本——账户、分类、预算、共享群组和交易。你可以用它来构建导入工具、导出工具、同步桥接、仪表盘,或者驱动你自己的自动化流程。

  • 基础 URL: https://api.manilo.app——此前的基础 URL api.ledgy.app 仍可继续使用。
  • 版本前缀:本文档中的所有端点都挂载在 /api/v1/ 下。
  • 传输:仅支持 HTTPS,不接受 HTTP 请求。
  • 编码:请求体与响应体均为 JSON,UTF-8 编码。属性名使用 camelCase
  • 身份验证: Authorization: Bearer …——每个请求都必须携带。
  • Cloud 订阅:每个 v1 端点都需要。参见订阅校验
在找 AI 助手?如果你想让 Claude、ChatGPT 或 Cursor 代表你操作 Manilo,请使用位于 https://api.manilo.app/mcp 的模型上下文协议端点——详见集成。本文档介绍的 REST API,面向你自己编写的代码。

快速开始

三步完成你的第一个带身份验证的请求。

1. 生成个人访问令牌

  1. 登录你的 Manilo 仪表盘,打开设置 → API 访问
  2. 点击 + 新建令牌
  3. 给令牌起一个有描述性的名字(例如“Zapier — 每周导出”),选择你需要的权限范围(参见权限范围),也可以选填一个过期时间。
  4. 复制该令牌。它只显示一次。令牌以前缀 lgpat_ 开头,后跟 64 个十六进制字符。

同一页面会列出你的活跃令牌及其最后使用时间和权限数量,给出“30 天内过期”的提醒,并可通过垃圾桶图标随时撤销任意令牌。每个账户同时最多可持有 25 个活跃令牌。

2. 发起请求

cURL列出账户
# Replace lgpat_… with your token
curl "https://api.manilo.app/api/v1/accounts" \
  -H "Authorization: Bearer lgpat_a1b2c3d4e5…"

3. 查看响应

200 OKapplication/json
{
  "items": [
    {
      "id": "acc_01HK8V…",
      "name": "Wise EUR",
      "currencyCode": "EUR",
      "initialBalance": 1240.50,
      "icon": "wallet",
      "color": "#4A90E2",
      "order": 0,
      "createdAt": "2026-04-12T08:13:09Z",
      "updatedAt": "2026-05-01T17:02:31Z"
    }
  ],
  "totalCount": 1
}
把令牌当作密码来对待。任何拿到该令牌的人,都能在你授予的权限范围内读取或修改你的账本。一旦令牌泄露,请立即在设置 → API 访问中撤销。

身份验证

发往 /api/v1/ 的每个请求都必须携带 Authorization 请求头。支持两种令牌类型:

  • 个人访问令牌(PAT)——长期有效的 Bearer 令牌,在仪表盘的设置 → API 访问页面创建。格式为 lgpat_ + 64 个十六进制字符。可限定权限范围、可撤销,并可选择设置过期时间。推荐所有第三方集成使用。
  • 会话 JWT——签发给第一方应用(iOS、仪表盘)的短期令牌,不受权限范围限制。如果你能从已登录的会话中提取到一个,可以用它做一次性测试,但 PAT 才是官方支持的方式。

请求头格式

HTTP
Authorization: Bearer lgpat_a1b2c3d4e5f6…

令牌限制

  • 每个 Manilo 账户最多可拥有 25 个活跃 PAT
  • 创建时可以选填一个过期时间。已过期的令牌会返回 401 Unauthorized
  • 被撤销的令牌会立即失效——Manilo 只保存令牌的 SHA-256 哈希,绝不保存令牌本身,因此泄露的令牌无法找回,只能撤销并重新创建。

常见的身份验证失败

401
令牌缺失、格式错误、已过期或已被撤销。
403
令牌有效,但所请求的端点需要你的 PAT 不具备的权限范围,或者你的 Cloud 订阅未处于有效状态。

权限范围

PAT 采用默认拒绝模型。令牌只能调用它持有对应权限范围的端点;其余一律返回 403 Forbidden。请只授予你的集成确实需要的最小权限范围。

可用的权限范围:

accounts:read accounts:write categories:read categories:write budgets:read budgets:write groups:read groups:write transactions:read transactions:write tags:read tags:write recurring:read recurring:write settings:read settings:write

下面列出的每个端点,都会用一个紫色小标签标出它所需的权限范围。:write 权限范围并不隐含 :read——两者都需要时,请同时申请。

订阅校验

所有 v1 端点——包括只读端点——都要求调用方持有有效的 Manilo Cloud 订阅。如果订阅已中断、已过期或从未开通,API 将返回:

403 Forbidden
HTTP/1.1 403 Forbidden
X-Subscription-Required: true
Content-Type: application/json

{ "error": "Active cloud subscription required" }

通过 X-Subscription-Required 响应头,客户端可以把订阅拦截与一般的权限拒绝区分开。在 iOS 应用中或在 dashboard.manilo.app/upgrade 重新激活 Cloud,即可恢复访问。

错误

错误使用标准 HTTP 状态码。响应体是一个只含单个字段的 JSON 对象:

JSON
{ "error": "Human-readable message" }

你应当处理的状态码:

200
OK——已返回资源,或已返回列表。
201
Created——已创建新资源。Location 响应头指向该资源的规范 URL。
204
No Content——删除成功;无响应体。
400
校验失败——缺少必填字段、取值超出范围,或 JSON 格式错误。
401
身份验证失败——参见身份验证
403
权限被拒——权限范围不足,或订阅未生效。
404
资源不存在,或因授权原因被隐藏。
409
冲突——例如违反唯一性约束。
5xx
服务端故障。幂等的读取请求可以配合指数退避安全重试。

分页与筛选

列表端点默认返回所有匹配项。交易是唯一可能变得很大的资源,它支持基于游标的分页。

交易游标

cURL分页列表
curl "https://api.manilo.app/api/v1/transactions?limit=50&type=expense&dateFrom=2026-01-01" \
  -H "Authorization: Bearer lgpat_…"

响应中包含 nextCursor。把它作为 cursor 查询参数回传,即可获取下一页;当 nextCursornull 时,说明已经到达末尾。

  • limit——页面大小,取值被限制在 1..200。默认 50
  • cursor——不透明的令牌,请把它当作黑盒处理。
  • type, dateFrom, dateTo, categoryId, accountId, groupId——可选筛选条件;参见列出交易端点。

类型与格式

  • ID——不透明字符串。请勿解析,把它们视为区分大小写的 UTF-8 标识符。
  • 时间戳——UTC 时区的 ISO-8601,以 Z 结尾,例如 "2026-05-13T10:30:00Z"
  • 日期(例如交易的 date)——同样是 ISO-8601 格式,但只有日期部分有意义。
  • 金额——以主单位表示的 JSON 数字,最多 4 位小数(例如 12.50)。绝不使用最小货币单位。始终与 currencyCode 成对出现。
  • 货币代码——ISO-4217,恰好三个大写字母(例如 "EUR", "USD", "GBP")。
  • 删除——所有删除操作都是软删除。被删除的条目不再出现在列表或获取响应中;共享成员与历史小票会被保留。
  • 连带影响——删除账户、分类或群组时,依赖它们的交易保持不变,只是引用关系被解除。删除账户端点接受一个显式的处理策略。

账户

账户是承载余额的容器——银行账户、信用卡、现金钱包、券商账户。每笔交易都关联到一个账户(转账则关联两个)。

GET /api/v1/accounts accounts:read

返回已认证用户拥有或有权访问的每一个账户。

GET /api/v1/accounts/{id} accounts:read

按 id 获取单个账户。未找到时返回 404

POST /api/v1/accounts accounts:write

创建新账户。返回 201,响应体为创建出的对象,并带有 Location 响应头。

请求体
字段类型说明
namestring必填显示名称。最多 100 个字符。
currencyCodestring必填ISO-4217,恰好 3 个字母。
initialBalancenumber必填currencyCode 计的期初余额。
orderinteger必填排序位置。数值越小越靠前。
iconstring可选来自 /api/v1/icons 的图标标识符。最多 50。
colorstring可选十六进制颜色,例如 "#4A90E2"。最多 20。
iconColorstring可选覆盖图标的着色。
请求
POST /api/v1/accounts
{
  "name": "Cash",
  "currencyCode": "EUR",
  "initialBalance": 50.00,
  "order": 2,
  "icon": "wallet",
  "color": "#22C55E"
}
201 Created
{
  "id": "acc_01HK8V…",
  "name": "Cash",
  "currencyCode": "EUR",
  "initialBalance": 50.00,
  "order": 2,
  "icon": "wallet",
  "color": "#22C55E",
  "iconColor": null,
  "shareInviteToken": null,
  "createdAt": "2026-05-13T10:30:00Z",
  "updatedAt": "2026-05-13T10:30:00Z"
}
PUT /api/v1/accounts/{id} accounts:write

替换已有账户。请求体与创建相同;所有字段都必须提供。

DELETE /api/v1/accounts/{id} accounts:write

软删除一个账户。通过 action 查询参数决定它名下的交易如何处理。

查询参数
字段类型说明
actionenum可选Detach(默认):清除每笔交易上的账户引用。Move:把交易改挂到 moveTargetAccountId 指定的账户。DeleteAll:软删除你拥有的每一笔关联交易。
moveTargetAccountIdstring可选action=Move 时必填。目标账户 id。

分类

分类用来标注一笔交易的用途(日用杂货、房租、自由职业收入)。系统分类为只读,所有用户共用;用户分类由你自行管理。分类组则把相关的分类归到一起。

GET /api/v1/categories/system-categories categories:read

返回 Manilo 精选的“常用”分类集——也就是 iOS 应用自带的初始分类。它们在全局范围内做版本管理,可以安全地按 version 缓存。

用户分类

GET /api/v1/categories categories:read

列出所有用户自定义的分类。

GET /api/v1/categories/{id} categories:read

按 id 获取单个用户分类。

POST /api/v1/categories categories:write

创建一个用户分类。

请求体
字段类型说明
namestring必填显示名称。最多 100。
typestring必填"income""expense"
orderinteger必填在所属分类组中的排序位置。
isPinnedboolean必填置顶到选择器的最前面。
categoryGroupIdstring可选父分类组的 id;未分组时为 null
iconstring可选图标标识符。
colorstring可选十六进制颜色。
PUT /api/v1/categories/{id} categories:write

替换一个用户分类。请求体与创建相同。

DELETE /api/v1/categories/{id} categories:write

软删除一个用户分类。交易不会被删除,只会清空它们的 categoryId

分类组

GET /api/v1/categories/groups categories:read

列出你的分类组。

GET /api/v1/categories/groups/{id} categories:read

获取单个分类组。

POST /api/v1/categories/groups categories:write

创建一个分类组。

请求体
字段类型说明
namestring必填显示名称。最多 100。
orderinteger必填排序位置。
iconstring可选图标标识符。
colorstring可选十六进制颜色。
PUT /api/v1/categories/groups/{id} categories:write

替换一个分类组。

DELETE /api/v1/categories/groups/{id} categories:write

软删除一个分类组。子分类会保留下来——只会清空它们的 categoryGroupId

预算

预算在一个周期性的时间窗内,为某个分类的支出设定上限(当 categoryId 为 null 时,则针对整个账本)。设置 groupId 即可把预算共享给某个群组。

GET /api/v1/budgets budgets:read

列出所有预算。

GET /api/v1/budgets/{id} budgets:read

获取单个预算。

POST /api/v1/budgets budgets:write

创建一个预算。

请求体
字段类型说明
amountnumber必填每个周期的上限。必须大于 0。
currencyCodestring必填ISO-4217。
periodinteger必填0 每周 · 1 每月 · 2 每季度 · 3 每年。
startDatestring (ISO-8601)必填第一个周期的开始时间。
isActiveboolean必填该预算当前是否生效。
endDatestring (ISO-8601)可选在此日期之后停止跟踪。
namestring可选标签。最多 200。
categoryIdstring可选要跟踪的分类。省略则对全部支出做预算。
groupIdstring可选要共享到的群组。省略则为个人预算。
PUT /api/v1/budgets/{id} budgets:write

替换一个预算。

DELETE /api/v1/budgets/{id} budgets:write

软删除一个预算。

群组

群组就是共享账本——一个家庭、一次旅行、一间合租公寓。每位成员看到的都是同一批交易;所有权仍然归个人。成员与邀请在 iOS 应用中管理;这里的 API 只覆盖群组资源本身。

GET /api/v1/groups groups:read

列出你拥有或参与的群组。

GET /api/v1/groups/{id} groups:read

获取单个群组。

POST /api/v1/groups groups:write

创建一个群组。你将成为它的所有者;请在 iOS 应用中邀请成员。

请求体
字段类型说明
namestring必填显示名称。最多 100。
descriptionstring可选自由文本。最多 500。
iconstring可选图标标识符。
colorstring可选十六进制颜色。
PUT /api/v1/groups/{id} groups:write

替换群组的元数据。

DELETE /api/v1/groups/{id} groups:write

软删除一个群组。成员将失去可见性;底层交易恢复为个人所有。

交易

交易是账本里的动词。它们共有四种形态:income, expense, transfer(账户之间),以及 adjustment(一次性的余额校正)。基础端点用于创建收入和支出;转账有自己的端点;面向大批量导入的场景还提供了批量变体。

GET /api/v1/transactions transactions:read

列出交易,支持游标分页与筛选。

查询参数
字段类型说明
limitinteger可选页面大小。1..200。默认 50
cursorstring可选上一次响应返回的不透明续传令牌。
typestring可选"income", "expense", "transfer",或 "adjustment"
dateFromstring (ISO-8601)可选下界,含该值。
dateTostring (ISO-8601)可选上界,含该值。
categoryIdstring可选筛选到某一个分类。
accountIdstring可选筛选到某一个账户。
groupIdstring可选筛选到某个共享群组。
200 OK
{
  "items": [ /* TransactionDto[] */ ],
  "totalCount": 317,
  "nextCursor": "eyJrIjoiMjAyNi0wNS0xM1QxMDoz…"
}
GET /api/v1/transactions/{id} transactions:read

获取单笔交易。

POST /api/v1/transactions transactions:write

创建单笔收入或支出交易。转账请使用 /transfer

请求体
字段类型说明
typestring必填"income""expense"
amountnumber必填currencyCode 计的正数金额。
currencyCodestring必填ISO-4217。
datestring (ISO-8601)必填交易发生的时间(UTC)。
accountIdstring可选来源或目标账户。
categoryIdstring可选分类标签。
payeestring可选商家或交易对方。最多 200。
notestring可选自由格式备注。最多 2000。
groupIdstring可选共享给某个群组。
exchangeRatenumber可选currencyCode ≠ 用户主货币时使用的汇率。
convertedAmountnumber可选以用户主货币计的金额。
请求昨天用 Wise 消费的 €8.50 咖啡
POST /api/v1/transactions
{
  "type": "expense",
  "amount": 8.50,
  "currencyCode": "EUR",
  "date": "2026-05-12T09:14:00Z",
  "accountId": "acc_01HK8V…",
  "categoryId": "cat_food_drinks",
  "payee": "Pret",
  "note": "Flat white"
}
PUT /api/v1/transactions/{id} transactions:write

替换一笔交易。请求体与创建相同。

DELETE /api/v1/transactions/{id} transactions:write

软删除单笔交易。

POST /api/v1/transactions/transfer transactions:write

在两个账户之间创建一笔转账。没有分类。跨币种转账时,请提供 exchangeRate,以及以目标货币计的 convertedAmount

请求体
字段类型说明
fromAccountIdstring必填来源账户。
toAccountIdstring必填目标账户。必须与来源账户不同。
amountnumber必填转出金额,以 currencyCode 计。
currencyCodestring必填来源货币,ISO-4217。
datestring (ISO-8601)必填转账日期。
exchangeRatenumber可选当来源货币与目标货币不同时必填。
convertedAmountnumber可选记入目标账户的金额,以其自身货币计。
notestring可选最多 2000。

批量操作

专为导入工具设计。每批最多 100 条,并以尽力而为的方式执行:单条数据有误不会回滚其余条目。成功的条目和逐条的错误会分开返回,方便你只重试失败的部分。

POST /api/v1/transactions/bulk transactions:write

一次调用创建最多 100 笔交易。

200 OK
{
  "items": [ /* successful TransactionDto[] */ ],
  "errors": [
    { "index": 3, "error": "Invalid currency code" }
  ]
}
PUT /api/v1/transactions/bulk transactions:write

一次调用更新最多 100 笔交易。每一条除完整的交易体外,还必须附带它的 id

POST /api/v1/transactions/bulk-delete transactions:write

一次调用软删除最多 100 笔交易。这里使用 POST 而非 DELETE,以确保所有 HTTP 客户端都接受请求体。

请求体
字段类型说明
idsstring[]必填1 到 100 个交易 id。
200 OK
{
  "deleted": 97,
  "notFound": [ "tx_old1", "tx_old2", "tx_old3" ]
}

交易对象

字段类型说明
idstring不透明标识符。
typestringincome · expense · transfer · adjustment.
amountnumbercurrencyCode 计。
currencyCodestringISO-4217。
exchangeRatenumber | null在跨币种条目上会设置该值。
convertedAmountnumber | null以用户主货币表示的同一笔金额。
datestringISO-8601 UTC。
accountIdstring | null对转账而言,这里是来源账户。
toAccountIdstring | null仅转账才有的目标账户。
categoryIdstring | null转账时为 null。
groupIdstring | null共享给群组时会设置该值。
payeestring | null商家或交易对方。
notestring | null自由文本。
receiptImagePathstring | null所附小票的路径。通过小票端点获取(独立于 v1)。
recurringTransactionIdstring | null如果这条记录由周期性规则生成,则会设置该值。
sourcestring来源标记——api, mobile, web, import 等。
createdAtstringISO-8601 UTC。
updatedAtstringISO-8601 UTC。

图标

Manilo 内置了一套精选图标和配色,在账户、分类、群组中通用。把目录取回一次并缓存起来,创建资源时复用这些标识符即可。

GET /api/v1/icons

返回完整的图标库(按类别分组),以及受支持的配色。带版本号——可以安全地按 version 字段缓存。

200 OK
{
  "version": "2026.05.10",
  "library": "font-awesome-6",
  "categories": [
    {
      "id": "finance",
      "name": "Finance",
      "icons": [ "wallet", "credit-card", "piggy-bank" ]
    }
  ],
  "colors": [
    { "name": "Blue", "hex": "#4A90E2" },
    { "name": "Green", "hex": "#22C55E" }
  ]
}

版本策略

  • v1 内不会有破坏性变更。我们只会新增端点、新增可选字段和新增枚举值。字段的类型、可空性或必填性都不会改变。
  • 新增枚举值不算破坏性变更。遇到无法识别的 type, sourceaction 取值时,请按“不渲染”处理,而不要直接崩溃——随着产品成长,我们会继续新增这些取值。
  • 带日期标记的破坏性变更(如果确有必要)将发布在 /api/v2/ 之下,并至少提供 6 个月的并行可用期,同时在 v1 响应中加上弃用响应头。

支持

发现了 bug、希望增加某个端点,或者遇到了文档未覆盖的情况?请打开帮助中心,或发邮件至 support@manilo.app——报告问题时,请附上请求 id(会在 X-Request-Id 响应头中回显)。

涉及安全的披露(令牌泄露、权限绕过、未经授权的读取),请写信至 security@manilo.app