Versioning
The API version is in every URL: https://api.finscale.dev/v1. Breaking changes only ship in a new version; everything else is additive. This page defines exactly what "additive" means, so you can build an integration that never breaks on a deploy you didn't know about.
One version: v1
The current — and only — version is v1. It's part of the path, so there is no ambiguity about what you're calling. Within v1, Finscale ships continuously: new endpoints, new fields, new payment methods, new event types. None of that changes the version, because none of it breaks a correctly built consumer.
If a change would break existing integrations — removing a field, renaming one, changing a type or the meaning of a status — it ships as a new version with its own path, and v1 keeps working as documented. Versions are retired only after a long, announced migration window.
Pinning with Finscale-Version
You can also pin the version per request with the optional Finscale-Version header. Without it, your account default applies — which today is always v1:
curl https://api.finscale.dev/v1/payments/pay_8Q2mX4nT1cVb \
-H "Authorization: Bearer sk_test_51FinscaleDemo…" \
-H "Finscale-Version: v1"
With one version live the header is a no-op, but sending it now buys you a controlled migration later: when a new version exists, an account can move its default while individual services keep pinning the old one until each is ready. Pin in one place — your HTTP client's default headers — not per call.
What changes without a new version
These are compatible changes. They can appear in v1 at any time, and your integration must tolerate all of them:
| Change | Example |
|---|---|
| New API endpoints | /v1/disputes appearing alongside existing resources |
| New optional request parameters | A new filter on a list endpoint |
| New fields in responses | A risk object appearing on the Payment |
| New event types | dispute.created added to the catalog |
| New values in open enums | A new payment method id, a new decline_code, a new error code |
| Longer opaque strings | Ids and cursors growing — never parse structure out of them |
What never changes inside v1: existing fields are not removed or renamed, types don't change, documented state machines (payment, dispute, KYB) don't lose or reorder states, and error envelope structure stays fixed.
Building tolerant integrations
- Ignore unknown fields. Deserialize into structures that skip what they don't recognize — reject-unknown-keys parsing is self-inflicted breakage.
- Handle unknown event types by acking. Return 2xx for event types you don't handle; log them if you're curious. See Webhooks.
- Default the enum switch. Every
switchon a method, decline code, or error code needs a sanedefaultarm — new values arrive without a version bump. - Treat ids as opaque. Prefixes like
pay_are a debugging convenience, not a contract. Compare ids byte-for-byte; never derive meaning from length or content.
https://api.docs.finscale.dev/openapi.json always describes current v1. If you generate a client, regenerate on your own schedule — additions are safe to pick up whenever you like.