Transactions API
List, create, categorize, update, attach, and remove eligible transactions through public API v1.
Summary
Transaction operations use the transactions.read scope or the transactions.write scope. Transaction reads and target-row mutations are scoped to the authenticated team. Use account, category, tag, and assignee identifiers that belong to that team.
Capabilities
Endpoints
| Method | Endpoint | Scope | Purpose |
|---|---|---|---|
| GET | /transactions | transactions.read | List and filter transactions |
| POST | /transactions | transactions.write | Create one transaction |
| GET | /transactions/{id} | transactions.read | Retrieve one transaction |
| PATCH | /transactions/{id} | transactions.write | Update the fields that you send |
| DELETE | /transactions/{id} | transactions.write | Delete a manually created transaction |
| POST | /transactions/bulk | transactions.write | Create up to 100 transactions |
| PATCH | /transactions/bulk | transactions.write | Update many transactions |
| DELETE | /transactions/bulk | transactions.write | Delete up to 100 manual transactions |
| POST | /transactions/{transactionId}/attachments/{attachmentId}/presigned-url | transactions.read | Create a time-limited attachment URL |
Prerequisites
Use a bearer token with the scope for each operation; write access does not replace read access. Obtain a real account ID from Bank Accounts API. Start with disposable records and inspect the response limitations below before enabling automated writes.
Concepts
Pagination
The list response contains:
- data
- meta.cursor
- meta.hasNextPage
- meta.hasPreviousPage
The response does not contain a total count. Keep the same filters and sort when you request the next cursor, and treat the cursor as opaque. A full final page can report hasNextPage: true; the next page can be empty.
Amounts are signed: positive values are income and negative values are expenses. manual: true identifies API-created, form-created, and CSV-imported records, not just records entered by hand. A transaction is fulfilled when it has an attachment or its status is completed; fulfillment does not prove that someone reviewed the receipt.
Workflow
List Transactions
The GET /transactions operation accepts cursor pagination. It accepts pageSize from 1 to 10,000, with a default of 40. Use whole numbers.
The filter schema includes q, start, end, categories, tags, accounts, assignees, statuses, recurring, attachments, manual, amountRange, amount, type, needsReview, syncStates, glCodes, and sort. manual and attachments use include or exclude. type uses income or expense. Send list-valued filters with the serialization supported by your client and confirm the returned rows before relying on a filter.
needsReview selects rows with a pending receipt-match suggestion, or rows without an attachment whose status is not completed, excluded, or archived. Current limitation: this query field uses a strict boolean schema without URL-string conversion. A request containing needsReview=true can fail validation; the example below omits it.
curl --request GET \
--url "https://api.eigenn.io/v1/transactions?pageSize=100" \
--header "Authorization: Bearer $EIGENN_API_TOKEN"Create a Transaction
The POST /transactions operation needs these fields:
- name
- amount
- currency
- date
- bankAccountId
curl --request POST \
--url https://api.eigenn.io/v1/transactions \
--header "Authorization: Bearer $EIGENN_API_TOKEN" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: transaction-office-supplies-2026-07-11" \
--data '{
"name": "Office Supplies Purchase",
"amount": -150.75,
"currency": "USD",
"date": "2026-07-11T12:00:00.000Z",
"bankAccountId": "00000000-0000-0000-0000-000000000000",
"categorySlug": "office-supplies",
"internal": false
}'Replace the example account ID and category slug with values from your team. Required strings must be non-empty. Use a valid date and currency; non-empty text alone does not establish that the database accepts a value.
Optional fields are assignedId, categorySlug, note, internal, and attachments. Each attachment describes an already-uploaded file using path (path segments), name, size, and type. This request does not upload file bytes. New transactions are marked manual and start with status posted.
The single-create response is a transaction object. The current handler returns 200 when response validation succeeds, although the OpenAPI response advertises 201. Check the response limitations below: a failed response can follow a saved transaction.
Update
PATCH /transactions/{id} accepts name, amount, currency, date, bankAccountId, categorySlug, glCode, accountingDate, departmentId, locationId, classId, vendorName, status, internal, recurring, frequency, note, assignedId, taxRate, and taxAmount. Omitted fields normally retain their values. Use the schema's nullable fields when clearing a value; not every field accepts null.
Statuses are pending, archived, completed, posted, and excluded. Frequencies are weekly, monthly, annually, and irregular. Setting recurring is a classification change, not a command to create future payments.
Category changes reset tax overrides. Sending categorySlug, including null or the existing slug, clears stored tax rate, amount, and type. This also overrides tax values sent in that same request. Returned tax values can then come from the category. Set the category first, then submit any tax override separately and inspect the result.
Delete and Exclude
Delete operations remove only records marked manual: true, including CSV imports. They do not remove bank-synced records. To retain a synced row with an excluded classification, PATCH its status to excluded.
Single delete returns { "id": "..." } on success. A missing, foreign-team, or bank-synced ID currently produces a response-validation 500, not a reliable 400 or 404.
Bulk delete accepts a JSON array of 1–100 UUIDs. It returns an array of { "id": "..." } objects for rows actually deleted. Ineligible or missing IDs are omitted; an empty array can be a successful response. Compare the returned IDs with the requested IDs.
Bulk Operations
| Operation | Request and response | Limitations |
|---|---|---|
| Create | JSON array of 1–100 create objects; returns a transaction array | Current success status is 200, despite advertised 201. Attachment metadata is accepted by the schema but is discarded by bulk creation. |
| Update | Object containing ids and shared changes; returns data and pagination meta | Supported changes are categorySlug, glCode, status, frequency, internal, note, assignedId, recurring, and tagId. Amount, date, name, and other single-update fields are not bulk fields. |
| Delete | JSON array of 1–100 UUIDs; returns deleted ID objects | Only matching manual rows in the team are deleted. |
Bulk update has no explicit 100-ID schema limit. Keep batches small enough to inspect and reconcile. Setting tagId adds that tag; it does not replace all existing tags. A null tag ID does not remove assignments. Category updates clear tax overrides just as single updates do.
Bulk update does not provide the same coding-constraint check as single update. Treat the two operations as separate contracts. Bulk changes are not an all-or-nothing workflow: tag, provenance, data, or follow-up work can persist before a later failure. Inspect the saved rows after an error.
Use one idempotency key for one complete write request. Do not reuse it for a different body or ID set. The shared replay guard does not compare request bodies, and failed responses are not cached as successful writes. See Errors and Retry Safety.
Attachments
Transaction responses can include attachment IDs, paths, filenames, sizes, and MIME types.
The presigned-URL operation returns url, expiresAt, and nullable fileName; the URL lasts 60 seconds. It checks that the attachment belongs to the transaction and team and that its stored path belongs to the team. Stored paths are not public URLs.
The handler defaults to download mode. The download query uses boolean coercion, so the non-empty URL string false can still become true. Do not depend on download=false for inline viewing until that behavior is verified for your deployment.
Behavior specification
Response Limitations and Recovery
The current response schema requires a non-null account and bank connection, although manual accounts can have no bank connection. A single read, list, create, or update can therefore fail with 500 when a returned transaction has no connection. Null account currency can also fail this schema. A single missing-row GET or PATCH likewise fails response validation instead of returning a dependable 404.
Response validation runs after mutations. Creation, updates, attachments, or deletion can already have persisted when the caller receives an error. Check the application ledger and saved record before retrying; repeating a failed create can create another transaction. Do not treat an unsuccessful HTTP response as proof of rollback.
The public response schema strips fields it does not declare, including GL code, accounting dimensions, sync state, and assigned-user details. Their use in a request does not guarantee they appear in the returned object. Confirm those changes in the application where needed.
Common Errors
| Status | Cause |
|---|---|
400 / 422 | Invalid request data; attachment URL requests also return 400 for a missing stored path |
401 | Missing or invalid bearer token |
403 | Required scope or current-role permission is missing |
404 | Attachment lookup or attachment path ownership check failed; not a universal missing-transaction response |
409 | Another request with the same idempotency key is still in progress |
429 | Request-rate limit exceeded |
500 | Response-schema mismatch, missing single-row result, storage failure, or an unmapped database/coding error |
Diagrams
Rendering diagram…
Screenshots
Not applicable: this page describes an API or a non-visual workflow rather than an application screen.
Verification
- List transactions with a documented filter and confirm the returned rows match it.
- Create a manual transaction using the documented required fields, then retrieve it and compare the saved values.
- Update one editable field and retrieve the transaction again to confirm persistence.
- Compare manual and synced deletion, including omitted bulk-delete IDs and the single-delete error.
- Check category tax resets, tag addition, bulk attachment omission, and response fields with disposable records.
- Test manual-account response failures and inspect persisted state before retrying. Check attachment ownership, expiry, and download behavior separately.
These are acceptance checks to perform in an isolated environment. Source review has not established runtime completion; verification metadata remains pending.