API reference
Fanback API
Give your cardholders cashback that Platforms pay for. Send settled spend, show a balance per Platform, and let cardholders claim it.
Get started
What Fanback is
Fanback is cashback for crypto cards, paid for by crypto Platforms instead of the card program.
Show each cardholder their cashback and let them claim it.
Costs you $0Fund the cashback in their own credit, to win card spenders as customers.
Pay only what's claimedRun the ledger, the claims, the abuse checks and the payouts to Platforms.
Runs everything in betweenHow a dollar moves
- A cardholder spends $500 on your card. When it settles, they earn cashback from every Platform open in their country: at 1%, $5 on each.
- Your app shows a balance per Platform: $5 on BloFin, $5 on Hyperliquid, and so on.
- The cardholder claims a balance by entering their username on that Platform.
- Fanback checks the claim and sends it to the Platform, which credits the cardholder's account there.
No money moves through your card. Cashback is credit on the Platform. It never appears as a card balance or a card transaction. Unclaimed cashback expires after 90 days, and Platforms pay nothing for it.
How the integration works
You build one screen, in your app and your brand. Three steps, each one or two API calls:
- A card spend settles. Call
POST /transactions. Fanback adds cashback to each Platform's balance. - The cardholder opens the cashback screen. Call
GET /cardholders/{id}/balancesand show a balance per Platform. - The cardholder taps Claim. Call
POST /claims. Fanback checks the claim, sends it to the Platform, and tells your webhook when it's paid or needs a corrected username.
Wrong usernames go back to the cardholder to fix, not to your support team. If your cards are issued at our issuer, spend is already connected and you can skip step 1.
Authentication
Send your API key as a bearer token on every request. You get a sandbox key and a live key during onboarding, and the base URL opens with your sandbox key.
Authorization: Bearer fb_live_xxxxxxxxxxxxxxxxhttps://api.fanback.net/v1Conventions
Amounts are always USD, as strings with two decimals: "12.40". Never floats.
Cardholder IDs are your own IDs. Use any stable string that isn't personal data. Fanback never needs a cardholder's name, email or card number.
Retries are safe. The same transaction id is recorded once. For claims, send an Idempotency-Key header.
Errors
Errors use standard HTTP status codes and a JSON body with a machine-readable code and a message you can log.
| Status | Meaning |
|---|---|
| 400 | The request is missing a field or a value is invalid. |
| 401 | The API key is missing or wrong. |
| 404 | The cardholder, claim or Platform doesn't exist. |
| 409 | The request conflicts with the current state, for example a claim already in progress. |
| 5xx | Something failed on our side. Retry with the same idempotency key. |
{
"error": {
"code": "below_minimum",
"message": "Claims start at $1.00 on BloFin."
}
}Step 1 · Spend
Send a transaction
POST/transactions
Send each card transaction once it settles. Don't send pending authorizations. Sending the same id again returns the original result.
idstringrequired- Your transaction ID.
cardholder_idstringrequired- Your ID for the cardholder.
cardholder_countrystringrequired- Two-letter country from KYC. Decides which Platforms the cardholder sees.
typestringrequiredpurchaseorrefund. A refund reverses the cashback its purchase earned.original_transaction_idstring- Required for a
refund: the purchase being refunded. amountstringrequired- Settled amount in USD.
mccstringrequired- Merchant category code. Deposits, top-ups and other cash-like spend don't earn.
settled_atstringrequired- When the transaction settled, ISO 8601.
Returns the cashback earned per Platform. cashback is empty when the transaction doesn't earn, for example a top-up.
curl https://api.fanback.net/v1/transactions \
-H "Authorization: Bearer $FANBACK_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "txn_8f2c91",
"cardholder_id": "user_4417",
"cardholder_country": "BR",
"type": "purchase",
"amount": "500.00",
"mcc": "5812",
"settled_at": "2026-11-01T14:03:00Z"
}'{
"id": "txn_8f2c91",
"cashback": [
{ "platform": "blofin", "amount": "5.00" },
{ "platform": "hyperliquid", "amount": "5.00" },
{ "platform": "stake", "amount": "5.00" }
]
}Step 2 · Balances
List balances
GET/cardholders/{cardholder_id}/balances
One balance per Platform, only for Platforms open in the cardholder's country.
platformstring- The Platform's ID.
availablestring- Cashback the cardholder can claim now.
claimablebooleanfalsebelow the Platform's minimum claim, usually $1.00. Show the balance anyway, with "Min $1 to claim".expiringobject or null- The next amount to expire and when.
curl https://api.fanback.net/v1/cardholders/user_4417/balances \
-H "Authorization: Bearer $FANBACK_KEY"{
"cardholder_id": "user_4417",
"balances": [
{
"platform": "stake",
"available": "11.80",
"claimable": true,
"expiring": {
"amount": "2.10",
"expires_at": "2026-11-13T00:00:00Z"
}
},
{
"platform": "axiom",
"available": "0.60",
"claimable": false,
"expiring": null
}
]
}List Platforms
GET/platforms
Names, logos and claim rules for the Platforms on your program. It changes rarely, so cache it.
countrystring- Two-letter country. Returns only Platforms open there.
category is one of exchange, perps, trading_app, terminal, prop_firm or casino. Use signup_url for "New to BloFin? Open an account".
curl "https://api.fanback.net/v1/platforms?country=BR" \
-H "Authorization: Bearer $FANBACK_KEY"{
"platforms": [
{
"id": "blofin",
"name": "BloFin",
"category": "exchange",
"logo_url": "https://fanback.net/brands/p/blofin-icon.webp",
"signup_url": "https://blofin.com/...",
"min_claim": "1.00",
"payout_currencies": ["USDT", "USDC", "BTC", "ETH", "SOL"]
}
]
}Step 3 · Claims
Create a claim
POST/claims
The cardholder picks a Platform, enters their username there, and chooses an amount and a payout currency. Send an Idempotency-Key header so a retry never creates a second claim.
cardholder_idstringrequired- Your ID for the cardholder.
platformstringrequired- The Platform's ID.
usernamestringrequired- The cardholder's username on the Platform.
amountstringrequired- Up to the available balance, at least the Platform's minimum.
payout_currencystringrequired- One of the Platform's
payout_currencies. cardholder_ipstringrequired- The IP the cardholder claimed from. Fanback checks it against the Platform's restricted countries.
terms_acceptedbooleanrequired- The cardholder accepted the cashback terms in your app. Must be
true.
A claim can be refused straight away:
| Code | Meaning |
|---|---|
below_minimum | The amount is under the Platform's minimum. |
insufficient_balance | The amount is more than the available balance. |
restricted_country | The Platform isn't available where the cardholder is. |
claim_in_progress | The cardholder already has a claim being processed. Try again in a few seconds. |
not_eligible | The cardholder can't claim cashback. We don't share the reason. |
curl https://api.fanback.net/v1/claims \
-H "Authorization: Bearer $FANBACK_KEY" \
-H "Idempotency-Key: 6c1e0a2f-claim" \
-H "Content-Type: application/json" \
-d '{
"cardholder_id": "user_4417",
"platform": "blofin",
"username": "lucas_k",
"amount": "7.15",
"payout_currency": "USDT",
"cardholder_ip": "177.92.10.4",
"terms_accepted": true
}'{
"id": "clm_51b7",
"cardholder_id": "user_4417",
"platform": "blofin",
"username": "lucas_k",
"amount": "7.15",
"payout_currency": "USDT",
"status": "pending",
"message": null,
"created_at": "2026-11-01T14:10:00Z"
}Claim statuses
A claim moves from pending to sent to paid. If the Platform can't find the username, it stops at action_needed until the cardholder fixes it.
| Status | What the cardholder sees | What happens next |
|---|---|---|
| pending | Claim received | Fanback checks it and adds it to the Platform's next batch. |
| sent | Sent to BloFin | The Platform is crediting the account. |
| paid | Paid | The credit is in the cardholder's Platform account. Final. |
| action_needed | lucas_k wasn't found on BloFin. Fix the username to resend. | The cardholder fixes the username. |
| returned | Claim returned to your balance | The Platform couldn't pay it, so the amount is back in the balance. Final. |
When the status is action_needed or returned, message carries text you can show as is.
Resubmit a claim
POST/claims/{id}/resubmit
For an action_needed claim. Send the corrected username and the claim goes back to pending.
usernamestringrequired- The corrected username on the Platform.
curl https://api.fanback.net/v1/claims/clm_51b7/resubmit \
-H "Authorization: Bearer $FANBACK_KEY" \
-H "Content-Type: application/json" \
-d '{ "username": "lucas.k" }'Retrieve a claim
GET/claims/{id}
Returns the claim object.
curl https://api.fanback.net/v1/claims/clm_51b7 \
-H "Authorization: Bearer $FANBACK_KEY"List claims
GET/claims
A cardholder's claims, newest first, 50 per page.
cardholder_idstringrequired- Your ID for the cardholder.
cursorstringnext_cursorfrom the previous page.
curl "https://api.fanback.net/v1/claims?cardholder_id=user_4417" \
-H "Authorization: Bearer $FANBACK_KEY"{
"claims": [
{ "id": "clm_51b7", "status": "paid", "...": "claim object" }
],
"next_cursor": null
}Webhooks
Claim updated
Fanback sends claim.updated to your HTTPS endpoint whenever a claim changes status, so you never need to poll.
Answer with any 2xx. Anything else is retried with backoff for 24 hours. The same event can arrive twice, so skip repeats by event id.
Verify the signature
Every request carries a Fanback-Signature header. Compute HMAC-SHA256 of {t}.{raw body} with your webhook secret, compare it to v1 in constant time, and reject timestamps older than 5 minutes.
{
"id": "evt_9a0d",
"type": "claim.updated",
"created_at": "2026-11-02T09:00:00Z",
"data": { "id": "clm_51b7", "status": "paid", "...": "claim object" }
}Fanback-Signature: t=1793350800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdimport { createHmac, timingSafeEqual } from 'node:crypto';
function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}Resources
Going live
- We send you a sandbox key, a webhook secret and a test Platform.
- You build the cashback screen against the sandbox and send test transactions.
- We review one claim end to end with you, then send your live key.
The OpenAPI file describes every endpoint and field for code generators and API tools.
FAQ
Is the cashback credited to the card?
No. It's credit on the Platform and can only be claimed into an account there. Nothing is added to the card balance.
Does it appear in the card's transaction history?
No. Show it on its own cashback screen, with a balance per Platform and the claim statuses.
Is this a way to bring cardholders to these Platforms?
Yes. That's why Platforms pay for it: every claim lands in an account on their Platform. Your cardholders get cashback on every spend, and you pay nothing.
What do Platforms see about my cardholders?
A username on their Platform, an amount and a payout currency. Never the card, the cardholder's name or their transactions.
What does Fanback store about my cardholders?
Your cardholder ID, their country, their settled transactions (amount, category, date), and their claims with the Platform username and IP address. No names, emails or card numbers.
Does it compete with my own cashback?
No. It stacks on top of whatever you already offer and has its own ledger.
Can I choose which Platforms appear?
Coming soon: you'll pick Platforms and categories, for example casinos off. Until then, tell us during onboarding and we'll set it for your program.