GitHub

Docs

Versioning

Two version lines that move independently — the protocol's formats, and an implementation's HTTP surface — and which one to pin.

LIVE

Two lines, on purpose

Looks likeChanges whenHeader
Protocol version0.1.0A format changes — a field, a state, a signed payloadPath-Protocol-Version
API version2026-09-09.genesisAn implementation's HTTP surface changesPath-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.

Fig. 01 — API version
2026-09-09.genesis
datetrain

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.genesis
new 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: false

That 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:

Fig. 02 — Path alias
ACanonical/api/path/v1/…
BExplicit train/api/path/genesis/v1/…

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.

ChangeBump
A new optional fieldPatch
A new object, a new URI kind, a new profileMinor
A field removed or re-typed; a signature payload changedMajor

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": null

An 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.


On this page