Product Checkout Real-time dashboard AI Workforce VCI verified identity Six payment states
Pricing
Developers Documentation API reference Webhooks SDKs Full docsComing soon Updates Changelog SandboxComing soon Migration toolComing soon
Compare Security
Company Our story Press Brand kit Updates Changelog
Live Try the demoComing soon Sign inComing soon Talk to us Get started

Developers

Developers. No surprises.

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.

Four primitives. One API.

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.

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 start

Recurring billing

Set 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 docs

Programmatic refunds

Issue 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 docs

Real-time settlement state

The 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 states

At a glance.

Everything a developer needs to know in one table. Detail in the sections below.

Question Honest answer
API styleREST, JSON over HTTPS only, predictable resource URLs, conventional HTTP verbs
Base URLhttps://api.fluxapay.co.uk/v1
AuthenticationBearer tokens. Secret keys prefixed sk_test_ or sk_live_. Restricted keys with scoped permissions for microservices.
IdempotencyIdempotency-Key header on every POST. Same key returns the same response, even on retries, for 24 hours.
VersioningDate-stamped API versions in the Fluxa-Version header. Breaking changes only in new versions, with at least six months of notice and overlap.
WebhooksHMAC-SHA256 signed, replay-protected with timestamp, retried with exponential backoff up to 72 hours
Hosted checkoutCard 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.
SandboxFull sandbox with test cards, simulated webhooks, identical schemas to live. No real money moves.
SDKOfficial Node.js / TypeScript SDK at launch. Python, PHP and Ruby on the roadmap. OpenAPI 3.1 spec for any language.
StatusLive platform health and incident history at /live. Subscribe via SMS, Slack or webhook.
SupportEmail support@fluxapay.co.uk. Logged-in merchants also have Tom, our in-dashboard AI chat, for instant first-line answers.

Quick start.

From signup to first webhook in under five minutes. The complete flow:

  1. Apply for sandbox access at fluxapay.co.uk/#start. No card needed for sandbox access. KYB verification happens before you go live, not before you test.
  2. Grab a test API key from Developers » API keys. Test keys are prefixed sk_test_; they cannot move real money.
  3. Make your first request. The call below creates a checkout session for £19.99 and returns a hosted URL you can redirect a test customer to. Switch language with the tabs:
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'];
  1. Pay with a test card. Use 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.
  2. Receive a webhook. Add a webhook endpoint in the dashboard, point it at a local URL via a tunnel of your choice, and you will receive the 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.

The PFaaS model.

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:

  • One contract, one integration. You sign one merchant agreement with Fluxa. You do not need a separate acquiring agreement, a separate gateway agreement, and a separate settlement agreement. All three sit behind our single REST API.
  • Faster onboarding. Verified Commercial Identity (VCI) handles KYB programmatically. Most merchants are onboarded in hours, not the weeks a direct payment partner would take.
  • Transparent rate. 1.8% flat per transaction. No interchange-plus, no payment partner markup buried in a quarterly statement, no surprise fees on commercial cards (corporate-card surcharge is on the page in advance, not after the fact).
  • We do not hold your money. Funds settle from the payment partner directly to your nominated bank account. Fluxa is a Referrer under PSR 2017, not an Electronic Money Institution; we are not authorised to safeguard funds and we do not.
  • You are the merchant of record. Chargebacks come to you, refunds are issued by you, the customer relationship is yours. We provide the dispute tooling and represent your evidence to the payment partner.

Who handles what

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.

API design.

Predictable beats clever. Nine rules apply across every endpoint, every resource and every response. Learn them once and the rest of the API follows.

  1. REST, JSON, HTTPS only

    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
  2. Prefixed, predictable IDs

    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
  3. Money in pence, currency in ISO 4217

    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
    }
  4. Idempotency by default

    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
  5. Versioning by date

    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
  6. Cursor pagination, never offset

    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"
    }
  7. Expandable resources

    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": "..." }
    }
  8. HTTP status means what it should

    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
  9. Metadata on every object

    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"
      }
    }

