Authentication
Every request carries an API key as a bearer token:
curl https://api.flychain.us/external/v1/business_entities \ -H "Authorization: Bearer $FLYCHAIN_API_KEY"Always https://. A request to http:// is refused with 400 INSECURE_TRANSPORT
rather than redirected, because a redirect cannot un-send what was already transmitted: the
key travelled the network unencrypted, so it must be treated as compromised and
rotated. We refuse rather than redirect precisely so this is
impossible to miss.
There is no OAuth flow, no token refresh and no per-provider consent step, whichever kind of key you hold.
Two kinds of key
Section titled “Two kinds of key”A key is always exactly one of these. Which one you hold determines both what it can read and which endpoints it reaches.
A partner key
Section titled “A partner key”Issued to a platform that reads the books of the providers in its partner relationship.
- Scoped to your partner relationship. It reads only the providers in that relationship. A request for anything outside it is rejected — see Data semantics on how “not yours” is reported differently from “does not exist”.
- No per-provider authorization. Because you hold the credential rather than each
provider, a provider joining Flychain needs no new authorization step on your side or
theirs. They simply appear in
GET /business_entities. This is the main operational difference from pulling reports out of a provider’s own accounting system.
A provider key
Section titled “A provider key”Issued to a Flychain provider reading its own books. See Provider quickstart for the full walkthrough.
- Scoped to one provider — its own. A request naming any other provider or business entity is answered as though that id did not exist.
- It reads every business entity under that provider. Per-user entity restrictions configured for individual staff in the provider’s Flychain organization do not apply to it: an organization key has no user, so there is nothing to filter by. Those restrictions are an internal delegation control, not a boundary against the provider itself.
- Access follows the Flychain account. A provider whose account is not active, or
which is not enrolled in the API programme, is refused with a
403— see the failure table.
Shared by both
Section titled “Shared by both”- Read-only. Every endpoint on this API is a
GET. There is nothing here that can modify data on our side. - Not tied to a person. A key belongs to your organization, so it keeps working when the engineer who created it changes role.
Which endpoints each key reaches
Section titled “Which endpoints each key reaches”Fourteen operations. Six are open to both kinds of key; eight are provider-key only.
Report paths are shown relative to
/provider/{provider_id}/business_entity/{business_entity_id}/financial_reports.
Discovery paths are absolute.
| Endpoint | Partner key | Provider key |
|---|---|---|
/providers |
yes | yes |
/business_entities |
yes | yes |
income_statement |
yes | yes |
income_statement/summary |
yes | yes |
income_statement/monthly |
yes | yes |
income_statement/monthly/summary |
yes | yes |
balance_sheet |
no | yes |
balance_sheet/summary |
no | yes |
balance_sheet/monthly |
no | yes |
balance_sheet/monthly/summary |
no | yes |
cash_flow_report |
no | yes |
cash_flow_report/summary |
no | yes |
cash_flow_report/monthly |
no | yes |
cash_flow_report/monthly/summary |
no | yes |
A partner key on one of the eight gets 403 ENDPOINT_NOT_AVAILABLE. Your URL is not
wrong and your key is not broken — that operation is not served for your kind of key, and
rotating the credential will not change it.
The narrowing is deliberate rather than incidental. A cash flow report descends to
individual transactions and a balance sheet is an entity’s whole financial position;
neither is needed to source revenue figures, which is what the partner integration exists
to do. Versioning commits us to additive change within v1, so opening one
of these to partner keys later costs nothing, while closing an already-published one would
be impossible.
Issuing, rotating and revoking keys
Section titled “Issuing, rotating and revoking keys”Key management is self-serve, from your Flychain partner dashboard or provider dashboard depending on which kind of key you hold. Either way you can issue a key, set its expiry, rotate it and revoke it yourself, without a request to us. The mechanics below are identical for both.
When you create a key you give it a label and an expiry, and you must choose the expiry explicitly — there is no default. A key that never expires is a legitimate choice for a service integration; it should be a decision rather than something that happens by omission.
The key value is shown exactly once, at the moment it is created. Store it in your secret manager then. It cannot be retrieved afterwards — if it is lost, issue a new one and revoke the old one.
Rotating without downtime
Section titled “Rotating without downtime”You can hold more than one key at a time, which is what makes a clean rotation possible:
- Create the new key.
- Deploy it to every environment that calls this API.
- Confirm traffic is flowing on the new key.
- Revoke the old key.
There is no window in which neither key works, and no coordination with us. Do the same thing to retire a key you believe has been exposed — create, deploy, revoke — rather than revoking first and going dark in between.
Separate keys per environment
Section titled “Separate keys per environment”Hold a distinct key for each environment that calls us. A development key can then be revoked on its own without touching production, and the labels tell you which is which when you come back to the dashboard months later.
Handling authentication failures
Section titled “Handling authentication failures”Seven responses look similar and mean different things. Distinguish them, because retrying the wrong one wastes hours and giving up on another loses a day of data.
| Status | Code | Applies to | What it means | What to do |
|---|---|---|---|---|
400 |
INSECURE_TRANSPORT |
Both | You called http:// instead of https://, so the key was sent in the clear. |
Rotate the key, then fix the URL. Retrying over HTTPS works, but the old key is exposed. |
401 |
INVALID_API_KEY |
Both | The key is missing, invalid, expired or revoked. | Stop and fix the credential. Retrying will not help. |
403 |
PARTNER_API_NOT_ENABLED |
Partner keys | Your key is fine. Your organization is not enrolled in the API programme. | Contact us to be enrolled. Do not rotate the key — a new one behaves identically. |
403 |
PROVIDER_NOT_ACTIVE |
Provider keys | Your key is fine. The provider’s Flychain account is not active — churned, paused, or still onboarding. | Contact us. This is an account state, not an outage; see below. |
403 |
PROVIDER_API_NOT_ENABLED |
Provider keys | Your key is fine. The account is active but not enrolled in the API programme. | Contact us to be enrolled. Do not rotate the key. |
403 |
ENDPOINT_NOT_AVAILABLE |
Partner keys | Your key and your URL are both fine. That operation is provider-key only — see the table above. | Stop calling it. No credential change will open it. |
503 |
AUTH_SERVICE_UNAVAILABLE |
Both | We could not check your key. Our identity provider was unreachable or rate-limited. | Retry with backoff. Your key is probably fine. |
A 401 returns the same message whichever way the key failed — revoked, unknown, expired,
or valid but on an organization that is neither a partner nor a provider. That is
deliberate: the response cannot be used to learn which keys exist.
The four 403s are deliberately not folded into that 401. Every one of them means
the credential itself is valid, so answering “invalid key” would send you off to rebuild an
integration whose credential was never the problem.
PROVIDER_NOT_ACTIVE and PROVIDER_API_NOT_ENABLED are checked in that order, and the
order carries information: if the account is not live, telling a provider to request access
to the API programme would point them at the wrong problem entirely.
Both are read fresh on every request, with nothing cached. That means a change in either — an account moving out of active state, an enrollment being withdrawn — takes effect on the next call, not on a schedule. Existing keys are not revoked; they go inert while the condition holds and work again once it clears.
Testing
Section titled “Testing”There is no separate sandbox, deliberately. Every endpoint is a read-only GET, so there
is nothing an integration can do to affect our data, and testing against real books is
more informative than testing against synthetic ones. Build and test against live
credentials from the start.
Partners: we will nominate a specific entity for you to develop against and confirm it alongside your key.
Providers: you develop against your own books. GET /business_entities lists the
entities your key reaches, and reporting_available tells you which of them a report can
be produced for today — see Data semantics.