Wallet API Reference
Base URL: https://rest.budgetbakers.com/wallet
Full Specification: OpenAPI (Swagger UI) — all endpoints, schemas, and interactive testing
Authentication
All endpoints require an API token, obtained in the Web app (Premium plan required):
Authorization: Bearer <your_api_token>
Pagination
All User Data endpoints support pagination:
| Parameter | Default | Max | Description |
|---|---|---|---|
limit |
30 | 200 | Items per page |
offset |
0 | - | Items to skip |
Response includes nextOffset when more results are available.
Rate Limiting
API uses the Token Bucket Rate limiting algorithm, which allows short bursts of requests while enforcing an average rate over time.
- Capacity: 400 requests/hour per client
- Exceeding limit returns
429 Too Many Requests
| Header | Example | Description |
|---|---|---|
X-RateLimit-Limit |
500 |
Maximum request capacity |
X-RateLimit-Remaining |
487 |
Remaining request capacity |
Query Filter Prefixes
Some endpoints have query parameters that require filter prefixes in format: prefix.value
amount=gte.100 → amount ≥ 100
Text Filters
Use prefix operators to filter text fields (e.g. note, payee fields).
| Prefix | Meaning | Example |
|---|---|---|
eq. |
Exact match | payee=eq.Amazon |
contains. |
Contains substring (case-sensitive) | note=contains.Bill |
contains-i. |
Contains substring (case-insensitive) | note=contains-i.grocery |
Text filters can be specified up to 2 times for AND logic. E.g. note=contains-i.grocery¬e=contains-i.market
Range Filters
Used for numeric properties (e.g. amount) and datetime fields (recordDate, createdAt).
| Prefix | Meaning | Example (numeric) | Example (datetime) |
|---|---|---|---|
eq. |
Equals | amount=eq.100 |
createdAt=eq.2024-01-15T10:30:00Z |
gt. |
Greater than | amount=gt.100 |
createdAt=gt.2024-01-01T00:00:00Z |
gte. |
Greater than or equal | amount=gte.100 |
createdAt=gte.2024-01-01T00:00:00Z |
lt. |
Less than | amount=lt.500 |
createdAt=lt.2024-12-31T23:59:59Z |
lte. |
Less than or equal | amount=lte.500 |
createdAt=lte.2024-12-31T23:59:59Z |
Date-only values
When date-only values (no time component) are provided, whole-day (in UTC) semantics apply.:
| Filter | Equivalent | Meaning |
|---|---|---|
gt.2025-01-14 |
gte.2025-01-15T00:00:00Z |
Starting from Jan 15 |
gte.2025-01-14 |
gte.2025-01-14T00:00:00Z |
Starting from Jan 14 |
lt.2025-01-15 |
lt.2025-01-15T00:00:00Z |
Before Jan 15 |
lte.2025-01-15 |
lt.2025-01-16T00:00:00Z |
Up to and including Jan 15 |
eq.2025-01-13 |
gte.2025-01-13T00:00:00Z<.2025-01-14T00:00:00Z |
Entire day of Jan 13 |
Range filters support up to 2 conditions (AND logic) using repeated parameters:
amount=gte.100&amount=lte.500recordDate=gte.2024-01-01&recordDate=lt.2024-02-01
Comma-separated syntax is also accepted: amount=gte.100,lte.500
Data Synchronization
User data is synchronized from Wallet app. Changes made in the app may not appear immediately via API.
Initial Sync
Synchronization begins when you generate your first API key. Until the initial sync completes, requests
return 409 Conflict:
{
"error": "init_sync_in_progress",
"message": "Data synchronization in progress. Please retry later.",
"retry_after_minutes": 5
}
Ongoing Sync
After initial sync, data is always returned, but recent changes in user data may not appear immediately. These response headers indicate sync status:
| Header | Example | Description |
|---|---|---|
X-Last-Data-Change-At |
2024-01-28T14:23:45Z |
Timestamp of last data modification |
X-Last-Data-Change-Rev |
r1234 |
Revision counter. Compare values to detect changes. |
X-Sync-In-Progress |
false |
If true, response is valid but more changes may follow shortly. |
Agent Hints
Advisory information for AI agents. Hints can provide context about results, pagination, and rate limits.
Enable: Add agentHints=true to any request.
| Type | Severity | When Generated |
|---|---|---|
pagination.has_more |
instruction | More results available (includes next page URL) |
result.partial_match |
info | Some requested IDs not found |
result.empty |
info | No records match filters |
param.inferred |
info | Filtering date bound auto-inferred (relevant to recordDate in /records)
|
rate_limit.warning |
warning | Approaching rate limit |
Write Operations
Coming soon. Write endpoints (POST, PATCH, DELETE) are currently in development and will be available in a future release. The documentation below describes the planned behavior and constraints.
Batch Semantics & Partial Success
Most write endpoints accept arrays of items in a single request. Batch sizes are intentionally small to keep operations predictable.
| Operation | Batch? | Max items |
|---|---|---|
POST /records |
Yes (array) | 20 |
POST /accounts |
No (single item) | 1 |
POST /budgets |
No (single item) | 1 |
POST /categories/custom |
No (single item) | 1 |
PATCH /records, /accounts, /categories |
Yes (array) | 10 |
DELETE /v1/api/{type} |
Yes (array of IDs) | 10 |
Due to technical limitations, batch operations are not atomic. Each item is processed independently — a single request can partially succeed. When a batch contains mixed results, the response uses HTTP 207:
| Field | Description |
|---|---|
summary.total |
Total items in the request |
summary.succeeded |
Number of items that succeeded |
summary.clientErrors |
Items that failed due to invalid input (your fault) |
summary.serverErrors |
Items that failed due to internal errors (our fault) |
results[] |
Per-item results with success, error, and errorType (client_error or server_error) |
If all items succeed, the response is HTTP 200. Use validation=strict in PATCH requests to reject unknown fields instead of silently ignoring them.
Records
Creating Records
POST /records — up to 20 records per request. Maximum 20,000 records per user (includes records created in mobile/web apps).
| Field | Required | Validation |
|---|---|---|
accountId |
Yes | Must reference an existing account. Cannot be a bank-synced account. |
amount |
Yes | Non-zero. DECIMAL(19,2) precision. Negative = expense, positive = income. recordType is auto-derived from the sign. |
recordDate |
Yes | ISO 8601 format. Cannot be more than 24 hours in the future or more than 10 years in the past. |
paymentType |
Yes | One of: cash, debit_card, credit_card, transfer, voucher, mobile_payment, web_payment |
categoryId |
No | Any category UUID from GET /categories. If omitted, auto-assigned to Unknown Income or Unknown Expense based on amount sign. Internal system categories cannot be assigned (see Categories). |
recordState |
No | Default: cleared. Also: reconciled, uncleared |
labelIds |
No | Array of label UUIDs. Each must exist. |
note |
No | Max 255 characters |
counterParty |
No | Max 255 characters |
Transfer records cannot be created via the API. Transfer pairs created in the mobile app are partially patchable (metadata only) and must be deleted as a pair.
Patching Records
PATCH /records — up to 10 records per request. At least one field must be provided.
| Field | Patchable | Notes |
|---|---|---|
categoryId |
Yes | Category UUID from GET /categories. |
labelIds |
Yes | Replaces all labels. Use empty array [] to clear. |
note |
Yes | Max 255 characters |
counterParty |
Yes | Max 255 characters |
paymentType |
Yes | Same enum as create |
recordState |
Yes* | Locked on bank-synced and transfer records |
recordDate |
Yes* | Locked on bank-synced and transfer records |
amount |
Yes* | Locked on bank-synced and transfer records. |
accountId |
No | Immutable after creation |
* Fields marked with * are locked on bank-synced accounts and transfer records. Attempting to patch them returns a client_error. Metadata fields (note, labels, category, paymentType, counterParty) can always be patched.
Accounts
Creating Accounts
POST /accounts — single account per request. Maximum 50 accounts per user (includes accounts created in mobile/web apps).
| Field | Required | Validation |
|---|---|---|
name |
Yes | Max 80 characters |
accountType |
Yes | One of: General, Cash, CurrentAccount, SavingAccount, Insurance. Other types (CreditCard, Investment, Loan, etc.) are read-only and managed by the mobile app. |
currencyCode |
Yes | ISO 4217 fiat currency code (e.g. USD, EUR). Immutable after creation. |
initialBalance |
Yes | DECIMAL(19,2) precision |
color |
No | One of 16 supported colors. Randomly assigned if omitted. |
Patching Accounts
PATCH /accounts — up to 10 per request.
| Field | Patchable | Notes |
|---|---|---|
name |
Yes | Max 80 characters |
archived |
Yes | Boolean |
color |
Yes | one of the 16 supported colors |
initialBalance |
Yes | DECIMAL(19,2). Also recalculates the base-currency amount. |
excludeFromStats |
Yes | Boolean |
bankAccountNumber |
Yes | Max 80 characters. Empty string clears the value. |
accountType |
No | Immutable after creation |
currencyCode |
No | Immutable after creation |
Bank-synced accounts cannot be patched via the API. Use the mobile app to manage them.
Account deletion is not currently supported via the API.
Categories
Custom Categories
Custom categories are always derived from a base (system) category via parentId.
Create them for highly specific purposes that don't fit into existing base categories
(e.g. "Subscriptions" under "Entertainment").
POST /categories/custom — single category per request. Maximum 50 custom categories per user (includes categories created in mobile/web apps).
| Field | Required | Validation |
|---|---|---|
name |
Yes | Max 80 characters. Must be unique (case-insensitive) across all categories. |
parentId |
Yes | UUID of a system (base) category. Use GET /categories to find valid parents. Internal system categories cannot be used as parents (see below). |
color |
No | One of 16 supported colors. Defaults to parent's color. |
cardinality |
No | Wallet: must / need / want.
Defaults to parent's value. |
Restricted System Categories
The following internal system categories cannot be assigned to records and cannot be used as parentId for custom categories:
| Name | UUID |
|---|---|
| Debt | 5c5c4e20-00c8-8000-8000-000000000000 |
| Transfer | 5c5c4e21-00c8-8000-8000-000000000000 |
| Shopping List | 5c5c4e22-00c8-8000-8000-000000000000 |
| Uncategorized | 5c5c4e23-00c8-8000-8000-000000000000 |
Patching Categories
PATCH /categories — up to 10 per request. Works on both base and custom categories.
| Field | Patchable | Notes |
|---|---|---|
name |
Yes | Max 80 characters. Sets customName=true. Cannot be used with resetName. |
resetName |
Yes | Boolean. Resets name to system default. Only works on base (non-custom) categories. Cannot be used with name. |
color |
Yes | one of the 16 supported colors |
cardinality |
Yes | Use "none" to clear |
Deleting categories: Only custom categories can be deleted. The category must have no references from records, budgets, standing orders, or record rules. Use GET /{type}/references?id=... to check before deleting.
Budgets
Creating Budgets
POST /budgets — single budget per request. Maximum 50 budgets per user (includes budgets created in mobile/web apps).
| Field | Required | Validation |
|---|---|---|
name |
Yes | Max 80 characters |
currencyCode |
Yes | ISO 4217 fiat currency code. Immutable after creation. |
type |
Yes | BUDGET_INTERVAL_WEEK, BUDGET_INTERVAL_MONTH, BUDGET_INTERVAL_YEAR, or BUDGET_CUSTOM. Immutable after creation. |
limit |
Yes | Budget spending limit. Must be > 0. DECIMAL(19,2) precision. |
startDate / endDate |
BUDGET_CUSTOM only | YYYY-MM-DD format. endDate must be ≥ startDate. Not accepted for interval types. |
categoryIds |
No | Array of category UUIDs to track. Both base and custom category IDs accepted. If left empty, all are tracked. |
accountIds |
No | Array of account UUIDs. Limits budget tracking to these accounts. If left empty, all are tracked. |
labelIds |
No | Array of label UUIDs. If left empty, all are tracked. |
Patching budgets is currently disabled.
Deleting budgets is supported. Budgets have no child references, so they can always be deleted.
Deleting Entities
DELETE /v1/api/{type} — up to 10 IDs per request.
| Type | Deletable | Restrictions |
|---|---|---|
records |
Yes | Not on bank-synced accounts. Transfer pairs must both be in the same batch. No active standing order references. |
budgets |
Yes | None — always deletable |
standing-orders |
Yes | None — always deletable |
record-rules |
Yes | None — always deletable |
categories |
Custom only | Must have no references. Use GET /{type}/references to check. |
accounts |
No | Not supported via API |
labels |
No | Not supported via API |
goals |
No | Not supported via API |
Pre-check references before deleting categories and records:
GET /v1/api/{type}/references?id=uuid1,uuid2 (max 10 IDs).
The response shows which entities reference the given IDs. If references exist, the delete will fail with a client_error for that item.