The payment object

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.

Authentication.

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:

Publishable key

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.

Secret key

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.

Restricted key

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.

Six payment states.

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.

Fluxa payment state machine PAYMENT_RECEIVED progresses to CAPTURED, then SETTLING, then SETTLED on the happy path. Any of the four happy-path states can transition to FAILED. SETTLED can transition to REFUNDED. PAYMENT_RECEIVED payment.received CAPTURED payment.captured SETTLING payment.settling SETTLED payment.settled REFUNDED payment.refunded FAILED payment.failed HAPPY PATH TERMINAL STATES
Happy-path transition Possible failure Refund transition

PAYMENT_RECEIVED

payment_received_at · payment.received

Card details accepted at the hosted checkout. Authorisation in progress at the scheme.

CAPTURED

captured_at · payment.captured

Scheme returned authorisation. Funds reserved on the cardholder’s card, not yet moved.

SETTLING

settling_at · payment.settling

Settlement initiated at the payment partner. Funds in flight toward your nominated bank account.

SETTLED

settled_at · payment.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

failed_at · payment.failed

Authorisation declined or settlement returned. Reason code on the payment object.

REFUNDED · terminal

refunded_at · payment.refunded

Full or partial refund processed. Original payment retains its history; the refund object links back to it.

The happy path is PAYMENT_RECEIVEDCAPTUREDSETTLINGSETTLED. 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.

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.

Signing

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.

Retries

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.

Event ordering

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.

Testing

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.

Event catalog

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.receivedCard details accepted at checkout, awaiting authorisation
payment.capturedAuthorisation approved by issuer, funds reserved
payment.settlingPayment Partner has initiated settlement to your nominated bank
payment.settledFunds confirmed in your bank account, settlement reference attached
payment.failedAuthorisation declined or settlement returned (see failure_code)
payment.refundedFull or partial refund processed
checkout.session.createdHosted or embedded checkout session opened
checkout.session.completedCardholder completed the checkout, payment created
checkout.session.expiredSession timed out without completion (24h default)
refund.createdRefund initiated, awaiting payment partner confirmation
refund.succeededRefund completed at the cardholder’s issuer
refund.failedRefund could not be processed (see failure_code)
dispute.createdChargeback raised by the cardholder’s issuer
dispute.evidence_requiredPayment Partner needs your evidence submission (deadline on payload)
dispute.evidence_submittedYour evidence has been forwarded to the payment partner
dispute.wonChargeback resolved in your favour, funds returned
dispute.lostChargeback final, funds debited from your next settlement
customer.createdCustomer object created (via API or first checkout)
customer.updatedCustomer details, payment methods or metadata changed
subscription.createdNew recurring billing subscription started
subscription.updatedPlan, schedule or quantity changed
subscription.cancelledSubscription cancelled (immediate or at period end)
subscription.payment.succeededScheduled recurring charge succeeded
subscription.payment.failedScheduled recurring charge declined; retry schedule attached
payment_method.attachedCard stored on a customer (vaulted, tokenised)
payment_method.detachedStored card removed from a customer
settlement.completedA settlement batch closed; aggregate of payment.settled events
merchant.kyb.approvedMerchant onboarding finalised; live keys now usable
merchant.kyb.action_requiredAdditional 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.

Reconciling settlements to your bank statement

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.

3D Secure and SCA.

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.

What Fluxa handles, what the payment partner and issuer handle

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.

3DS v1 and 3DS v2

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.

Frictionless and challenge flows

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.

