Skip to content
In this section

Authentication & Tokens

How to obtain, use, and rotate seller bearer tokens for the Fynex Payments API.

Every request to /payments-api/v1 authenticates with a seller bearer token. Tokens are scoped to a single seller account and grant full API access — treat them like passwords.

The auth header

Include the token in every request as an HTTP Bearer token:

Authorization: Bearer <your_token>

Anything else — missing header, malformed value, or an unrecognised token — returns 401 Unauthorized.

How to get a token

You obtain your token yourself — there is no need to wait on the Fynex team.

From the dashboard (easiest):

  1. Log in to the dashboard (https://dashboard.fynex.ai) with your username and password.
  2. Select your seller account.
  3. Open the Integration page. It shows the API token masked, with a reveal (eye) toggle and a copy button.
  4. Click the reveal icon, then copy the token. This value is exactly what you pass as Authorization: Bearer <token>.

If you don't have a dashboard login yet, sign up self-service (see Programmatically below).

Programmatically: POST /api/v1/onboarding/start (sign-up; sets the session cookie itself) — or, for an existing account, POST /api/v1/login/dashboard (which sets the session cookie) → the createSellerAccount GraphQL mutation, which returns the new seller account's authorizationToken. The first user of a new organization is automatically granted the selleraccounts:create / selleraccounts:update permissions this requires. See the Account setup & onboarding guide for the full walkthrough.

Important

A new seller account is created in Demo mode and activated automatically, so your token works immediately for sandbox testing — you can take test payments straight away (see the Quickstart). Going live (real-money processing) is separate: it requires KYB approval and a Fynex-assigned live acquirer terminal. If a sandbox call ever returns 403 seller account is not active, the token is still valid — the account just isn't active yet; contact Fynex with your seller account ID.

Note

A separate token is issued per seller account, and each environment's dashboard issues its own — a token only works against the API host of the dashboard that issued it.

Caution

The Integration page also has a Regenerate button. Regenerating issues a new token and immediately invalidates the old one — there is no overlap window. Only use it when you intend to rotate (see Rotating a token).

What a token looks like

There are two credential shapes on this API, and which one you get depends on how you got it:

  • Dashboard-issued (Integration page, above). An opaque long-lived string with no prefix — treat the entire value as opaque, don't parse it.
  • Secret key (Integration → API keys, see /keys). Prefixed sk_test_… while the account is in demo mode and sk_live_… once it has gone live.

Both authenticate identically as a Bearer token on the Payments API. Don't assume every token on this API carries an sk_ prefix — the dashboard-issued shape doesn't.

The Billing API (/billing-api/v1) is stricter: it accepts only sk_test_… / sk_live_… secret keys and answers 401 to the prefix-less dashboard token. If you need both APIs, use an sk_ key for both — one key authenticates both surfaces, the legacy token authenticates only this one.

Verifying a token

The quickest "is this token alive, and which account is it?" check is GET /account. It requires only a valid seller token and returns the account the token acts for — including operationalMode, which tells you whether you are in Demo (test cards, no real money) or Live.

curl -sS "$FYNEX_API/account" \
  -H "Authorization: Bearer $FYNEX_TOKEN"
{
  "sellerAccountUuid": "3f2b1c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "name": "Acme Marketplace",
  "operationalMode": "Demo",
  "status": "Active",
  "currency": "EUR",
  "selfServeSandbox": false,
  "features": { "walletCreation": true, "payeePayoutAutoFunding": true, "refunds": true }
}

A 200 OK JSON response confirms the token works and the account is active. A 401 means the token is missing, malformed, or invalid. A 403 seller account is not active means the token is valid and recognised but the account isn't active — contact Fynex with your seller account ID (see How to get a token).

Storing tokens securely

  • Never commit tokens to source control. Use a secret manager (HashiCorp Vault, AWS Secrets Manager, GitHub Encrypted Secrets) or environment variables loaded at runtime.
  • One token per environment. Keep staging and production tokens separate.
  • One token per seller account. There is no cross-account access; operate multiple sellers with one token each.
# .env — never commit this file
FYNEX_TOKEN=<your_token_here>
FYNEX_API=https://api.fynex.ai/payments-api/v1

Rotating a token

The simplest way to rotate is the Regenerate button on the dashboard Integration page (the same page you got the token from). It issues a new token and reveals it for copying.

Caution

Rotation atomically replaces the existing token. The old token becomes invalid immediately — there is no two-token overlap window. Plan a brief service restart or deploy when rotating.

If you prefer to automate it, the same operation is exposed as the GraphQL mutation regenerateSellerAccountToken on the /dashboard/graphql endpoint (cookie-session authenticated).

Rotation steps (GraphQL)

  1. Authenticate with the dashboard to obtain a dashboard_session cookie:
   curl -c cookies.txt -X POST https://api.fynex.ai/api/v1/login/dashboard \
     -H "Content-Type: application/json" \
     -d '{"email": "you@example.com", "password": "..."}'
  1. Call regenerateSellerAccountToken with your seller account ID as merchantId:
   curl -b cookies.txt -X POST https://api.fynex.ai/dashboard/graphql \
     -H "Content-Type: application/json" \
     -d '{
       "query": "mutation Rotate($merchantId: ID!) { regenerateSellerAccountToken(merchantId: $merchantId) { authorizationToken } }",
       "variables": { "merchantId": "42" }
     }'

