Bank Accounts API
List and manage team-scoped bank account records through public API v1.
Summary
List and manage account records for the authenticated team with bank-accounts.read or bank-accounts.write. Creating a record does not authorize a bank, establish a live feed, or start synchronization.
Capabilities
Endpoints
| Method | Endpoint | Scope | Purpose |
|---|---|---|---|
| GET | /bank-accounts | bank-accounts.read | List bank accounts |
| POST | /bank-accounts | bank-accounts.write | Create a bank account record |
| GET | /bank-accounts/{id} | bank-accounts.read | Retrieve one account |
| PATCH | /bank-accounts/{id} | bank-accounts.write | Update supplied fields |
| DELETE | /bank-accounts/{id} | bank-accounts.write | Delete an account record |
Prerequisites
Use a valid API key or OAuth token with the required effective scope. Read and write scopes are separate. Every account ID in a path must be a UUID belonging to the authenticated team. See Authentication for credential and role restrictions.
Concepts
Balances and Cash Flow
Legacy examples used /balances and /cash-flow, which are not public v1 paths.
Use:
- GET /metrics/balances with metrics.read
- GET /metrics/cash-balance with metrics.read
- GET /metrics/cash-flow with metrics.read
These are analytics operations, not bank-account CRUD.
Workflow
List Accounts
GET /bank-accounts returns { "data": [...] } for the authenticated team. Single-account and mutation responses return an account object directly. There is no documented cursor or page-size parameter.
The contract lists optional enabled and manual boolean filters. The current request schema uses strict booleans without converting URL strings, so ordinary query values such as enabled=true can fail validation. Until that path is corrected or verified in your deployment, list without these filters and filter the returned boolean fields in your client.
The response contains id, name, currency, type, enabled, balance, and manual. Name, currency, type, balance, and manual can be null. Provider credentials, institution details, and defaultCoding are not part of this public response, even if internal account records include them.
Create a Manual Record
POST /bank-accounts requires name. Optional fields are currency and manual. Explicitly set manual: true for an account intended for imported or manually entered transactions. Omitting it defaults to false; that still does not connect a bank. Newly created records default to enabled with a zero balance; currency and type remain unset unless supplied or updated through an accepted field.
The OpenAPI response advertises 201, but the current handler returns its account body with HTTP 200. Handle the actual successful response without depending on 201 alone.
curl --request POST \
--url https://api.eigenn.io/v1/bank-accounts \
--header "Authorization: Bearer $EIGENN_API_TOKEN" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: bank-account-operating-usd" \
--data '{
"name": "Operating Account",
"currency": "USD",
"manual": true
}'Do not send institution or account-number fields from legacy descriptions only. The current public create schema does not advertise them.
Update an Account
PATCH /bank-accounts/{id} can update documented fields such as:
- name
- enabled
- balance
- currency
- type
- defaultCoding
Account types are depository, credit, other_asset, loan, and other_liability. Omitted top-level fields remain unchanged. The path ID is authoritative even if a body ID is supplied. The API accepts a numeric balance, including negative values; the application’s Edit Account form separately restricts balance to zero or more.
Default coding
defaultCoding supplies the category and GL account used when transaction coding runs and no rule claims that field. It accepts optional categorySlug and glCode, either nullable. The supplied object replaces the saved object rather than merging nested fields; send all defaults you intend to keep. Send null for the whole object to clear it.
Updating the default does not itself recode existing transactions. Public GET/PATCH responses omit this object, so they cannot confirm its saved fields. Inspect the application’s coding view and a controlled coding preview to check its effect.
{
"defaultCoding": {
"categorySlug": "software",
"glCode": "6350"
}
}Precedence runs in one direction: the account default is the floor, a transaction rule outranks it, and a value that a person set by hand outranks both. See Transaction Coding.
Delete Carefully
DELETE /bank-accounts/{id} permanently removes the account and cascades to its transactions and recurring-transaction records. It does not remove the parent institution connection or revoke bank authorization. Export needed history first; use enabled: false when you intend to disable an account instead of delete its history.
Behavior specification
Account reads and writes are team-scoped. A missing or foreign-team ID currently produces no account row, then fails response validation with 500; do not assume the standard error catalog guarantees 404 on these operations.
Creation, update, and deletion happen before response validation. A failed response does not prove that a mutation was rolled back. Check account state before retrying. The optional HTTP idempotency guard stores successful responses only; see Errors and Safe Retries.
The public response schema is deliberately smaller than the internal record. Do not interpret an omitted field as proof that it is absent from storage. Balance analytics have separate scopes and filters from account CRUD.
Diagrams
Rendering diagram…
Screenshots
Not applicable: this page describes an API or a non-visual workflow rather than an application screen.
Verification
Use isolated synthetic accounts and disposable transactions for these acceptance checks; runtime verification remains pending.
- List accounts without filters and confirm the
{ "data": [...] }wrapper and team boundary. Exercise URL boolean filters separately and record any validation failure. - Create an explicitly manual account with name and currency. Check the actual success status and returned account fields; creation must not be interpreted as bank authorization.
- Patch the name and verify it in GET. Change default coding and confirm its effect through a coding preview, since public responses omit that field.
- Delete a disposable account and check that its associated transaction records are removed while its parent connection, if any, remains. A later single-account GET may return the documented response-validation limitation rather than 404.
- Check missing scopes and foreign-team IDs without changing another team’s data. After any failed mutation response, inspect state before retrying.