SCA exemptions

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:

  • Low value. Transactions under £25 may skip SCA. A running counter at the issuer triggers SCA after five consecutive low-value exemptions or a cumulative £85.
  • Transaction Risk Analysis (TRA). Low-risk transactions, judged against the payment partner’s and issuer’s fraud rate thresholds, may be exempted. Threshold tiers: under £500 if payment partner fraud rate <0.13%; under £250 if <0.06%; under £100 if <0.01%.
  • Trusted beneficiary. If the cardholder has whitelisted the merchant with their issuer (a one-time SCA), subsequent payments to the same merchant can skip SCA.
  • Recurring transactions. Merchant-Initiated Transactions in a subscription series are exempt after the first payment (which is fully SCA-authenticated and creates a stored credential).
  • Corporate cards. Lodged commercial cards and Secure Corporate Payment (SCRT) transactions on dedicated B2B rails are out of scope.

The three_d_secure object

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.

Failed authentication

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.

Testing

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.

Sandbox and testing.

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.

Test cards

Card number Behaviour
4242 4242 4242 4242Immediate success, full lifecycle through SETTLED
4000 0000 0000 0002Generic decline at authorisation
4000 0000 0000 9995Insufficient funds decline
4000 0000 0000 0069Expired card
4000 0000 0000 32203DS challenge required, then success
4000 0000 0000 9979Fraud decline (do not retry)
4000 0000 0000 1976Successful auth, settlement returned (rare; tests your settlement-failure path)
5555 5555 5555 4444Mastercard 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.

Simulated settlement timing

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 and rate limits.

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.

Retry strategy

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.

Rate limits

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.

SDKs and tools.

Honest about what exists and what is on the roadmap. We will not claim a tool exists until you can npm install it.

Available now

Node.js / TypeScript SDK

npm install @fluxa/node. Full TypeScript types, automatic idempotency-key generation, retry-with-backoff built in. ESM and CommonJS both supported.

Postman collection

One-click import. Covers the full API surface with environment variables for test and live keys; test mode and live mode preconfigured.

Webhook signature samples

Verification snippets in Node, Python, PHP, Ruby, Go and Java in the webhooks docs. Copy-paste; no SDK required.

On the roadmap

Python SDK H2 2026

If you need it sooner, the OpenAPI generator produces a usable Python client today with full type hints from the spec.

PHP and Ruby SDKs H2 2026

Same situation as Python. The OpenAPI-generated client works today; an official package is on the way for both languages.

CLI tool Late 2026

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.

Status, support, security.

Status

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.

Developer support

Three routes, in increasing order of how much detail you should send:

  • Tom (in-dashboard AI chat) for quick questions while you are logged in. Tom is signed as AI in every message and hands off to a human in the support inbox when the question needs one.
  • Email support@fluxapay.co.uk with the 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.
  • Email support@fluxapay.co.uk for deep technical or architectural conversations. The Fluxa team routes these to the right person.

The full set of contact routes, including out-of-hours emergency, is on the contact page.

Security and compliance

The full security story is on the security page. The developer-relevant highlights:

  • PCI DSS SAQ-A scope through hosted checkout. Card numbers never touch your servers; the hosted payment fields are served from our payment partner’s PCI-Level-1 environment, embedded in the checkout iframe. Your scope is SAQ-A, the simplest tier.
  • TLS 1.3 only. Older TLS versions and plain HTTP are refused at the edge. HSTS is enforced with a 12-month max-age and preload.
  • AES-256 at rest for every persistent store. Keys managed in a dedicated KMS, rotated automatically every 90 days.
  • Encrypted webhook payloads available on request: in addition to HMAC signing, payloads can be encrypted with your public key (RSA-OAEP) so even our infrastructure cannot read them in transit.
  • Vulnerability disclosure via security@fluxapay.co.uk with PGP available. Acknowledged within one working day. Bounty programme in scoping; coordinated disclosure timelines on the security page.

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.

Start building. Today.

Sign up, mint a test key, and have a sandbox transaction live in under five minutes. No salesperson between you and a working integration. Node.js SDK, OpenAPI 3.1 spec, Postman collection and webhook signature samples in six languages, all ready when you are. KYB verification happens before you go live, not before you test.

Got it, we’ll send your sandbox details within one working day.
Or email direct: support@fluxapay.co.uk