Skip to content

Load one statement: an account, a period, an opening and closing balance, lines, and the document

POST
/v1/entities/{entity_id}/statements
curl --request POST \
--url https://example.com/v1/entities/example/statements \
--header 'Content-Type: application/json' \
--data '{ "account": "example", "commodity": "example", "period_start": "example", "period_end": "example", "opening": "-12.00", "closing": "-12.00", "lines": [ { "dt": "example", "description": "example", "amount": "-12.00", "currency": "example", "reference": "example", "raw": "example", "source_key": "example" } ], "document": { "sha256": "example", "filename": "example", "media_type": "example", "size_bytes": 1, "blob_path": "", "external_ref": "example" }, "locator": "example", "page": 1, "reason": "example", "id": "example" }'
entity_id
required
Entity Id

The entity this path is relative to

string

The entity this path is relative to

Media typeapplication/json
StatementSubmission

A statement: one account, one period, an opening and a closing balance, a list of lines, and the document.

There is no file format here. Haddon parses nothing: whatever the bank produced (CSV, XLSX, PDF, a pasted table) is read outside the ledger and submitted as normalised lines. The ledger enforces what a parser must not be trusted with, and refuses the whole submission if any of it fails:

  1. the lines must sum to closing minus opening;
  2. every line’s dt must fall inside period_start..period_end;
  3. the currency must be declared, on the statement and on every line, and they must agree;
  4. the same document cannot be loaded twice (by document.sha256).

opening is checked against the closing balance already recorded for the period ending the day before, and a disagreement is refused. When there is no such record the statement is still accepted, and the response says opening_verified: false with an opening_basis naming why. It is never assumed to be 0.

object
account
required
Account

Account path (preferred) or stable account id

string
>= 1 characters <= 255 characters
commodity
required
Commodity
string
>= 1 characters <= 64 characters
period_start
required
Period Start

First day of the statement period (YYYY-MM-DD)

string
period_end
required
Period End

Last day of the statement period; inclusive, and free to cross a year

string
opening
required
Opening

Signed decimal amount in whole commodity units, as a string; never a JSON number. Converted by the commodity’s declared precision, so more decimal places than the currency has is refused rather than rounded.

string
/^-?[0-9]+(\.[0-9]+)?$/
closing
required
Closing

Signed decimal amount in whole commodity units, as a string; never a JSON number. Converted by the commodity’s declared precision, so more decimal places than the currency has is refused rather than rounded.

string
/^-?[0-9]+(\.[0-9]+)?$/
lines
Lines
Array<object>
StatementLineSubmission

One normalised statement line.

currency is required and must equal the statement’s commodity: the ledger declares currency, it never infers it.

object
dt
required
Dt

The line’s value date (YYYY-MM-DD); must fall inside the declared period

string
description
required
Description
string
>= 1 characters <= 4096 characters
amount
required
Amount

Signed decimal amount in whole commodity units, as a string; never a JSON number. Converted by the commodity’s declared precision, so more decimal places than the currency has is refused rather than rounded.

string
/^-?[0-9]+(\.[0-9]+)?$/
currency
required
Currency
string
>= 1 characters <= 64 characters
reference
Any of:
string
<= 512 characters
raw
Any of:
string
<= 8192 characters
source_key
Any of:
string
<= 512 characters
document
required
StatementDocument

The original document, by reference. The ledger records this and does not fetch it.

sha256 is the hash of the bytes the agent read, and the key that makes a second load of the same statement a refusal. Give exactly one of blob_path (bytes this service holds) and external_ref (bytes held elsewhere).

object
sha256
required
Sha256

SHA-256 of the original document, 64 lowercase hex characters

string
>= 64 characters <= 64 characters /^[0-9a-f]{64}$/
filename
required
Filename
string
>= 1 characters <= 512 characters
media_type
required
Media Type
string
>= 1 characters <= 255 characters
size_bytes
required
Size Bytes

Size of the original document in bytes

integer
blob_path
Blob Path

Where the original document is kept

string
"" <= 2048 characters
external_ref
Any of:
string
<= 2048 characters
locator
required
Locator

Where in the document the closing balance is printed

string
>= 1 characters <= 512 characters
page
Any of:
integer
>= 1
reason
required
Reason

Why this statement is being loaded; kept in the record

string
>= 1 characters
id
Any of:
string

Successful Response

Media typeapplication/json
StatementWriteResponse

The reply to loading or replacing a statement: the statement, and the action that recorded it.

The read routes use StatementResponse, which has no audit_seq because a read has no action behind it. Hence a separate model rather than an optional field.

object
entity_id
required
Entity Id
string
legal_name
required
Legal Name
string
base_currency
required
Base Currency
string
snapshot_seq
required
Snapshot Seq
integer
statement
required
Statement
object
statement_id
required
Statement Id
string
account_id
required
Account Id
string
account_path
required
Account Path
string
commodity
required
Commodity
string
period_start
required
Period Start
string
period_end
required
Period End
string
opening_micros
required
Opening Micros

Signed integer micro-units (millionths) as a base-10 string; never a JSON number.

string
/^-?(0|[1-9][0-9]*)$/
closing_micros
required
Closing Micros

Signed integer micro-units (millionths) as a base-10 string; never a JSON number.

string
/^-?(0|[1-9][0-9]*)$/
opening_verified
required
Opening Verified

True only when the opening equals a recorded prior closing

boolean
opening_basis
required
Opening Basis

Which of the three opening cases this is

string
prior_statement_id
required
Any of:
string
line_count
required
Line Count
integer
lines_micros
required
Lines Micros

Signed integer micro-units (millionths) as a base-10 string; never a JSON number.

string
/^-?(0|[1-9][0-9]*)$/
document_id
required
Document Id
string
document
required
DocumentView

The original document as a reader sees it: what identifies it, and whether it can be opened here.

There is deliberately no blob_path (removed in issue 0007): a server-local path is no use to a reviewer and discloses where the service keeps files. storage says instead whether the bytes are available: stored means GET /documents/{id}/content will hand them over, external means only external_ref exists and the document is held elsewhere.

object
sha256
required
Sha256

SHA-256 of the registered bytes, and the document’s identity

string
filename
required
Filename
string
media_type
required
Media Type
string
size_bytes
required
Size Bytes
integer
storage
required
Storage

Stored | external

string
external_ref
required
Any of:
string
page
required
Any of:
integer
locator
required
Locator
string
reason
required
Reason
string
replaces_id
required
Any of:
string
replaced_by_id
required
Any of:
string
live
required
Live

Nothing replaces this statement

boolean
created_at
required
Created At
string
created_by
required
Created By
string
links
required
Links
object
key
additional properties
string
audit_seq
required
Audit Seq

The statement.loaded / statement.replaced action’s event sequence

integer

Example

{
"statement": {
"opening_micros": "-12000000",
"closing_micros": "-12000000",
"lines_micros": "-12000000"
}
}

Bad Request

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Unauthorized

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Forbidden

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Not Found

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Conflict

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Precondition Failed

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Unprocessable Content

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Internal Server Error

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}

Service Unavailable

Media typeapplication/json
ErrorEnvelope
object
error
required
ErrorBody
object
code
required
Code
string
message
required
Message
string
retryable
required
Retryable
boolean
details
required
Details
Array<object>
object
key
additional properties
any
request_id
Any of:
string
next_action
Any of:
object
key
additional properties
any

Example generated

{
"error": {
"code": "example",
"message": "example",
"retryable": true,
"details": [
{}
],
"request_id": "example",
"next_action": {}
}
}