The response contains the new authorizationToken value.

  1. Update your services — replace the token in your secret manager / environment and restart affected services before the old token is invalidated (which happened in step 2).

When to rotate

  • A developer with access leaves the team.
  • You suspect or confirm a token leak.
  • As a precautionary measure on a regular schedule (quarterly is a common default).

Rotating an API key without an outage

sk_ API keys, unlike the dashboard token, rotate with an overlap: regenerating a key can leave the old one working for up to 7 days beside its replacement, so you deploy the new key first and let the old one lapse. Regenerating answers with the replacement's plaintext, once; the replacement keeps the old key's name, permissions and lifetime.

# $FYNEX_TOKEN is the dashboard token from the Integration page, not an sk_ key.
curl -sS -X POST https://api.fynex.ai/api/v1/developer/api-keys/<id>/regenerate \
  -H "Authorization: Bearer $FYNEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"overlapHours": 24}'
  • overlapHours — 0 to 168. 0, or no body at all, stops the old key at once, as before.
  • expiresAt — optional RFC 3339 time at which the replacement stops working. Without it the replacement lives as long as the old key was issued to, counted from now: a 30-day key regenerated today expires in 30 days, and a key that never expired is replaced by one that never expires.

Key management needs the dashboard token or a dashboard session: an sk_ key cannot create, regenerate or revoke keys.

Naming and expiring keys

Give each integration its own key — production, CI, one per agent — so one can be revoked without touching the others. POST /api/v1/developer/api-keys takes:

Field Required Meaning
type yes secret or publishable
name no What the key is for, up to 64 characters. Shown in the key list
expiresAt no RFC 3339 time at which the key stops working, at least a minute ahead. Omit it for a key that never expires

A key used after its expiresAt is refused with 401 api_key_expired. The key list reports it with status: "expired"; create or regenerate a key to replace it.

Restricted keys

A secret key can be restricted to what one integration needs: a reporting agent that reads payments and events, a metering job that only submits usage. Pass permissions when you create it:

{ "type": "secret", "name": "metering", "permissions": ["billing_usage:write"] }

Each permission is a resource at read or write; write includes read. Without permissions the key has full access, as every key had before.

Resource Covers
payments Payments, checkout sessions, capture, payment methods, settling a test payment
refunds Refunds and their cancellation, cancelling a promotion-paid order
payees Payees
payout_methods Payout methods, the seller's own and its payees'
payouts Payouts
wallets Wallets, their funding and transactions
topups Top-up invoices
split_rules Split rules and a payment's split decisions
webhooks Webhook endpoints and the allowlist
events The events feed
vendors Marketplace vendors
billing_contracts Billing customers, contracts, credits, prices and the test clock
billing_subscriptions Subscriptions
billing_invoices Billing invoices
billing_usage Usage events, metrics, dead letters and usage webhook endpoints

Every operation in the OpenAPI documents states the permissions it needs in x-fynex-permissions. A restricted key calling an operation it lacks a permission for is refused with 403 insufficient_scope, and the message names what is missing:

{ "error": "this API key is restricted: this route needs payouts:write", "code": "insufficient_scope" }

A restricted key reaches only the documented Payments and Billing operations, under /payments-api/v1 and /billing-api/v1, and the MCP endpoint. GET /account works with any key and reports the calling key's access and permissions under apiKey, so an integration can check what it may do. Regenerating a restricted key keeps its permissions; only a secret key can be restricted.

OAuth for AI clients (MCP)

Not yet available on production.

Token properties

Property Dashboard token sk_ API key
Scope Single seller account Single seller account
Expiry None Optional, per key (expiresAt)
Revoke None — rotate to invalidate Any key, at any time
Rotation Replaces the token at once At once, or with up to 7 days of overlap
Several per account No — one per seller account Yes, each with its own name
Permissions Full access Full access, or restricted to listed permissions
Rate limiting Per-seller token-bucket rate limit; the budget differs by environment — read X-RateLimit-Limit / X-RateLimit-Remaining rather than assuming a rate. Returns 429 with a Retry-After header when exceeded Same

Common errors

Status Body Cause
401 {"error": "authorization token is required", "code": "missing_api_key"} Header absent or malformed at the middleware level
401 {"error": "invalid authorization token", "code": "invalid_api_key"} Token value is not recognised
401 {"error": "invalid authorization token: this API key has expired …", "code": "api_key_expired"} An sk_ key used after its expiresAt
401 {"error": "invalid authorization token: invalid access token: …", "code": "invalid_token"} An OAuth access token that expired, whose connection was revoked, or presented outside the MCP endpoint
401 {"error": "seller auth is required"} Missing seller context inside a handler
403 {"error": "seller account is not active", "code": "account_not_active"} Token is valid but the seller account isn't active — new demo accounts activate automatically; if you see this, contact Fynex with your seller account ID
403 {"error": "this API key is restricted: …", "code": "insufficient_scope"} A restricted sk_ key without the permission this route needs — see Restricted keys
403 {"error": "resource does not belong to this seller"} Token is valid but the resource belongs to a different seller

Note

Every error body is JSON. Branch on code, not on the error text — see Errors.

See also

  • Quickstart — Take your first test payment in 5 minutes.
  • Errors — Full status code and error body reference.
  • Request headers — All request headers reference — required, conditional, and optional.