Take card, wallet, bank and BNPL payments with one API — no PSP account required.
Welcome to spuke
spuke is a payments platform. You create a merchant account, we handle the PSP relationships (cards, wallets, SEPA, Klarna, iDEAL, and more), and you get one REST API to charge customers, run subscriptions, issue refunds and receive payouts to your bank.
This section gets you from zero to your first live payment.
Everything is JSON over HTTPS. Base URL: https://api.spuke.com/v1.
Quickstart (5 minutes)
1. Onboard your merchant account. Sign in to the dashboard, complete the 15-step onboarding, and wait for approval. You must be active before live keys work.
2. Grab an API key. Dashboard → Developers → API keys. You'll get two:
Redirect the buyer to the returned checkout_url. Use test card 4242 4242 4242 4242 with any future expiry and any CVC.
4. Receive the webhook. Add an endpoint at Dashboard → Developers → Webhooks and listen for checkout.session.completed. Always verify the signature — instructions here.
Core concepts
Merchant account. Your business on spuke. Owns your keys, products, transactions, payouts.
Session / Subscription / Invoice. Three ways to collect money. Pick by use case.
client_secret. Short-lived token used by the browser to confirm a payment. Never trust it as proof of payment — details.
Fees. Per-plan, per-method, calculated in EUR and deducted per transaction. See Fees.
Payouts. Automatic, on your configured schedule (daily/weekly), to your verified bank account.
Environments
Mode
Keys
Money
Payouts
Test
sk_test_… / pk_test_…
No
No
Live
sk_live_… / pk_live_…
Yes
Yes
Both modes share the same base URL and endpoints. The key alone decides which environment you hit.
Idempotency, errors, rate limits
Send Idempotency-Key: <your-key> on every mutating request. Safe retries, no duplicate charges.
Errors follow a stable shape: { "error": { "type": "…", "message": "…", "param": "…" } }. See Error reference.
Default limits: 60 req/min per key for reads, 20 req/min for writes. 429 rate_limited includes a Retry-After header.
Open Dashboard → Developers → API keys and click Create key. Pick a name, a mode (Test or Live), and copy the secret. The secret is shown once — spuke stores only a SHA-256 hash. If you lose it, revoke and create a new one.
Keys look like:
sk_live_ABCDEfghi23jkLmnOPqrSTUv... ← live
sk_test_ABCDEfghi23jkLmnOPqrSTUv... ← test
Authenticate a request
Send the secret as a Bearer token in the Authorization header:
POST /v1/checkout/sessions HTTP/1.1
Host: api.spuke.com
Authorization: Bearer sk_test_ABCDEfghi23jkLmnOPqrSTUv...
Content-Type: application/json
Never call the API from the browser — the secret would leak to visitors. Always proxy through your own backend.
Scopes
Keys are unscoped by default and grant full merchant access (checkout:write, checkout:read, refunds:write, invoices:write). Per-scope keys are on the roadmap.
Rotation
Rotating a key is a two-step revoke:
Create a new key, deploy it, verify traffic uses it.
Revoke the old key in the dashboard (revoked_at is set immediately, all subsequent requests receive 401).
Idempotency
Every mutating request accepts an Idempotency-Key header. spuke stores the first response for 24 hours and returns the exact same status and body when the same key is sent again with the same payload. Reusing the key with a different payload returns 400 invalid_request (Idempotency-Key reused with different payload).
Always lowercase 3-letter ISO. Currencies outside this list return 400 invalid_request (currency_unsupported).
Payment method coverage
Not every method supports every currency. spuke automatically hides methods at checkout that the currency does not support (e.g. Klarna is EU-only, iDEAL is EUR-only, Bancontact is EUR-only).
Minimum and maximum
Minimum: 2.50 EUR equivalent (e.g. 250 in EUR/USD/CHF, 300 in JPY). Below this: 400 amount_too_small.
Maximum per charge: 999,999.99 in the transaction currency. Above this: 400 amount_too_large.
See Fees, currency & minimum amounts for the reasoning behind the minimum.
Payout currency
Merchants are paid out in their onboarding currency (usually EUR or CHF). Charges in other currencies are converted at the daily FX rate with a +2.00% FX surcharge.
How spuke calculates fees, converts currencies and why there is a minimum charge.
Fees, currency & minimum amounts
spuke charges fees in a predictable way — no matter which currency your customer pays in. This page explains exactly how it works.
1. Fees are always denominated in EUR
Every fee in your plan (Free / Starter / Business / Plus) and every per-method fee shown on the pricing and compare pages is defined in EUR.
The percentage part (e.g. 2.5%) applies to the transaction amount converted to EUR.
The fixed part (e.g. €0.25) is an EUR amount.
The final fee is charged in the transaction currency, converted at the live FX rate at the moment the payment is captured.
Example: a customer pays CHF 40.00 on the Business plan (3.9% + €0.30).
CHF 40.00 → EUR ≈ 41.20 (live FX)
Fee in EUR = 41.20 × 3.9% + 0.30 = €1.91
Fee back to CHF ≈ CHF 1.85 — this is what appears on the charge.
You always see both values in your dashboard (wallet, transactions, ledger).
2. Why the fee ledger sometimes shows a "top-up"
At checkout creation we don't yet know which card region the customer will use, so we charge the base rate. Once the payment is captured we know the real card region (EU / UK / intl) and compare the real fee to what was already deducted:
Correct: no ledger entry.
Under-collected: a fee_topup entry is created and added to the next transaction.
Over-collected: a refund entry balances it out.
This guarantees the merchant always pays exactly the plan's rate — no more, no less.
3. Minimum charge amount: 2.50 EUR
You can only create invoices, products and payment links with a total of at least 2.50 EUR (or the equivalent in the transaction currency).
Why? PSP fees + spuke fees always include a fixed component (e.g. €0.25 – €0.95 per transaction). On a €1.00 charge those fixed costs would exceed the amount and result in a loss — for you and for us.
The 2.50 minimum applies to:
Invoice totals (line items + tax)
Product prices (one-time and recurring)
API checkout_sessions — smaller amounts are rejected with amount_too_small
For subscriptions with trial periods the minimum applies to each billing cycle amount, not to the trial.
4. Payout currency & FX conversion
Payouts are made in your merchant currency (usually EUR or CHF). If a customer pays in another currency:
The gross amount is converted to your payout currency at the daily FX rate.
A +2.00% currency-conversion surcharge is applied on top of the standard fee.
The full breakdown is visible on the transaction detail sheet.
5. Full fee summary (per transaction)
Component
When
Amount
Plan percentage
Every transaction
e.g. 2.5% (plan-dependent)
Plan fixed
Every transaction
e.g. €0.25 (plan-dependent)
International card surcharge
Card issued outside merchant country
+1.50%
FX surcharge
Charge currency ≠ payout currency
+2.00%
Chargeback
Per dispute case
€15.00
Payout
Per bank payout
included on all paid plans
All values are defined in EUR and converted per transaction. Surcharges apply on top of the plan rate and only when the trigger condition is met.
6. TL;DR
Everything is priced in EUR.
Fees are converted to the transaction currency at capture time.
Minimum 2.50 EUR per invoice, product or checkout — otherwise fees can't be covered.
The fee ledger self-corrects so your effective rate always matches your plan.
How to protect your secret keys, webhooks and integration.
API security best practices
Never expose secret keys
sk_live_… and sk_test_… grant full merchant access. Never ship them in browser code, mobile apps, git repos, logs, error reports, or client-side analytics.
Always call the spuke API from your server. From the browser, only ever use the client_secret returned by POST /v1/checkout/sessions.
Rotate keys immediately if you suspect a leak (Dashboard → Developers → Revoke).
Separate test and live
sk_test_… keys only work in test mode and never move real money. Use them for CI, staging and local development.
Live keys should be stored in your production secret store (AWS Secrets Manager, Vault, GitHub Actions Secrets, etc.) — never in .env files committed to git.
Enforce TLS
The API only accepts requests over HTTPS (TLS 1.2+). Plaintext HTTP is rejected.
Verify TLS certificates in your HTTP client. Disabling verification defeats the point.
Always use Idempotency-Keys
Send an Idempotency-Key header on every POST that creates money-moving objects.
Use a stable value per business action (e.g. order_12345) — not a random UUID per retry.
See Checkout Sessions → Idempotency.
Verify webhook signatures
Never trust a webhook body without verifying the Spuke-Signature header with your whsec_… secret.
Reject signatures older than 5 minutes (replay protection).
See Webhooks → Verify signatures.
Rate limits
Default: 100 requests / second per merchant, burst 200.
Exceeding returns 429 rate_limited with a Retry-After header. Implement exponential backoff.
Principle of least privilege
Create separate keys per environment / service and revoke unused ones.
Per-scope keys (checkout:write only, refunds:write only, …) are on the roadmap.
Incident response
If a key is leaked:
Revoke it in the Dashboard immediately — all requests using it start returning 401.
Create a new key and deploy it.
Review the Developers → Request log for suspicious traffic.
Contact support@spuke.com if funds were moved that you did not authorise.