SDKs & API clients
Finscale is a plain REST API: JSON in, JSON out, Bearer auth, predictable ids. Every example in these docs is curl, because curl is the whole truth — no wrapper hiding a header from you. When you want types, generate a client in your language from the same OpenAPI document that powers the API reference.
curl-first, by design
There is no proprietary Finscale SDK to install, version, and wait on. That's deliberate:
- The HTTP surface is small. One base URL, one auth header, one error envelope, cursor pagination. An SDK would abstract about a screenful of conventions.
- What curl shows is what production does. Every doc sample translates line-for-line into any HTTP client — the headers you see are the headers you send.
- No language lottery. Generated clients come from the machine-readable contract, so every language is a first-class citizen the day an endpoint ships.
Requests run against the sandbox at https://api.finscale.dev/v1 with any sk_test_ key — try one in the API playground without writing code.
The OpenAPI document
The complete machine-readable contract lives at:
curl https://api.docs.finscale.dev/openapi.json -o openapi.json
It describes every endpoint, request and response schema, error shape, and webhook event payload — the same source that renders the API reference. Each operation carries a stable operationId (createPayment, listMerchants, submitDisputeEvidence…), which generators turn into method names, so generated code reads like the reference. It always tracks current v1; pin the file in your repo and regenerate on your own schedule.
Generating a typed client
Any OpenAPI 3.1 generator works. Recipes for the common stacks:
# types only — zero runtime cost
npx openapi-typescript https://api.docs.finscale.dev/openapi.json -o finscale.d.ts
# typed client over fetch
npm install openapi-fetch
import createClient from "openapi-fetch";
import type { paths } from "./finscale";
const finscale = createClient<paths>({
baseUrl: "https://api.finscale.dev/v1",
headers: { Authorization: `Bearer ${process.env.FINSCALE_SECRET_KEY}` },
});
const { data, error } = await finscale.POST("/payments", {
headers: { "Idempotency-Key": "idem_ord_9f21_0716" },
body: {
amount: 4900,
currency: "EUR",
payment_method: "ideal",
reference: "ord_9f21_0716",
return_url: "https://shop.example.com/checkout/return",
},
}); // data is a fully typed Payment
pipx install openapi-python-client
openapi-python-client generate \
--url https://api.docs.finscale.dev/openapi.json
# generates a package with typed models (Payment, Merchant, Dispute…)
# and one module per operationId under api/
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
curl https://api.docs.finscale.dev/openapi.json -o openapi.json
oapi-codegen -generate types,client -package finscale openapi.json > finscale.gen.go
# client methods follow operationIds: CreatePayment, ListMerchants, …
npx @openapitools/openapi-generator-cli generate \
-i https://api.docs.finscale.dev/openapi.json \
-g java --library native \
-o finscale-client
# same tool generates csharp, kotlin, ruby, php, rust, swift, …
These are independent open-source tools, named as examples — any generator that reads OpenAPI 3.1 produces an equivalent client.
What your client must still do
Generation gives you types and method names. The conventions that make an integration production-grade stay your job, whichever route you take:
| Concern | Rule |
|---|---|
| Auth | Secret keys live server-side only; inject Authorization: Bearer from config, never from code. See Authentication. |
| Idempotency | Every POST carries an Idempotency-Key derived from your own entity — idem_ord_9f21_0716, not a random UUID per attempt. |
| Retries | Retry 429 honoring Retry-After and 5xx with backoff — same key, bounded attempts. See Rate limits. |
| Errors | Map the error envelope to typed failures on error.code; log request_id on every non-2xx. |
| Money | Amounts are integers in minor units with an ISO 4217 currency — 4900 + "EUR" is €49.00. Never floats. |
| Tolerance | Ignore unknown fields and enum values — additive change is normal inside v1. See Versioning. |
| Webhooks | Signature verification is part of your integration, not the client — recipes in Webhooks. |