Hosted or embedded checkout
Take card payments without holding card data. Hosted at pay.fluxapay.co.uk for the fastest path; embedded as an iframe on your own domain when you want full visual control. Both keep your PCI scope at SAQ-A.
Quick startDevelopers
REST API, signed webhooks, hosted checkout in PCI SAQ-A scope, a transparent six-state payment lifecycle, a Node.js SDK. Predictable. Versioned. Documented to the response code. No undocumented behaviour, no breaking changes without notice, no states hidden behind a single “processing” flag.
Most Fluxa integrations use some combination of these four. Pick the ones you need, ignore the rest. The same authentication, idempotency, versioning and webhook contract applies across all of them.
Take card payments without holding card data. Hosted at pay.fluxapay.co.uk for the fastest path; embedded as an iframe on your own domain when you want full visual control. Both keep your PCI scope at SAQ-A.
Quick startSet up a subscription with a single API call. Automatic retry on declined cards, dunning emails, prorated upgrades and downgrades, cancellation at period end. All state changes fire webhooks.
Subscriptions docsIssue full or partial refunds via the API. Refunds become first-class payment objects linked back to the original. Chargeback evidence is submitted through the API or the dashboard; both surface the same dispute object.
Refunds docsThe six-state lifecycle exposes settlement progress through every state, end to end. When a payment shows SETTLED, the funds are in your bank account with a settlement reference you can match against your statement.
Payment statesEverything a developer needs to know in one table. Detail in the sections below.
| Question | Honest answer |
|---|---|
| API style | REST, JSON over HTTPS only, predictable resource URLs, conventional HTTP verbs |
| Base URL | https://api.fluxapay.co.uk/v1 |
| Authentication | Bearer tokens. Secret keys prefixed sk_test_ or sk_live_. Restricted keys with scoped permissions for microservices. |
| Idempotency | Idempotency-Key header on every POST. Same key returns the same response, even on retries, for 24 hours. |
| Versioning | Date-stamped API versions in the Fluxa-Version header. Breaking changes only in new versions, with at least six months of notice and overlap. |
| Webhooks | HMAC-SHA256 signed, replay-protected with timestamp, retried with exponential backoff up to 72 hours |
| Hosted checkout | Card data never touches your servers. PCI SAQ-A scope through our payment partner’s hosted payment fields. Visa, Mastercard, Apple Pay, Google Pay at UK launch. |
| Sandbox | Full sandbox with test cards, simulated webhooks, identical schemas to live. No real money moves. |
| SDK | Official Node.js / TypeScript SDK at launch. Python, PHP and Ruby on the roadmap. OpenAPI 3.1 spec for any language. |
| Status | Live platform health and incident history at /live. Subscribe via SMS, Slack or webhook. |
| Support | Email support@fluxapay.co.uk. Logged-in merchants also have Tom, our in-dashboard AI chat, for instant first-line answers. |
From signup to first webhook in under five minutes. The complete flow:
sk_test_; they cannot move real money.curl https://api.fluxapay.co.uk/v1/checkout/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Fluxa-Version: 2026-05-19" \
-d amount=1999 \
-d currency=gbp \
-d success_url=https://example.com/done \
-d cancel_url=https://example.com/cancel// npm install @fluxa/node
import Fluxa from '@fluxa/node';
import { randomUUID } from 'node:crypto';
const fluxa = new Fluxa(process.env.FLUXA_SECRET_KEY);
const session = await fluxa.checkout.sessions.create({
amount: 1999,
currency: 'gbp',
success_url: 'https://example.com/done',
cancel_url: 'https://example.com/cancel',
}, {
idempotencyKey: randomUUID(),
});
console.log(session.url);# Using a client generated from our OpenAPI 3.1 spec.
# Official Python SDK on the roadmap for H2 2026.
import os, uuid, requests
resp = requests.post(
'https://api.fluxapay.co.uk/v1/checkout/sessions',
headers={
'Authorization': f"Bearer {os.environ['FLUXA_SECRET_KEY']}",
'Idempotency-Key': str(uuid.uuid4()),
'Fluxa-Version': '2026-05-19',
},
data={
'amount': 1999,
'currency': 'gbp',
'success_url': 'https://example.com/done',
'cancel_url': 'https://example.com/cancel',
},
)
session = resp.json()
print(session['url'])<?php
// Using a client generated from our OpenAPI 3.1 spec.
// Official PHP SDK on the roadmap for H2 2026.
$ch = curl_init('https://api.fluxapay.co.uk/v1/checkout/sessions');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('FLUXA_SECRET_KEY'),
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
'Fluxa-Version: 2026-05-19',
],
CURLOPT_POSTFIELDS => http_build_query([
'amount' => 1999,
'currency' => 'gbp',
'success_url' => 'https://example.com/done',
'cancel_url' => 'https://example.com/cancel',
]),
]);
$session = json_decode(curl_exec($ch), true);
echo $session['url'];4242 4242 4242 4242 for an immediate success, any future expiry, any three-digit CVC. The full set of test cards (decline, 3DS challenge, insufficient funds, fraud-decline) is in the testing docs.checkout.session.completed event signed with HMAC-SHA256 in the X-Fluxa-Signature header.That is the entire happy path. There is no waitlist, no application form for the sandbox, no salesperson between you and a test transaction.
Fluxa is a Payment Facilitation as a Service platform. That means we are not a card scheme, not an acquiring bank, and not your merchant of record. We are the technical and operational layer that sits between you and an FCA-authorised payment partner. We onboard you, hold the technical relationship with the schemes through that partner, settle funds to your nominated bank account, and provide the tooling.
Concretely, this affects you in five ways:
Under PFaaS, four parties have distinct responsibilities. Knowing where Fluxa sits clarifies what your integration is and is not on the hook for. This matrix is the same whether the card payment is £5 or £5,000.
| Responsibility | You | Fluxa | Payment Partner | Issuer |
|---|---|---|---|---|
| Collect card data | Card data never touches you | Hosts the checkout iframe | Owns PCI scope (Level 1) | |
| SCA / 3D Secure | Orchestrates the flow | Is the 3DS Server | Performs authentication | |
| Authorise the payment | Routes the request | Submits to scheme | Approves or declines | |
| Hold or safeguard funds | Funds never touch you in flight | Never. We are a Referrer, not an EMI | In flight only during settlement | |
| Settle funds to bank | Receives in your nominated account | Tracks state, surfaces reference | Sends BACS or Faster Payment | |
| Merchant of record | You are the merchant | Not Fluxa | Not the payment partner | |
| Issue refunds | You initiate via API or dashboard | Submits to payment partner, surfaces state | Reverses to original card | |
| Handle chargebacks | Defend or accept | Surfaces dispute, submits your evidence | Adjudicates with scheme | Raises the chargeback |
| KYB on the merchant | Provide the data | VCI handles programmatically | Final approval | |
| Support cardholders | You own the customer relationship | We support you, not cardholders | Supports their own cardholder |
Two lines stand out and are worth restating. We never touch your funds: the payment partner settles directly to your nominated bank account, with a Fluxa settlement reference attached so you can match it against your bank statement. You are the merchant of record: the cardholder relationship is yours, refund policy is yours, chargebacks come to you. We provide the tooling, the data and the integration; the commercial relationship with your customer stays with you.
If you have integrated Stripe, GoCardless, Adyen or Square before, the developer experience will feel familiar: REST resources, signed webhooks, idempotency keys, test mode. The PFaaS model is what differs, and is documented in full so you can decide if it fits your stack and your compliance posture.
Predictable beats clever. Nine rules apply across every endpoint, every resource and every response. Learn them once and the rest of the API follows.
Conventional REST. Resources are nouns, verbs are HTTP methods. JSON request and response bodies, UTF-8 throughout. TLS 1.3 only; plain HTTP is refused at the edge with no redirect.
GET https://api.fluxapay.co.uk/v1/payments/pay_5f8b2c1e2a4b4d8c
Authorization: Bearer sk_live_...
Accept: application/json
Every object ID carries a short type prefix. Passing the wrong ID to the wrong endpoint surfaces an obvious error instead of a silent miss, and IDs stay readable in your logs.
pay_ payment rfd_ refund
cus_ customer sub_ subscription
chk_ checkout session mer_ merchant
evt_ webhook event dsp_ dispute
req_ request log err_ error code
Every monetary field is an integer in the smallest currency unit. Pence for GBP, cents for EUR and USD when those launch. No floats, no rounding decisions on your side, no locale ambiguity. Currency is the three-letter ISO 4217 code.
{
"amount": 4999,
"currency": "GBP",
"fee": 99,
"net": 4900
}
Every POST accepts an Idempotency-Key header. The same key returns the same response for 24 hours, even if the network failed on your first attempt. Retries are safe, expected, and never charged twice.
POST /v1/payments
Authorization: Bearer sk_live_...
Idempotency-Key: 8d8fcb1d-7b8a-4d6c-9a13-1f7c8e9b8c5d
Content-Type: application/json
Pin a version per request with Fluxa-Version. Old versions stay live for at least six months after a successor is released, with a 30-day sunset notice on the changelog before any URL retires. No silent breaking changes, ever.
Fluxa-Version: 2026-05-19
List endpoints return has_more and next_cursor. Pass limit (up to 100) and after on subsequent calls. No skipped or duplicated records when items are inserted mid-iteration, no slow OFFSET on large tables.
GET /v1/payments?limit=50
{
"data": [...],
"has_more": true,
"next_cursor": "pay_5f8b2c1e2a4b4d8c"
}
Inline related objects with expand[] instead of making N+1 follow-up requests. Up to three levels deep. Costs nothing extra at our end and saves a round trip at yours.
GET /v1/payments/pay_5f8b...?expand[]=customer&expand[]=settlement
{
"id": "pay_5f8b...",
"customer": { "id": "cus_...", "email": "..." },
"settlement": { "id": "stl_...", "settled_at": "..." }
}
2xx for success, 4xx for client errors with a machine-readable code and request_id, 5xx for our problems (safe to retry with exponential backoff). Status codes are never 200 with a hidden error in the body.
200 OK Request succeeded
201 Created Resource created
400 Bad Request Validation failure, see error.code
401 Unauthorised Missing or invalid bearer token
404 Not Found Resource ID unknown
409 Conflict Idempotency-Key reused with different body
429 Too Many Rate limit hit, see Retry-After
5xx Server Error Our fault, retry with backoff
Every Fluxa resource accepts a customer-controlled metadata key-value bag, up to 50 keys, 40-character key names, 500-character values. Useful for linking Fluxa objects to your internal IDs without us needing to know your data model.
POST /v1/payments
{
"amount": 4999,
"currency": "GBP",
"customer": "cus_3f7a91d8c2b04a6f",
"metadata": {
"order_id": "ord_8421",
"campaign": "spring-2026",
"fulfilment_centre": "london-01"
}
}
Here is a complete payment object showing every field a settled card payment carries. The same shape comes back from POST /v1/payments, GET /v1/payments/{id} and the payment.* webhook events.
{
"id": "pay_5f8b2c1e2a4b4d8c",
"object": "payment",
"amount": 4999,
"currency": "GBP",
"fee": 99,
"net": 4900,
"state": "SETTLED",
"ledger_state": "SETTLED",
"livemode": true,
"created_at": "2026-05-19T10:23:14.572Z",
"captured_at": "2026-05-19T10:23:15.103Z",
"settling_at": "2026-05-20T07:00:00.000Z",
"settled_at": "2026-05-21T07:14:32.001Z",
"settlement_reference": "STL-2026-05-21-FX-04812",
"settlement_method": "bacs_credit",
"payment_method": {
"type": "card",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028,
"country": "GB",
"funding": "credit"
}
},
"three_d_secure": {
"version": "2.2.0",
"method": "frictionless",
"result": "authenticated"
},
"customer": "cus_3f7a91d8c2b04a6f",
"checkout_session": "chk_8c9b2a14e0f74e90",
"metadata": {
"order_id": "ord_8421",
"campaign": "spring-2026"
},
"url": "https://api.fluxapay.co.uk/v1/payments/pay_5f8b2c1e2a4b4d8c"
}
Refund objects, checkout sessions, customers, subscriptions and disputes follow the same shape: a typed id, an object field that names the resource, a livemode boolean, a created_at timestamp, a metadata bag and resource-specific fields. Once you have read one, you can read them all.
Every request to the Fluxa API authenticates with a Bearer token in the Authorization header. There are three kinds of key, scoped to where they should live:
pk_test_ or pk_live_
Safe to embed in browser-side JavaScript or mobile apps. Used to initialise the hosted checkout. Cannot create charges, refunds or read any data on its own.
sk_test_ or sk_live_
Server-side only. Full read and write access. Never commit to source control, never put in front-end code. Use a secrets vault or environment variables.
rk_test_ or rk_live_
Server-side, scoped to specific resources and permissions (for example, read-only on payments, write-only on refunds). Useful for microservices that only need a slice of the API.
Keys can be rotated at any time from the dashboard. Old keys remain valid for a configurable grace period (1 hour to 30 days) to allow rolling deploys. Compromised keys can be revoked instantly, with the dashboard showing every request the revoked key has made in the prior 30 days for audit.
For additional defence in depth, secret and restricted keys support optional IP allowlisting. Specify CIDR ranges in the dashboard; requests from outside the allowlist are rejected with HTTP 403 and a clear error code, never silently dropped.
Most payment APIs collapse the payment lifecycle into a single status field with values like processing or pending. That hides every step between the customer’s card being charged and the money landing in your bank account. Fluxa shows all six states explicitly, every transition is timestamped, and every transition fires a webhook.
PAYMENT_RECEIVED
Card details accepted at the hosted checkout. Authorisation in progress at the scheme.
CAPTURED
Scheme returned authorisation. Funds reserved on the cardholder’s card, not yet moved.
SETTLING
Settlement initiated at the payment partner. Funds in flight toward your nominated bank account.
SETTLED
Funds in your bank account, confirmed by the payment partner’s settlement file. Settlement reference and method visible on the payment object.
FAILED · terminal
Authorisation declined or settlement returned. Reason code on the payment object.
REFUNDED · terminal
Full or partial refund processed. Original payment retains its history; the refund object links back to it.
The happy path is PAYMENT_RECEIVED → CAPTURED → SETTLING → SETTLED. FAILED and REFUNDED are terminal and can be reached from earlier states. A payment object never lies about where it is: if it shows SETTLED, the funds are in your bank account with a settlement reference you can match against your bank statement.
Every payment object also carries a ledger_state field with three coarser values (CAPTURED, SETTLING, SETTLED) intended for double-entry bookkeeping integrations where the intermediate transitions are less interesting than the accounting outcome.
Webhooks are the canonical way to keep your system in sync with Fluxa. Every state transition above fires one, signed and replay-protected. Polling the API is supported but webhooks are strictly preferred.
Every webhook request includes an X-Fluxa-Signature header containing a timestamp and an HMAC-SHA256 signature of the request body, separated by a comma:
X-Fluxa-Signature: t=1716135600,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
To verify: concatenate the timestamp, a full stop, and the raw request body. Compute HMAC-SHA256 over that string using your webhook signing secret (visible once in the dashboard when you create the endpoint). Compare to v1 using a constant-time comparison. Reject the request if the timestamp is more than 5 minutes old; this prevents replay attacks even if a valid signature is captured.
If your endpoint returns anything other than a 2xx response within 30 seconds, Fluxa retries with exponential backoff: 1 minute, 5 minutes, 15 minutes, 1 hour, 6 hours, 12 hours, 24 hours, 48 hours, 72 hours. After the final retry the event is marked as failed and surfaces in the dashboard with the full request history. Retried events carry the same id, so deduplicate on that on your side.
Events are not guaranteed to arrive in order. A payment.captured may arrive after a payment.settled for the same payment if a retry succeeds late. Always trust the payment object’s current state, not the event you received; events are notifications, not the source of truth.
The dashboard has a webhook replay tool: pick any past event, re-send it to any endpoint, and inspect the request and response live. Useful for debugging signature verification without waiting for a real payment.
Every event Fluxa fires. Names are resource.verb and stable across versions; we add new event types without breaking existing ones.
| Event | Fires when |
|---|---|
payment.received | Card details accepted at checkout, awaiting authorisation |
payment.captured | Authorisation approved by issuer, funds reserved |
payment.settling | Payment Partner has initiated settlement to your nominated bank |
payment.settled | Funds confirmed in your bank account, settlement reference attached |
payment.failed | Authorisation declined or settlement returned (see failure_code) |
payment.refunded | Full or partial refund processed |
checkout.session.created | Hosted or embedded checkout session opened |
checkout.session.completed | Cardholder completed the checkout, payment created |
checkout.session.expired | Session timed out without completion (24h default) |
refund.created | Refund initiated, awaiting payment partner confirmation |
refund.succeeded | Refund completed at the cardholder’s issuer |
refund.failed | Refund could not be processed (see failure_code) |
dispute.created | Chargeback raised by the cardholder’s issuer |
dispute.evidence_required | Payment Partner needs your evidence submission (deadline on payload) |
dispute.evidence_submitted | Your evidence has been forwarded to the payment partner |
dispute.won | Chargeback resolved in your favour, funds returned |
dispute.lost | Chargeback final, funds debited from your next settlement |
customer.created | Customer object created (via API or first checkout) |
customer.updated | Customer details, payment methods or metadata changed |
subscription.created | New recurring billing subscription started |
subscription.updated | Plan, schedule or quantity changed |
subscription.cancelled | Subscription cancelled (immediate or at period end) |
subscription.payment.succeeded | Scheduled recurring charge succeeded |
subscription.payment.failed | Scheduled recurring charge declined; retry schedule attached |
payment_method.attached | Card stored on a customer (vaulted, tokenised) |
payment_method.detached | Stored card removed from a customer |
settlement.completed | A settlement batch closed; aggregate of payment.settled events |
merchant.kyb.approved | Merchant onboarding finalised; live keys now usable |
merchant.kyb.action_required | Additional KYB information needed from you |
Subscribe to specific events per endpoint in the dashboard, or pass a comma-separated list to the events parameter when creating an endpoint via API. Endpoints with no filter receive every event for the account.
Under PFaaS, funds settle from the payment partner directly to your bank account. There is no Fluxa settlement report to reconcile against, because Fluxa does not hold the money in the first place. You reconcile against the bank statement line itself, using the settlement_reference field that Fluxa attaches to every SETTLED payment.
A typical bank statement line for a Fluxa-acquired settlement looks like:
21/05/2026 STL-2026-05-21-FX-04812 BACS CR 4,237.45 CR
Settlement, 7 transactions, FX payment partner
The reference STL-2026-05-21-FX-04812 matches the settlement_reference on every payment object in that batch. A practical reconciliation loop:
// On payment.settled webhook, save (settlement_reference, payment_id, net)
// Nightly, pull the day's bank statement and group by reference
// For each reference, sum payments → expect bank credit amount
const payments = await fluxa.payments.list({
state: 'SETTLED',
settled_at: { gte: yesterday },
expand: ['settlement']
});
const byRef = groupBy(payments.data, 'settlement_reference');
for (const [ref, batch] of Object.entries(byRef)) {
const expected = batch.reduce((sum, p) => sum + p.net, 0);
const bankLine = await bank.findCredit({ reference: ref });
assert(bankLine.amount === expected, `Mismatch on ${ref}`);
}
Every SETTLED payment also carries settlement_method (bacs_credit for standard UK settlement, faster_payment for accelerated batches, same_day for premium accounts). The reference itself is namespaced: STL-YYYY-MM-DD-FX-NNNNN, with FX denoting the payment partner code, the date being the settlement date (not the transaction date), and NNNNN a daily counter starting at 00001.
Worth flagging: under Stripe, GoCardless or Adyen, funds land in their account first, then transfer to yours on a schedule. Their reconciliation tools match payments to those provider-controlled settlements. Under Fluxa, that intermediate step does not exist. Your reconciliation matches payments directly to the bank statement line. This is a small architectural shift but a meaningful one for treasury and finance teams.
Strong Customer Authentication has applied to UK card payments since the PSD2 SCA enforcement ramp completed in March 2022. Most online card payments require the cardholder to authenticate with two of three factors: knowledge (password, PIN), possession (phone, token) or inherence (fingerprint, face). For card payments, that authentication runs over 3D Secure.
As a PFaaS layer, Fluxa orchestrates 3DS but does not perform the authentication itself. The payment partner is the 3DS Server; the cardholder’s bank (the issuer) runs the challenge. Fluxa carries the outcome on every payment object and signals state changes via webhooks.
Card details are collected at the hosted (or embedded) checkout, which is served from our payment partner’s PCI-Level-1 environment. Card data never touches your servers and never touches ours. When the checkout submits a payment, the payment partner initiates the 3DS flow with the issuer; the issuer decides whether to challenge or authenticate frictionlessly. If a challenge is required, it appears inside the same checkout iframe; the cardholder never leaves.
Fluxa sits above this. We expose the result to you as a three_d_secure object on the payment, advance the state machine and fire the webhook. You never write 3DS integration code yourself.
Both versions are supported. The payment partner prefers 3DS v2.x (faster, more frictionless approvals, in-app challenges) and falls back to v1 for the small number of issuers that have not migrated. Soft-decline retries on v1-only issuers are automatic; you do not have to handle them.
Roughly three quarters of authentications complete frictionlessly: the issuer authenticates the cardholder from the data the payment partner passes (device fingerprint, transaction history, risk signals) without showing any prompt. The cardholder sees the checkout submit and the payment succeed.
The rest require a challenge: a one-time passcode by SMS, a banking-app push notification, or biometric approval. The challenge is shown inside the checkout iframe. When the cardholder completes it, the payment continues without a page reload.
The issuer decides whether to apply an exemption, with the payment partner signalling preferences. You do not request exemptions yourself; we pass the right indicators based on the transaction context. The five exemptions in scope under PSR 2017 Article 100 are:
Every card payment object carries a three_d_secure sub-object describing the authentication outcome:
"three_d_secure": {
"version": "2.2.0",
"method": "frictionless",
"result": "authenticated",
"exemption": null,
"eci": "05",
"cavv": "AAACAWQ9...",
"ds_trans_id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
}
Possible values for method: frictionless, challenge, not_required (when an exemption applies). Possible values for result: authenticated, attempted (issuer not enrolled), not_required, failed, rejected. The exemption field names which of the five exemptions applied, or is null if a full authentication ran. The cryptogram fields (cavv, eci, ds_trans_id) are surfaced for your records; we already pass them to the payment partner with the authorisation request.
If the cardholder fails the challenge or the issuer rejects the request, the payment transitions to FAILED. The payment object carries failure_code: three_d_secure_failed and failure_message with the human-readable reason. A payment.failed webhook fires. You can retry the same checkout session with a different card; failed 3DS does not count against your VAMP or dispute thresholds.
The sandbox card 4000 0000 0000 3220 always triggers a 3DS challenge, then succeeds. 4000 0000 0000 3238 triggers a challenge and fails it. 4000 0000 0000 1091 exercises a v1-only issuer fallback. Other 3DS scenarios are listed in the testing reference at docs.fluxapay.co.uk/testing/3ds.
The sandbox is identical to live in every API contract: same endpoints, same schemas, same webhook events, same error codes. The only difference is that no real money moves and the payment partner is in sandbox mode.
Switch by changing the key prefix. sk_test_ hits the sandbox; sk_live_ hits production. Same code, no environment flag to flip in your application.
| Card number | Behaviour |
|---|---|
4242 4242 4242 4242 | Immediate success, full lifecycle through SETTLED |
4000 0000 0000 0002 | Generic decline at authorisation |
4000 0000 0000 9995 | Insufficient funds decline |
4000 0000 0000 0069 | Expired card |
4000 0000 0000 3220 | 3DS challenge required, then success |
4000 0000 0000 9979 | Fraud decline (do not retry) |
4000 0000 0000 1976 | Successful auth, settlement returned (rare; tests your settlement-failure path) |
5555 5555 5555 4444 | Mastercard equivalent of 4242; immediate success |
For any test card: use any future expiry date, any three-digit CVC, any postcode. Cardholder name is ignored in sandbox.
Sandbox runs at accelerated time so you can test the full state machine without waiting two days for settlement. By default, SETTLING arrives 30 seconds after CAPTURED and SETTLED arrives 30 seconds after that. You can force any delay (or skip directly to SETTLED) using the X-Fluxa-Sandbox-Timing header described in the testing docs.
Errors are JSON, structured, and stable. Every error has an HTTP status (meaning what it should), a machine-readable code, a human-readable message, and a request_id you can quote when contacting support.
{
"error": {
"type": "invalid_request",
"code": "amount_too_small",
"message": "Amount must be at least 30 pence for GBP transactions.",
"param": "amount",
"doc_url": "https://docs.fluxapay.co.uk/amount_too_small",
"request_id": "req_5f8b2c1e2a4b4d8c"
}
}
The complete error code list is in the errors reference. Each code has its own page with cause, example request, and the right way to handle it.
4xx errors are client errors and should not be retried without changing the request. 5xx errors and network errors are safe to retry; use exponential backoff with jitter, give up after a sensible number of attempts (we suggest 5), and re-use the same Idempotency-Key so the retry is not charged twice.
Standard limits are 100 requests per second per secret key, with a burst allowance of 200. Webhook endpoints we send to are limited to 1 request per second per endpoint, sustained, to avoid overwhelming your infrastructure during incident replay.
Rate limit headers are included on every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1716135660
When you exceed the limit, you get an HTTP 429 with code: rate_limit_exceeded and a Retry-After header in seconds. If your workload genuinely needs higher limits, email support@fluxapay.co.uk and we will lift them with no commercial uplift.
Honest about what exists and what is on the roadmap. We will not claim a tool exists until you can npm install it.
npm install @fluxa/node. Full TypeScript types, automatic idempotency-key generation, retry-with-backoff built in. ESM and CommonJS both supported.
Published at docs.fluxapay.co.uk/openapi.json. Generate a client in any language with openapi-generator-cli or any compatible tool.
One-click import. Covers the full API surface with environment variables for test and live keys; test mode and live mode preconfigured.
Verification snippets in Node, Python, PHP, Ruby, Go and Java in the webhooks docs. Copy-paste; no SDK required.
If you need it sooner, the OpenAPI generator produces a usable Python client today with full type hints from the spec.
Same situation as Python. The OpenAPI-generated client works today; an official package is on the way for both languages.
Local webhook forwarding, sandbox event triggering, and key management from the terminal. Not at launch; scoped for the second half of 2026.
The roadmap above is published on the changelog, with dates that get firmer as we approach them.
Live platform health is at /live: current state, recent incidents, scheduled maintenance, and historical uptime. Subscribe via SMS, Slack, email or webhook to get incident notifications the moment we detect them, not after they affect your transactions.
The status page covers four services independently: the public API, the merchant dashboard, the hosted checkout, and webhooks delivery. Each has its own uptime history. The target SLA for the public API is 99.99% monthly uptime, measured against a synthetic transaction running every 15 seconds.
Three routes, in increasing order of how much detail you should send:
request_id from any error response. Same-day reply during UK working hours, within one working day otherwise. Tag URGENT: in the subject for incidents.The full set of contact routes, including out-of-hours emergency, is on the contact page.
The full security story is on the security page. The developer-relevant highlights:
Fluxa is operated by Fluxa Ltd, a UK company registered in England and Wales (Companies House number 17028144). Fluxa is a UK registered trademark. Fluxa is a Referrer under the Payment Services Regulations 2017; payments are acquired by an FCA-authorised payment partner. Full regulatory and corporate detail is on the about page and the contact page.