Docs
Versioning
Two version lines that move independently — the protocol's formats, and an implementation's HTTP surface — and which one to pin.
Two lines, on purpose
| Looks like | Changes when | Header | |
|---|---|---|---|
| Protocol version | 0.1.0 | A format changes — a field, a state, a signed payload | Path-Protocol-Version |
| API version | 2026-09-09.genesis | An implementation's HTTP surface changes | Path-Version |
A protocol change almost always forces an API change. The reverse is not true: renaming a query parameter is an implementation concern and leaves every signed object untouched.
Collapsing the two would mean bumping the specification every time an implementation tidied a route — which makes a stable protocol look unstable, and trains integrators to ignore version numbers entirely.
API versions
Dated releases inside a named train, in the Stripe style.
The train is a long-lived line. Within it, each dated release is a snapshot of the HTTP surface. New dates are added; old ones keep working.
Pinning
Path-Version: 2026-09-09.genesisnew PathClient({ baseUrl, apiVersion: '2026-09-09.genesis' });An unpinned request gets the latest version of the default train, and the response says so:
Path-Version: 2026-09-09.genesis
Path-Version-Pinned: falseThat is fine while exploring and wrong in production. Unpinned means a shape can change under you, and you find out from a customer rather than from a test.
Path-Version-Pinned: false in a production log is a defect waiting for a date. Pin at construction
and change the constant deliberately.
Path alias
The train can also be pinned in the path, for callers whose proxies strip unfamiliar headers:
Same routers, same behaviour.
How a change reaches you
A response shape changes; the new shape is what the implementation produces. Callers pinned to an earlier date get it walked back by a transform, so nothing breaks on the day of the change.
Which means a version bump is something you adopt when you are ready, and adopting it is changing one string and running your tests.
Protocol versions
Semantic versioning over the formats.
| Change | Bump |
|---|---|
| A new optional field | Patch |
| A new object, a new URI kind, a new profile | Minor |
| A field removed or re-typed; a signature payload changed | Major |
Every signed envelope carries the version it was produced under.
{
"kind": "settlement_receipt",
"protocol_version": "0.1.0",
"…": "…"
}A verifier never has to guess. This is not decoration: a signature covers the canonical bytes of the payload, so a verifier that assumed the wrong version would canonicalise differently and reject a valid envelope.
Reserved fields
RESERVED fields are declared and never populated. They exist so that a future capability arrives as
an extension rather than a migration.
"route": nullAn implementation MUST carry reserved fields through untouched and MUST NOT reject an object because one is populated by a newer counterparty. Dropping unknown fields is the single most common way to break forward compatibility, and it fails silently.
What v0.1 does not promise
It is a draft. Formats may change before v1.0, and the changelog records every change with what it breaks and what to do.
What will not change without a major bump: the meaning of an error code, the shape of a signature payload, or the semantics of a status.