Fynex Payments API
Accept money and pay it out: hosted checkout, server-to-server card payments, Apple Pay and Google Pay, split payments to marketplace payees, payouts and wallets.
Integration guides and the complete API reference for accepting payments and sending payouts with Fynex. Accept cards, Apple Pay, Google Pay, and bank transfers — and send payouts — with one unified API.
What is Fynex?
Fynex is a unified payments platform that lets you accept money from customers across cards, Apple Pay, Google Pay, and bank transfers, and pay money out to registered counterparties via the banking provider. A single seller-issued bearer token authenticates every call, and the same set of endpoints powers both hosted-checkout and full server-to-server flows.
These docs walk you through the integration end-to-end. Getting started covers your first successful payment. Concepts dives into the data model. Payment flows describes each acceptance method. Payouts covers sending money out. Operations has the operational essentials — idempotency, polling & SSE, errors, reconciliation, and the production checklist. Reference lists every endpoint, parameter, and response — generated from the live OpenAPI spec.
Where to start
- Quickstart — Take your first test payment in 5 minutes.
- Hosted checkout — The fastest way to accept a payment with the lowest PCI scope.
- Server-to-server — Full control of the flow when you collect card data on your own server.
- Payouts — Move money out from a wallet to a registered payee.
- Going live — Production checklist: tokens, monitoring, and the launch playbook.
Related: the Billing API
Invoices, subscriptions, contracts, metered usage and credit balances live on
their own surface, /billing-api/v1, documented at
/billing-api/v1/docs with the machine contract at
/billing-api/v1/openapi.json. It authenticates with the same seller secret
key. One convention difference to know before writing code: Billing carries
money exclusively as integer minor units in fields ending Minor, while
this API's checkout takes major units — see Amounts and currency.
Welcome to the Fynex Payments API
Fynex is a unified payments platform for accepting cards, Apple Pay, Google Pay, and bank transfers, plus moving money out via payouts. This reference describes every endpoint a seller integration needs.
All routes live under /payments-api/v1 and authenticate with a seller bearer
token. You can mint a token from the Fynex Dashboard.
Base URLs
- Production:
https://api.fynex.ai/payments-api/v1
The OpenAPI document's servers block declares the origin of the deployment
serving it. Each entry is the bare origin; the /payments-api/v1 prefix is
already part of every path in the document.
Which host you call does not decide whether money is real: that follows your account's operational mode. A Demo account is a sandbox account on any host.
Versioning
Every operation is served under /payments-api/v1 — there is no /v2
operation surface. /payments-api/v2 carries only the documentation for that
same contract (/payments-api/v2/docs, /openapi.json, /llms.txt), because it
was the path the current spec was first published under during the docs
migration. A request to /payments-api/v2/initialize-payment answers 404.
Call /payments-api/v1. Reading the reference at /v2/docs does not put
you on a newer contract; there is only one.
When a genuinely incompatible version ships it will be a new prefix, announced before it lands, and this section will say what changed and by when the old one stops. Until then there is one contract.
How a change is signalled
Additive changes — a new endpoint, a new optional field, a new enum value — ship without notice, so write clients that ignore fields they do not recognise and do not fail on an unknown enum value.
Anything being withdrawn is marked deprecated: true in openapi.json before
it goes, on the field, schema or operation itself. A renamed schema keeps its
former name published as a deprecated alias pointing at the replacement, so a
client generated against the older document still resolves. Read the spec, not
just the guides: the deprecation markers are in the schema, which is where a
generated client and an agent will see them.
Support and deprecation policy
Payments and Billing use the same shared public API lifecycle and deprecation policy. The terms below apply consistently to both public API references.
A public major version remains supported for at least 12 months after its successor becomes generally available. We give integrators at least 90 calendar days of notice before removing a public operation, field or enum value from a supported major version. Each notice names the replacement (when there is one), the migration action and the planned removal date in the public Changelog.
The OpenAPI deprecated: true marker is the machine-readable signal. For an
HTTP operation with a scheduled retirement, the changelog entry records the
planned Deprecation and Sunset response headers: Deprecation will say the
operation is no longer the preferred integration path, and Sunset will give
its planned last-availability date. No HTTP operation has a scheduled
retirement today, so neither header is currently emitted. A critical security,
legal or abuse response may need an earlier withdrawal; if that happens, we
publish the reason and the affected contract in the Changelog as soon as it is
safe to do so.
Integration paths
Pick the flow that fits your product:
- Hosted checkout — call
POST /checkout, redirect the buyer to the returned URL, receive the result via webhook. Lowest PCI scope. - Server-to-server — call
POST /initialize-payment, complete any 3DS challenge client-side, then callPOST /finalize-payment. You handle card data. - Capture / refund — once a payment is authorized or captured, use
POST /payments/{payment_id}/captureand/refundfor the full amount or partials. - Payouts —
POST /payoutsto send money from a wallet to a registered payee's payout method. - Top-up invoices —
POST /payments-api/v1/topup-invoicesissues an invoice a buyer settles by bank transfer, funding the seller's wallet once the deposit is matched. Enabled per deployment (404where it is off); ask Fynex before building on it.
The open top-up invoice cap
A seller may hold at most 20 open top-up invoices at a time — open being
pending, outdated or marked_paid. Issuing the twenty-first answers 422
with too many open top-up invoices; settle or cancel one and the same request
succeeds.
The bound exists because every issued invoice burns one number from the shared
(agreement number, YYMM) invoice-number series, which is also drawn on by
billing and settlement invoices. It is a supply bound, not a rate limit:
retrying the same request without clearing an open invoice will keep answering
422, and there is nothing to back off for.
Errors
Errors return JSON in the form { "error": "<message>", "code": "<code>" }:
code is specific where the API can say more than the status, and otherwise
derived from it (bad_request, not_found, conflict, …). Status codes follow
the usual conventions:
| Code | Meaning |
|---|---|
| 400 | Validation error |
| 401 | Missing or invalid bearer token |
| 404 | Resource not found for this seller |
| 409 | Conflict (e.g., invalid status transition, duplicate refund) |
| 422 | The request is well formed but not applicable in the seller's current state (e.g., the open top-up invoice cap above) |
| 500 | Internal error |
| 502 | Upstream provider failure |
Amounts and currency
Request amounts are major units everywhere on this API (e.g. 19.99), with one
exception: a split share in orderData.payeeDistribution takes amountMinor, integer minor
units (its major-units amount is deprecated).
Payouts take them as a decimal string ("amount": "19.99"); the integer
amountMinor on payouts is a deprecated fallback that amount overrides when
both are sent. Currencies are ISO 4217 codes (GBP, EUR, USD).
Error format
Every error returns JSON with an error and a machine-readable code. Branch
on code, never on the error text, which may be reworded:
{ "error": "This card has expired. Please use a different card.", "code": "card_expired" }
code is specific where the API can say more than the status (card_expired
above); otherwise it is derived from the status — bad_request,
unauthorized, forbidden, not_found, conflict, unprocessable_entity,
rate_limited, internal_error, bad_gateway and so on.
Status codes:
| Code | Meaning |
|---|---|
| 400 | Validation error |
| 401 | Missing or invalid bearer token |
| 404 | Resource not found for this seller |
| 409 | Conflict (e.g., invalid status transition, duplicate refund). A refund refused by the seller balance check carries code insufficient_balance (the main wallet cannot cover the refund plus its fee), balance_settling (retry later: the payment's balance is still settling) or refund_requires_review (Fynex must review the payment first); no refund was created |
| 422 | Card refused with no funds moved (code: card_expired, card_brand_not_supported) — ask for a different card. On POST /checkout and payment-link creation, code currency_not_supported: no terminal of the account can take the currency (or declared country), and nothing was created |
| 500 | Internal error |
| 502 | Upstream provider failure |
Quoting a failure to support
Every response carries X-Request-Id — a req_-prefixed identifier generated
at the boundary, on failures as well as successes, and on a 404 for a URL that
does not exist. Log it alongside your own request and quote it when reporting a
problem: it is what turns "a call failed this morning" into one line in ours.
It is generated here and never taken from your request, so it is a correlation
id and not a field you can set. If you need to carry your own, send it in a
header of your own choosing — and note that on writes, Idempotency-Key is the
one caller-supplied value this API does act on.
Amounts and currency
Request amounts are major units everywhere on this API (e.g. 19.99).
Payouts take them as a decimal string ("amount": "19.99"); the integer
amountMinor on payouts is a deprecated fallback that amount overrides when
both are sent. Responses add …Minor integer twins where the wire needs them.
Currencies are ISO 4217 codes (GBP, EUR, USD).
Read the field name before the value — this is where the 100× mistake lives. The platform's house convention (the Billing API follows it throughout, and new Payments fields adopt it) is:
| Value | Type | Field name | Example |
|---|---|---|---|
| Money amount | Integer, minor units | ends Minor |
4999 is £49.99 |
| Rate / percentage | Integer, basis points | ends Bps |
275 is 2.75% |
| Quantity | Decimal string | — | "1250.5" |
| Tax rate | Decimal string, percent | — | "20.0" |
Checkout's major-unit fields predate that convention and remain accepted.
The trap is real in both directions: 49.99 sent into a …Minor field
underpays a hundredfold, and 4999 sent into checkout's totalAmount
overcharges by the same factor — neither errors. When a field name ends
Minor, it is an integer count of the currency's smallest unit, everywhere,
on both APIs.