Full cash flow report per calendar month
const url = 'https://api.flychain.us/external/v1/provider/9c1e7a42-0b3d-4e58-9f21-6a8b5c4d3e2f/business_entity/3f5d8b16-7c94-42a1-b0e6-58d9c2a71b43/financial_reports/cash_flow_report/monthly?start_date=2026-07-01&end_date=2026-07-31';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.flychain.us/external/v1/provider/9c1e7a42-0b3d-4e58-9f21-6a8b5c4d3e2f/business_entity/3f5d8b16-7c94-42a1-b0e6-58d9c2a71b43/financial_reports/cash_flow_report/monthly?start_date=2026-07-01&end_date=2026-07-31' \ --header 'Authorization: Bearer <token>'Provider keys only. A partner key is refused with 403 ENDPOINT_NOT_AVAILABLE.
One full report per calendar month across the range, plus the whole period, in the
same envelope as monthly/summary.
The heaviest endpoint on this API in both directions. Every transaction in the
range is serialized once for its month and again under whole_period, so a
twelve-month request writes out the entity’s whole year twice; on our side it is one
call per month plus one for the whole period, and a wide range can approach our
30-second request timeout. There is no range limit, deliberately — but point a
recurring pull at monthly/summary instead, which returns five integers per period
rather than every transaction of every month, and reach for this variant when you
actually need the detail.
Months at the edge of the range are clipped, not extended: a range starting
mid-month yields a first period that starts on your start_date. Note what that
means for a cash flow report specifically — a clipped period’s
starting_cash_balance_cents is the balance on your start_date, not on the first
of the month.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The provider, from GET /providers or the provider_id on a business-entity
record. With a partner key it must be a provider in your partner relationship;
with a provider key it is always your own provider_id, which GET /providers
returns.
Example
9c1e7a42-0b3d-4e58-9f21-6a8b5c4d3e2fThe business entity, from GET /business_entities. Must belong to the
provider_id in the same path.
Example
3f5d8b16-7c94-42a1-b0e6-58d9c2a71b43Query Parameters
Section titled “Query Parameters”First day of the requested range, inclusive.
Must be a real calendar date, zero-padded, in YYYY-MM-DD form — 2026-7-1 and
2026-02-31 are both 400 INVALID_REQUEST. Ranges are evaluated in UTC.
Raised to the entity’s books_start_date when it falls earlier. If the whole
requested range sits before the books begin, the result is
400 INVALID_REQUEST rather than a zero statement, so a range with no books
behind it can never read as a period with no revenue.
Example
2026-07-01Last day of the requested range, inclusive. Same format rules as start_date, and
must not be earlier than it.
Capped at today when it falls in the future, which is the ordinary case for a job that asks for the current month every day. The response echoes the effective range it used.
Example
2026-07-31Responses
Section titled “Responses”One full report per period, plus the whole period.
A full report per period, plus the whole range.
object
The basis an entity’s financials are prepared on.
A property of the entity, not a request parameter. A report is produced on the
basis the underlying books are kept on; there is no per-request switch. It is
returned on every entity record and every report so a figure is never ambiguous,
and it will not change without notice. See guides/data-semantics.
null only where the entity has no books yet — every entity with
reporting_available: true carries a basis.
When this payload was produced, ISO 8601 UTC.
Chronological, one entry per month in the effective range.
A period carrying the full report.
object
Month and year of this period, e.g. July 2026. Derived from start_date, so
a clipped period still reads as its calendar month — use the dates, not the
label, to know what the period covers.
First day of the period, inclusive.
Last day of the period, inclusive.
Whether a Flychain bookkeeper has finalized the books through end_date.
This is the flag that says a figure is final.
Whether this period’s end_date is in the past. false for the in-progress
current month.
Note this is about the period, not the calendar month: a period clipped by
the requested range reads true once its end date has passed, even though the
calendar month it is labelled with is not fully covered.
The full report: every cash ledger with its transactions, plus the same five
figures the summary variant returns on their own.
This is the identical structure our own application consumes. Nothing is held back and nothing is added for external callers — a pared-down copy would be a second definition of the report, and a second definition drifts.
object
One entry per cash account, ordered by total_net_cash_flow_cents descending.
Each carries its transactions in children.
A flat list, not a tree. Where one account rolls up into another, both appear
here as their own entries — which is why total_net_cash_flow_cents must not be
summed across it. The report totals below are built from each account’s
total_inflow_cents / total_outflow_cents, so those are what reconcile.
A node in the full cash flow report: either a ledger (a cash account) or a
transaction beneath one. Recursive through children, though in practice the
report is two levels deep.
Written out separately from Record rather than extending it, because the two
answer “where is the total” differently and conflating them is the single easiest
way to read a zero here:
| Ledger | Transaction | |
|---|---|---|
| Read one node | total_net_cash_flow_cents |
amount_cents |
| Sum across nodes | total_inflow_cents / total_outflow_cents |
amount_cents |
total_amount_cents |
0, always |
the same amount |
ledger_id |
present | absent |
line_id / datetime |
absent | present |
The two ledger rows differ because ledgers is a flat list: where one account
rolls up into another, both are entries in it, so the rolled-up figure double-counts
under a sum. See the two fields below.
Null fields are omitted rather than sent as null, exactly as on Record, so
treat an absent key as “not applicable to this node”.
object
Stable identifier for a cash account, so the same account can be tracked across periods. Present on ledger nodes, absent on transactions.
Opaque — not a UUID, unlike the provider and business-entity ids. Treat it as a string and do not parse it.
Identifier for a single journal-entry line as we hold it. Present on transaction nodes only, and opaque — treat it as a string and do not parse it.
Not promised to be stable across pulls. If you need to reconcile two pulls of the same period, match on amount, date and description rather than on this; that is what we do internally.
Display name of the account, or the transaction’s description as it reaches us
from the bank or the journal entry. No Description Available where neither
carries one.
When the transaction posted. Present on transaction nodes only. A timestamp
rather than a date, and passed through from the ledger as we hold it — use the
period’s start_date / end_date to know which period a transaction belongs
to, not this.
On a transaction, the signed amount that moved: positive in, negative out.
On a ledger, always 0 — read total_net_cash_flow_cents instead.
On a transaction, the same value as amount_cents. On a ledger, always
0. This is the field to read on an income statement or balance sheet and the
wrong one here; it is published because the report carries it, not because it
is useful.
Net movement through this account alone over the range, excluding anything
that rolls up into it. Present on ledger nodes, and the counterpart to
total_net_cash_flow_cents below.
To reconcile against the report totals, add up total_inflow_cents and
total_outflow_cents rather than this — those are the per-account figures the
report totals are summed from, so they agree exactly.
Net movement through this account and every account that rolls up into it.
Present on ledger nodes, and equal to net_cash_flow_cents for any account with
nothing beneath it.
This is the figure to read for one account, and the one this list is ordered
by. Do not sum it across ledgers — the accounts beneath a parent are
entries in that same list, so a sum counts them twice. Aggregate
total_inflow_cents / total_outflow_cents instead.
Cash in through this account’s own transactions over the range. Positive. Present on ledger nodes.
This and total_outflow_cents are the fields to aggregate. The report’s own
total_inflow_cents / total_outflow_cents are these values added up, and its
net_cash_flow_cents is their sum, so the reconciliation is exact rather than
merely expected.
Cash out through this account’s own transactions over the range. Negative,
or 0 where nothing left it. Present on ledger nodes, and the other half of the
pair to aggregate.
Cash in over the range. Positive.
Cash out over the range, as a negative number (0 if none).
Cash on hand at the start of the effective range.
Cash on hand at the end of the effective range.
total_inflow_cents + total_outflow_cents.
The whole range carrying the full report.
object
First day of the effective range, inclusive.
Last day of the effective range, inclusive.
Whether the books are finalized through end_date.
The full report: every cash ledger with its transactions, plus the same five
figures the summary variant returns on their own.
This is the identical structure our own application consumes. Nothing is held back and nothing is added for external callers — a pared-down copy would be a second definition of the report, and a second definition drifts.
object
One entry per cash account, ordered by total_net_cash_flow_cents descending.
Each carries its transactions in children.
A flat list, not a tree. Where one account rolls up into another, both appear
here as their own entries — which is why total_net_cash_flow_cents must not be
summed across it. The report totals below are built from each account’s
total_inflow_cents / total_outflow_cents, so those are what reconcile.
A node in the full cash flow report: either a ledger (a cash account) or a
transaction beneath one. Recursive through children, though in practice the
report is two levels deep.
Written out separately from Record rather than extending it, because the two
answer “where is the total” differently and conflating them is the single easiest
way to read a zero here:
| Ledger | Transaction | |
|---|---|---|
| Read one node | total_net_cash_flow_cents |
amount_cents |
| Sum across nodes | total_inflow_cents / total_outflow_cents |
amount_cents |
total_amount_cents |
0, always |
the same amount |
ledger_id |
present | absent |
line_id / datetime |
absent | present |
The two ledger rows differ because ledgers is a flat list: where one account
rolls up into another, both are entries in it, so the rolled-up figure double-counts
under a sum. See the two fields below.
Null fields are omitted rather than sent as null, exactly as on Record, so
treat an absent key as “not applicable to this node”.
object
Stable identifier for a cash account, so the same account can be tracked across periods. Present on ledger nodes, absent on transactions.
Opaque — not a UUID, unlike the provider and business-entity ids. Treat it as a string and do not parse it.
Identifier for a single journal-entry line as we hold it. Present on transaction nodes only, and opaque — treat it as a string and do not parse it.
Not promised to be stable across pulls. If you need to reconcile two pulls of the same period, match on amount, date and description rather than on this; that is what we do internally.
Display name of the account, or the transaction’s description as it reaches us
from the bank or the journal entry. No Description Available where neither
carries one.
When the transaction posted. Present on transaction nodes only. A timestamp
rather than a date, and passed through from the ledger as we hold it — use the
period’s start_date / end_date to know which period a transaction belongs
to, not this.
On a transaction, the signed amount that moved: positive in, negative out.
On a ledger, always 0 — read total_net_cash_flow_cents instead.
On a transaction, the same value as amount_cents. On a ledger, always
0. This is the field to read on an income statement or balance sheet and the
wrong one here; it is published because the report carries it, not because it
is useful.
Net movement through this account alone over the range, excluding anything
that rolls up into it. Present on ledger nodes, and the counterpart to
total_net_cash_flow_cents below.
To reconcile against the report totals, add up total_inflow_cents and
total_outflow_cents rather than this — those are the per-account figures the
report totals are summed from, so they agree exactly.
Net movement through this account and every account that rolls up into it.
Present on ledger nodes, and equal to net_cash_flow_cents for any account with
nothing beneath it.
This is the figure to read for one account, and the one this list is ordered
by. Do not sum it across ledgers — the accounts beneath a parent are
entries in that same list, so a sum counts them twice. Aggregate
total_inflow_cents / total_outflow_cents instead.
Cash in through this account’s own transactions over the range. Positive. Present on ledger nodes.
This and total_outflow_cents are the fields to aggregate. The report’s own
total_inflow_cents / total_outflow_cents are these values added up, and its
net_cash_flow_cents is their sum, so the reconciliation is exact rather than
merely expected.
Cash out through this account’s own transactions over the range. Negative,
or 0 where nothing left it. Present on ledger nodes, and the other half of the
pair to aggregate.
Cash in over the range. Positive.
Cash out over the range, as a negative number (0 if none).
Cash on hand at the start of the effective range.
Cash on hand at the end of the effective range.
total_inflow_cents + total_outflow_cents.
Examples
One month of full reports, plus the whole range
GET .../financial_reports/cash_flow_report/monthly?start_date=2026-07-01&end_date=2026-07-31
A single-month range, so whole_period covers the same window as the one period and
carries the same figures. Ask for a wider range and each month gets its own full
report — every transaction serialized once for its month and again under
whole_period, which is what makes this the heaviest endpoint on the API.
A single-account entity, so ledgers has one entry and the report totals are that
account’s. Nothing here is abridged — an entity with several accounts carries one
entry each, as in FullCashFlowRangeResponse.
{ "provider_id": "9c1e7a42-0b3d-4e58-9f21-6a8b5c4d3e2f", "business_entity_id": "3f5d8b16-7c94-42a1-b0e6-58d9c2a71b43", "accounting_basis": "CASH", "currency": "USD", "generated_at": "2026-08-20T14:02:11Z", "periods": [ { "label": "July 2026", "start_date": "2026-07-01", "end_date": "2026-07-31", "is_closed": false, "is_complete": true, "cash_flow_report": { "ledgers": [ { "ledger_id": "qBn4LxKe7ZaW2mtRcH9dY", "name": "Savings", "amount_cents": 0, "total_amount_cents": 0, "children": [ { "line_id": "ln_6Wq1sB4xTg", "name": "Interest earned", "amount_cents": 12500, "total_amount_cents": 12500, "children": [], "datetime": "2026-07-31T00:00:00Z" } ], "net_cash_flow_cents": 12500, "total_net_cash_flow_cents": 12500, "total_inflow_cents": 12500, "total_outflow_cents": 0 } ], "total_inflow_cents": 12500, "total_outflow_cents": 0, "starting_cash_balance_cents": 1250000, "ending_cash_balance_cents": 1262500, "net_cash_flow_cents": 12500 } } ], "whole_period": { "start_date": "2026-07-01", "end_date": "2026-07-31", "is_closed": false, "cash_flow_report": { "ledgers": [ { "ledger_id": "qBn4LxKe7ZaW2mtRcH9dY", "name": "Savings", "amount_cents": 0, "total_amount_cents": 0, "children": [ { "line_id": "ln_6Wq1sB4xTg", "name": "Interest earned", "amount_cents": 12500, "total_amount_cents": 12500, "children": [], "datetime": "2026-07-31T00:00:00Z" } ], "net_cash_flow_cents": 12500, "total_net_cash_flow_cents": 12500, "total_inflow_cents": 12500, "total_outflow_cents": 0 } ], "total_inflow_cents": 12500, "total_outflow_cents": 0, "starting_cash_balance_cents": 1250000, "ending_cash_balance_cents": 1262500, "net_cash_flow_cents": 12500 } }}Malformed request — a missing or unparseable date, a path id that is not a UUID,
start_date after end_date, or a range that does not overlap the period this
entity has books for. Also returned when the request reached us over plaintext
HTTP; see INSECURE_TRANSPORT below and guides/authentication.
The failure shape for every error — validation, authorization, ours — so a client needs a single error path.
Match on code, not on message: the code set below is the contract and is
stable, while wording may be clarified. New codes may be added within v1 (see
guides/versioning), so treat an unrecognised code as “the HTTP status is
authoritative”.
This includes a request that never reaches a documented operation at all — an
unrouted path or a method we do not serve on that path, which is the failure you
are most likely to meet while integrating. Those are answered before any operation
runs, so no operation below lists them, but they arrive in this same shape, as
ENDPOINT_NOT_FOUND (404) and METHOD_NOT_ALLOWED (405). Either one means
check the URL rather than your credentials: the operations below are the whole
surface.
object
object
Machine-readable cause.
INVALID_REQUEST(400) — malformed dates, a non-UUID path id, an inverted range, or a range with no reportable books behind it.INSECURE_TRANSPORT(400) — the request was sent over plaintexthttp, so the key crossed the network in the clear. Rotate the key, then fix the URL; we refuse rather than redirect so this cannot pass unnoticed.INVALID_API_KEY(401) — missing, invalid, expired or revoked key.PARTNER_API_NOT_ENABLED(403) — your key is valid, but your organization is not enrolled in the API programme. Contact us; do not rotate the key.PROVIDER_NOT_ACTIVE(403) — a provider key whose Flychain account is not active. Contact us; do not rotate the key.PROVIDER_API_NOT_ENABLED(403) — a provider key on an account that is not enrolled in the API programme. Contact us; do not rotate the key.ENDPOINT_NOT_AVAILABLE(403) — the path exists and your key is valid, but that operation is not served for your kind of key. The balance sheet and cash flow families are provider-only. Your URL is not wrong; do not rotate the key.PROVIDER_NOT_IN_PARTNER_SCOPE(403) — the provider exists but is not in your relationship, including one that has left it.BUSINESS_ENTITY_NOT_IN_PARTNER_SCOPE(403) — the entity exists but does not belong to theprovider_idin the path.PROVIDER_NOT_FOUND(404) — no provider with that id.BUSINESS_ENTITY_NOT_FOUND(404) — no business entity with that id.ENDPOINT_NOT_FOUND(404) — the URL itself is not one we serve, as opposed to a record we do not have. Check the path against the operations below.METHOD_NOT_ALLOWED(405) — the path exists but not with that method; theAllowresponse header lists the ones it takes. Every operation here is aGET.BOOKS_NOT_AVAILABLE(409) — the entity has no reportable books.INTERNAL_ERROR(500) — ours; retry with backoff.AUTH_SERVICE_UNAVAILABLE(503) — we could not verify your key; retry with backoff, and do not treat it as an authentication failure.
Human-readable detail. Do not match on it.
Examples
Unparseable date
{ "error": { "code": "INVALID_REQUEST", "message": "start_date must be a valid date in YYYY-MM-DD format." }}Path id is not a UUID
{ "error": { "code": "INVALID_REQUEST", "message": "business_entity_id is not a valid UUID." }}Range sits entirely outside the reportable period
{ "error": { "code": "INVALID_REQUEST", "message": "The requested range does not overlap the period this entity has reportable books for. It must fall on or after books_start_date (see GET /business_entities) and must not be entirely in the future." }}Request sent over plaintext HTTP
{ "error": { "code": "INSECURE_TRANSPORT", "message": "This request was sent over plaintext HTTP, so any API key it carried was transmitted in the clear. Reissue it over HTTPS, and treat the key as compromised: rotate it." }}Missing, invalid, expired or revoked API key. One message covers every case on purpose — a caller cannot tell a revoked key from an unknown one.
The failure shape for every error — validation, authorization, ours — so a client needs a single error path.
Match on code, not on message: the code set below is the contract and is
stable, while wording may be clarified. New codes may be added within v1 (see
guides/versioning), so treat an unrecognised code as “the HTTP status is
authoritative”.
This includes a request that never reaches a documented operation at all — an
unrouted path or a method we do not serve on that path, which is the failure you
are most likely to meet while integrating. Those are answered before any operation
runs, so no operation below lists them, but they arrive in this same shape, as
ENDPOINT_NOT_FOUND (404) and METHOD_NOT_ALLOWED (405). Either one means
check the URL rather than your credentials: the operations below are the whole
surface.
object
object
Machine-readable cause.
INVALID_REQUEST(400) — malformed dates, a non-UUID path id, an inverted range, or a range with no reportable books behind it.INSECURE_TRANSPORT(400) — the request was sent over plaintexthttp, so the key crossed the network in the clear. Rotate the key, then fix the URL; we refuse rather than redirect so this cannot pass unnoticed.INVALID_API_KEY(401) — missing, invalid, expired or revoked key.PARTNER_API_NOT_ENABLED(403) — your key is valid, but your organization is not enrolled in the API programme. Contact us; do not rotate the key.PROVIDER_NOT_ACTIVE(403) — a provider key whose Flychain account is not active. Contact us; do not rotate the key.PROVIDER_API_NOT_ENABLED(403) — a provider key on an account that is not enrolled in the API programme. Contact us; do not rotate the key.ENDPOINT_NOT_AVAILABLE(403) — the path exists and your key is valid, but that operation is not served for your kind of key. The balance sheet and cash flow families are provider-only. Your URL is not wrong; do not rotate the key.PROVIDER_NOT_IN_PARTNER_SCOPE(403) — the provider exists but is not in your relationship, including one that has left it.BUSINESS_ENTITY_NOT_IN_PARTNER_SCOPE(403) — the entity exists but does not belong to theprovider_idin the path.PROVIDER_NOT_FOUND(404) — no provider with that id.BUSINESS_ENTITY_NOT_FOUND(404) — no business entity with that id.ENDPOINT_NOT_FOUND(404) — the URL itself is not one we serve, as opposed to a record we do not have. Check the path against the operations below.METHOD_NOT_ALLOWED(405) — the path exists but not with that method; theAllowresponse header lists the ones it takes. Every operation here is aGET.BOOKS_NOT_AVAILABLE(409) — the entity has no reportable books.INTERNAL_ERROR(500) — ours; retry with backoff.AUTH_SERVICE_UNAVAILABLE(503) — we could not verify your key; retry with backoff, and do not treat it as an authentication failure.
Human-readable detail. Do not match on it.
Examples
{ "error": { "code": "INVALID_API_KEY", "message": "Missing, invalid, expired or revoked API key." }}Your key is valid, but this call is not allowed. Either the operation is not served for your kind of key — the balance sheet and cash flow families are provider-only, eight operations in all — or your provider’s Flychain account is not active or not enrolled in the API programme. None of these is a credential problem; rotating the key will not help.
The partner scope refusals do not appear here: a partner key cannot reach these operations at all.
The failure shape for every error — validation, authorization, ours — so a client needs a single error path.
Match on code, not on message: the code set below is the contract and is
stable, while wording may be clarified. New codes may be added within v1 (see
guides/versioning), so treat an unrecognised code as “the HTTP status is
authoritative”.
This includes a request that never reaches a documented operation at all — an
unrouted path or a method we do not serve on that path, which is the failure you
are most likely to meet while integrating. Those are answered before any operation
runs, so no operation below lists them, but they arrive in this same shape, as
ENDPOINT_NOT_FOUND (404) and METHOD_NOT_ALLOWED (405). Either one means
check the URL rather than your credentials: the operations below are the whole
surface.
object
object
Machine-readable cause.
INVALID_REQUEST(400) — malformed dates, a non-UUID path id, an inverted range, or a range with no reportable books behind it.INSECURE_TRANSPORT(400) — the request was sent over plaintexthttp, so the key crossed the network in the clear. Rotate the key, then fix the URL; we refuse rather than redirect so this cannot pass unnoticed.INVALID_API_KEY(401) — missing, invalid, expired or revoked key.PARTNER_API_NOT_ENABLED(403) — your key is valid, but your organization is not enrolled in the API programme. Contact us; do not rotate the key.PROVIDER_NOT_ACTIVE(403) — a provider key whose Flychain account is not active. Contact us; do not rotate the key.PROVIDER_API_NOT_ENABLED(403) — a provider key on an account that is not enrolled in the API programme. Contact us; do not rotate the key.ENDPOINT_NOT_AVAILABLE(403) — the path exists and your key is valid, but that operation is not served for your kind of key. The balance sheet and cash flow families are provider-only. Your URL is not wrong; do not rotate the key.PROVIDER_NOT_IN_PARTNER_SCOPE(403) — the provider exists but is not in your relationship, including one that has left it.BUSINESS_ENTITY_NOT_IN_PARTNER_SCOPE(403) — the entity exists but does not belong to theprovider_idin the path.PROVIDER_NOT_FOUND(404) — no provider with that id.BUSINESS_ENTITY_NOT_FOUND(404) — no business entity with that id.ENDPOINT_NOT_FOUND(404) — the URL itself is not one we serve, as opposed to a record we do not have. Check the path against the operations below.METHOD_NOT_ALLOWED(405) — the path exists but not with that method; theAllowresponse header lists the ones it takes. Every operation here is aGET.BOOKS_NOT_AVAILABLE(409) — the entity has no reportable books.INTERNAL_ERROR(500) — ours; retry with backoff.AUTH_SERVICE_UNAVAILABLE(503) — we could not verify your key; retry with backoff, and do not treat it as an authentication failure.
Human-readable detail. Do not match on it.
Examples
A partner key on a provider-only operation
{ "error": { "code": "ENDPOINT_NOT_AVAILABLE", "message": "This endpoint is available to provider API keys only. See the reference at https://docs.flychain.us for the endpoints a partner key can read." }}Provider key whose Flychain account is not active
{ "error": { "code": "PROVIDER_NOT_ACTIVE", "message": "This provider's Flychain account is not active, so the API is not available to it. Contact Flychain." }}Provider key on an account not enrolled in the API programme
{ "error": { "code": "PROVIDER_API_NOT_ENABLED", "message": "This provider is not enrolled in the Flychain external API programme. Contact Flychain to request access." }}No provider or business entity exists with that id.
The failure shape for every error — validation, authorization, ours — so a client needs a single error path.
Match on code, not on message: the code set below is the contract and is
stable, while wording may be clarified. New codes may be added within v1 (see
guides/versioning), so treat an unrecognised code as “the HTTP status is
authoritative”.
This includes a request that never reaches a documented operation at all — an
unrouted path or a method we do not serve on that path, which is the failure you
are most likely to meet while integrating. Those are answered before any operation
runs, so no operation below lists them, but they arrive in this same shape, as
ENDPOINT_NOT_FOUND (404) and METHOD_NOT_ALLOWED (405). Either one means
check the URL rather than your credentials: the operations below are the whole
surface.
object
object
Machine-readable cause.
INVALID_REQUEST(400) — malformed dates, a non-UUID path id, an inverted range, or a range with no reportable books behind it.INSECURE_TRANSPORT(400) — the request was sent over plaintexthttp, so the key crossed the network in the clear. Rotate the key, then fix the URL; we refuse rather than redirect so this cannot pass unnoticed.INVALID_API_KEY(401) — missing, invalid, expired or revoked key.PARTNER_API_NOT_ENABLED(403) — your key is valid, but your organization is not enrolled in the API programme. Contact us; do not rotate the key.PROVIDER_NOT_ACTIVE(403) — a provider key whose Flychain account is not active. Contact us; do not rotate the key.PROVIDER_API_NOT_ENABLED(403) — a provider key on an account that is not enrolled in the API programme. Contact us; do not rotate the key.ENDPOINT_NOT_AVAILABLE(403) — the path exists and your key is valid, but that operation is not served for your kind of key. The balance sheet and cash flow families are provider-only. Your URL is not wrong; do not rotate the key.PROVIDER_NOT_IN_PARTNER_SCOPE(403) — the provider exists but is not in your relationship, including one that has left it.BUSINESS_ENTITY_NOT_IN_PARTNER_SCOPE(403) — the entity exists but does not belong to theprovider_idin the path.PROVIDER_NOT_FOUND(404) — no provider with that id.BUSINESS_ENTITY_NOT_FOUND(404) — no business entity with that id.ENDPOINT_NOT_FOUND(404) — the URL itself is not one we serve, as opposed to a record we do not have. Check the path against the operations below.METHOD_NOT_ALLOWED(405) — the path exists but not with that method; theAllowresponse header lists the ones it takes. Every operation here is aGET.BOOKS_NOT_AVAILABLE(409) — the entity has no reportable books.INTERNAL_ERROR(500) — ours; retry with backoff.AUTH_SERVICE_UNAVAILABLE(503) — we could not verify your key; retry with backoff, and do not treat it as an authentication failure.
Human-readable detail. Do not match on it.
Examples
{ "error": { "code": "PROVIDER_NOT_FOUND", "message": "No provider exists with that id." }}{ "error": { "code": "BUSINESS_ENTITY_NOT_FOUND", "message": "No business entity exists with that id." }}The entity exists and is in your relationship, but has no books a report can be
produced from — it is still onboarding, or it has been deactivated. It appears in
GET /business_entities with reporting_available: false; skip it rather than
recording a zero.
The failure shape for every error — validation, authorization, ours — so a client needs a single error path.
Match on code, not on message: the code set below is the contract and is
stable, while wording may be clarified. New codes may be added within v1 (see
guides/versioning), so treat an unrecognised code as “the HTTP status is
authoritative”.
This includes a request that never reaches a documented operation at all — an
unrouted path or a method we do not serve on that path, which is the failure you
are most likely to meet while integrating. Those are answered before any operation
runs, so no operation below lists them, but they arrive in this same shape, as
ENDPOINT_NOT_FOUND (404) and METHOD_NOT_ALLOWED (405). Either one means
check the URL rather than your credentials: the operations below are the whole
surface.
object
object
Machine-readable cause.
INVALID_REQUEST(400) — malformed dates, a non-UUID path id, an inverted range, or a range with no reportable books behind it.INSECURE_TRANSPORT(400) — the request was sent over plaintexthttp, so the key crossed the network in the clear. Rotate the key, then fix the URL; we refuse rather than redirect so this cannot pass unnoticed.INVALID_API_KEY(401) — missing, invalid, expired or revoked key.PARTNER_API_NOT_ENABLED(403) — your key is valid, but your organization is not enrolled in the API programme. Contact us; do not rotate the key.PROVIDER_NOT_ACTIVE(403) — a provider key whose Flychain account is not active. Contact us; do not rotate the key.PROVIDER_API_NOT_ENABLED(403) — a provider key on an account that is not enrolled in the API programme. Contact us; do not rotate the key.ENDPOINT_NOT_AVAILABLE(403) — the path exists and your key is valid, but that operation is not served for your kind of key. The balance sheet and cash flow families are provider-only. Your URL is not wrong; do not rotate the key.PROVIDER_NOT_IN_PARTNER_SCOPE(403) — the provider exists but is not in your relationship, including one that has left it.BUSINESS_ENTITY_NOT_IN_PARTNER_SCOPE(403) — the entity exists but does not belong to theprovider_idin the path.PROVIDER_NOT_FOUND(404) — no provider with that id.BUSINESS_ENTITY_NOT_FOUND(404) — no business entity with that id.ENDPOINT_NOT_FOUND(404) — the URL itself is not one we serve, as opposed to a record we do not have. Check the path against the operations below.METHOD_NOT_ALLOWED(405) — the path exists but not with that method; theAllowresponse header lists the ones it takes. Every operation here is aGET.BOOKS_NOT_AVAILABLE(409) — the entity has no reportable books.INTERNAL_ERROR(500) — ours; retry with backoff.AUTH_SERVICE_UNAVAILABLE(503) — we could not verify your key; retry with backoff, and do not treat it as an authentication failure.
Human-readable detail. Do not match on it.
Examples
{ "error": { "code": "BOOKS_NOT_AVAILABLE", "message": "This business entity does not currently have reportable books. GET /business_entities lists it with reporting_available: false." }}Our side. Safe to retry with backoff.
The failure shape for every error — validation, authorization, ours — so a client needs a single error path.
Match on code, not on message: the code set below is the contract and is
stable, while wording may be clarified. New codes may be added within v1 (see
guides/versioning), so treat an unrecognised code as “the HTTP status is
authoritative”.
This includes a request that never reaches a documented operation at all — an
unrouted path or a method we do not serve on that path, which is the failure you
are most likely to meet while integrating. Those are answered before any operation
runs, so no operation below lists them, but they arrive in this same shape, as
ENDPOINT_NOT_FOUND (404) and METHOD_NOT_ALLOWED (405). Either one means
check the URL rather than your credentials: the operations below are the whole
surface.
object
object
Machine-readable cause.
INVALID_REQUEST(400) — malformed dates, a non-UUID path id, an inverted range, or a range with no reportable books behind it.INSECURE_TRANSPORT(400) — the request was sent over plaintexthttp, so the key crossed the network in the clear. Rotate the key, then fix the URL; we refuse rather than redirect so this cannot pass unnoticed.INVALID_API_KEY(401) — missing, invalid, expired or revoked key.PARTNER_API_NOT_ENABLED(403) — your key is valid, but your organization is not enrolled in the API programme. Contact us; do not rotate the key.PROVIDER_NOT_ACTIVE(403) — a provider key whose Flychain account is not active. Contact us; do not rotate the key.PROVIDER_API_NOT_ENABLED(403) — a provider key on an account that is not enrolled in the API programme. Contact us; do not rotate the key.ENDPOINT_NOT_AVAILABLE(403) — the path exists and your key is valid, but that operation is not served for your kind of key. The balance sheet and cash flow families are provider-only. Your URL is not wrong; do not rotate the key.PROVIDER_NOT_IN_PARTNER_SCOPE(403) — the provider exists but is not in your relationship, including one that has left it.BUSINESS_ENTITY_NOT_IN_PARTNER_SCOPE(403) — the entity exists but does not belong to theprovider_idin the path.PROVIDER_NOT_FOUND(404) — no provider with that id.BUSINESS_ENTITY_NOT_FOUND(404) — no business entity with that id.ENDPOINT_NOT_FOUND(404) — the URL itself is not one we serve, as opposed to a record we do not have. Check the path against the operations below.METHOD_NOT_ALLOWED(405) — the path exists but not with that method; theAllowresponse header lists the ones it takes. Every operation here is aGET.BOOKS_NOT_AVAILABLE(409) — the entity has no reportable books.INTERNAL_ERROR(500) — ours; retry with backoff.AUTH_SERVICE_UNAVAILABLE(503) — we could not verify your key; retry with backoff, and do not treat it as an authentication failure.
Human-readable detail. Do not match on it.
Examples
{ "error": { "code": "INTERNAL_ERROR", "message": "An unexpected error occurred. Retry with backoff." }}We could not verify your API key right now — our identity provider was
unreachable or rate-limited. This is not an authentication failure: the key
may well be valid. Retry with backoff rather than treating it as a 401.
The failure shape for every error — validation, authorization, ours — so a client needs a single error path.
Match on code, not on message: the code set below is the contract and is
stable, while wording may be clarified. New codes may be added within v1 (see
guides/versioning), so treat an unrecognised code as “the HTTP status is
authoritative”.
This includes a request that never reaches a documented operation at all — an
unrouted path or a method we do not serve on that path, which is the failure you
are most likely to meet while integrating. Those are answered before any operation
runs, so no operation below lists them, but they arrive in this same shape, as
ENDPOINT_NOT_FOUND (404) and METHOD_NOT_ALLOWED (405). Either one means
check the URL rather than your credentials: the operations below are the whole
surface.
object
object
Machine-readable cause.
INVALID_REQUEST(400) — malformed dates, a non-UUID path id, an inverted range, or a range with no reportable books behind it.INSECURE_TRANSPORT(400) — the request was sent over plaintexthttp, so the key crossed the network in the clear. Rotate the key, then fix the URL; we refuse rather than redirect so this cannot pass unnoticed.INVALID_API_KEY(401) — missing, invalid, expired or revoked key.PARTNER_API_NOT_ENABLED(403) — your key is valid, but your organization is not enrolled in the API programme. Contact us; do not rotate the key.PROVIDER_NOT_ACTIVE(403) — a provider key whose Flychain account is not active. Contact us; do not rotate the key.PROVIDER_API_NOT_ENABLED(403) — a provider key on an account that is not enrolled in the API programme. Contact us; do not rotate the key.ENDPOINT_NOT_AVAILABLE(403) — the path exists and your key is valid, but that operation is not served for your kind of key. The balance sheet and cash flow families are provider-only. Your URL is not wrong; do not rotate the key.PROVIDER_NOT_IN_PARTNER_SCOPE(403) — the provider exists but is not in your relationship, including one that has left it.BUSINESS_ENTITY_NOT_IN_PARTNER_SCOPE(403) — the entity exists but does not belong to theprovider_idin the path.PROVIDER_NOT_FOUND(404) — no provider with that id.BUSINESS_ENTITY_NOT_FOUND(404) — no business entity with that id.ENDPOINT_NOT_FOUND(404) — the URL itself is not one we serve, as opposed to a record we do not have. Check the path against the operations below.METHOD_NOT_ALLOWED(405) — the path exists but not with that method; theAllowresponse header lists the ones it takes. Every operation here is aGET.BOOKS_NOT_AVAILABLE(409) — the entity has no reportable books.INTERNAL_ERROR(500) — ours; retry with backoff.AUTH_SERVICE_UNAVAILABLE(503) — we could not verify your key; retry with backoff, and do not treat it as an authentication failure.
Human-readable detail. Do not match on it.
Examples
{ "error": { "code": "AUTH_SERVICE_UNAVAILABLE", "message": "Unable to verify the API key right now. Retry with backoff." }}