API for developers

Automate your balance and cards from your own scripts — outside the cabinet.

Base URL: https://app.fynko.net/api/v1

Authentication

  1. Open the cabinet → Security → “API access”.
  2. Name your token and press Create. The value is shown once — copy it now, it can't be shown again.
  3. Send it with every request:
    Authorization: Bearer <token>
    Accept: application/json

You can revoke a token any time from the same page — it stops working immediately. No valid token → 401 Unauthorized.

Card number & CVV

Your regular API token can't see the full card number or CVV. That needs a separate, narrower token type — safer than putting that power in a token you might also use in a script.

  1. In the cabinet → Security → “Card number & CVV access”, create a token: list the IP address(es) it may be used from, and confirm with your current password.
  2. That token can only call the reveal endpoint below — nothing else, and only from the IP(s) you listed. Any other request, or a request from another IP, is rejected even with a valid token.

Like in the cabinet, the CVV is shown once — a second reveal returns "cvv": null, "cvv_consumed": true.

Endpoints

GET/me

Your account profile and current balance.

{
  "id": 42, "name": "Ivan", "email": "[email protected]",
  "balance_minor": 5000, "balance": "50.00", "two_factor_enabled": true
}

GET/bins

The card options you can order (the bin_id values POST /cards accepts) — with fees and minimums already calculated for your account.

{
  "data": [
    {
      "id": 3, "code": "400242", "brand": "visa",
      "fee_rate_bps": 600, "open_price_minor": 100,
      "description": "US Ads", "region": "US",
      "min_top_minor": 500, "instant": true, "topup": true,
      "withdraw": false, "history": true
    }
  ]
}

GET/cards

Your cards.

GET/cards/{id}

Details for one card. balance is what you can spend right now: the balance minus open holds (authorizations the merchant has not settled or cancelled yet; a hold counts as spent until it is released). holds_minor is the amount currently held.

{
  "data": {
    "id": 7, "brand": "visa", "label": "Ads card", "last4": "4242",
    "status": "active", "balance_minor": 12000, "balance": "120.00",
    "holds_minor": 0, "pending_recharge_minor": 0, "expiry_month": 8, "expiry_year": 2028,
    "created_at": "2026-08-03T12:00:00+00:00"
  }
}

GET/cards/{id}/reveal

The full card number, CVV, expiry — and, for cards that have one, the static online-payment password (threeds_password): the value asked for on the second step of a 3DS purchase check. It is the same for all your cards and never changes; null when the card has none. Requires a reveal-scoped token — see “Card number & CVV” above.

{
  "pan": "4111111111111111", "cvv": "123", "cvv_consumed": false,
  "expiry": "08/28", "threeds_password": "Kx7pQ2"
}

GET/cards/{id}/statement

That card's own recent activity — issuing, recharges, purchases, refunds, cash-out. Up to 500 of the most recent entries; "truncated": true means older history isn't included.

{ "data": [ { "id": 501, "type": "card_spend", "amount": "−$12.34", "...": "..." } ], "truncated": false }

GET/cards/{id}/3ds-codes

Polling fallback for 3DS one-time codes: the codes for that card that haven't expired yet (10 minutes), newest first. Prefer the webhook — this is for clients that can't receive one. Up to 60 requests per minute.

{
  "data": [
    { "id": 123, "otp": "482913", "merchant": "Amazon",
      "created_at": "2026-09-13T12:00:00+00:00", "expires_at": "2026-09-13T12:10:00+00:00" }
  ]
}

GET/history

Your recent wallet activity: deposits, and money moving to/from a card (issuing, recharging, cashing out). A card's own purchases show up in that card's statement instead, not here. Up to 500 most recent entries.

POST/cards

Order a new card.

{ "bin_id": 3, "amount": 50 }

POST/cards/{id}/recharge

Top up an existing card.

{ "amount": 20 }

POST/cards/{id}/cashout

Close a card and return its remaining balance to your wallet.

POST/deposits

Reserve a crypto deposit address.

{ "network": "usdt_trc20", "amount": 25 }

Webhook for 3DS codes

When a purchase asks for a one-time 3DS code, the card provider sends it to us — and we can push it straight to your server, so your script can enter it without a human watching the cabinet.

  1. In the cabinet → Security → “Webhook for 3DS codes”, save an HTTPS URL. You get a signing secret (shown once). Saving the URL again issues a new secret.
  2. We POST a JSON body to that URL for every code. Respond with any 2xx status within 5 seconds; anything else is retried about once a minute until the code expires (10 minutes).
    POST {your URL}
    Content-Type: application/json
    X-Fynko-Event: card.3ds_code
    X-Fynko-Signature: t=1757764800,v1=5f1c…e2a9
    
    {
      "id": 123,
      "event": "card.3ds_code",
      "created_at": "2026-09-13T12:00:00+00:00",
      "data": {
        "card_id": 7, "otp": "482913", "merchant": "Amazon",
        "expires_at": "2026-09-13T12:10:00+00:00"
      }
    }
  3. Verify the signature before trusting the code: the X-Fynko-Signature header is t=<unix time>,v1=<hex>, where v1 is HMAC-SHA256 of the string <t>.<raw body> using your secret. Reject requests whose t is more than 5 minutes old.
    // PHP
    [$t, $v1] = explode(',', $_SERVER['HTTP_X_FYNKO_SIGNATURE']);
    $t = substr($t, 2); $v1 = substr($v1, 3);
    $expected = hash_hmac('sha256', $t.'.'.file_get_contents('php://input'), $secret);
    $valid = hash_equals($expected, $v1) && abs(time() - (int) $t) < 300;

The same code may arrive more than once (retries) — treat id as the deduplication key.

Limits & errors

Before retrying a request

If ordering or recharging a card times out, don't resend the same request blindly — check GET /cards first. A new card, or a changed pending_recharge_minor, means it already went through.