coding.status:
- UNCATEGORIZED: no journal entry. Grain’s rules and AI may add a
suggestion. - CATEGORIZED: a draft journal entry exists. It does not affect reports until approved.
- APPROVED: the journal entry is posted to the ledger.
Look up coding IDs
Only dimension values with
isAssignable: true can be assigned. Parent values roll up for reporting. Dimensions with isRequired: true must be set before a transaction can be approved.
Create a transaction without duplicates
Send your system’s ID asexternalId and a stable Idempotency-Key header. Reuse the same key if a request times out.
- A retry with the same key and body returns
201with the original transaction. A different body returns409. - A second transaction with the same
externalIdreturns409, even with a new key. Find the existing one withGET /api/v1/transactions?externalId=card-txn-8812. amountis always positive.DEPOSITrecords money in;WITHDRAWAL,TRANSFER, andADJUSTMENTrecord money out. The response’sdirectionisINFLOWorOUTFLOW.accountIdis the ledger cash, bank, or card account the transaction clears through. Transactions from a connected feed usebankAccountIdinstead.
400.
Code or split a transaction
PUT /api/v1/transactions/{id}/coding replaces the coding. Line amounts must add up to the transaction amount.
- One line codes the transaction to a single account. Several lines split it across accounts, funds, or dimensions.
- A line’s fund falls back to
defaultFundId, then to the transaction’s fund, then to the organization’s default fund. - A line without
dimensionsinherits the transaction’s dimensions. - Positive amounts allocate the transaction. A negative amount offsets another line, for example a refund inside a split. Response
coding.linesuse the same convention. - Recoding is allowed until the transaction is approved. Unapprove first to change approved coding. Reconciled transactions cannot be recoded, and transactions in an open reconciliation return
409.
PUT replaces the coding, retrying it is safe.
DELETE /api/v1/transactions/{id}/coding removes the draft journal entry and returns the transaction to UNCATEGORIZED. An approved transaction returns 409; unapprove it first.
Set dimensions
PUT /api/v1/transactions/{id}/dimensions replaces the transaction’s dimensions. On a single-account transaction they are copied to the coded line. Split lines keep their own dimensions; set those in the coding request. Send {"dimensions": []} to clear them.
Approve
POST /api/v1/transactions/{id}/approve posts the journal entry. It needs the transactions:approve scope. With OAuth, the signed-in user must also be allowed to post journal entries.
- Approval fails if
coding.missingRequiredDimensionsis not empty (400) or the accounting period is locked. - Approving an approved transaction returns it unchanged, so retries are safe.
POST /unapproveworks the same way in reverse.
Receipts
Upload a PDF or image (JPEG, PNG, GIF, WebP, or HEIC) of up to 5 MB asmultipart/form-data:
GET /transactions/{id}/attachmentslists attachments, including receipts synced from card platforms.GET /transactions/{id}/attachments/{fileId}/downloadreturns a signed URL that expires in 15 minutes. Request a new one each time you need the file; don’t store it.DELETE /transactions/{id}/attachments/{fileId}removes an attachment.
Changes in API version 1.3.0
Transaction responses now use a documented, stable shape. Internal fields that earlier responses passed through, such as model reasoning, audit logs, and bank-feed metadata, are no longer returned. ThefundId and vendorId fields are replaced by fund and vendor summaries, and the new coding object carries the journal entry and its lines.