Authentication

How Tokeflow API authentication works — secret vs public keys, the Bearer scheme, key anatomy, Organization vs Merchant scoping, scopes, and IP allowlisting.

Every request to the Tokeflow API is authenticated with an API key. The key you present determines two things at once: who you are (the Organization or Merchant the key belongs to) and what you can do (the scopes granted to that key).

Tokeflow uses two kinds of keys:

  • Secret keys (sk_…) — full server-side access. Used from your backend with the Authorization: Bearer header.
  • Public keys (pk_…) — restricted, client-safe access. Used by the Bridge SDK in the browser to collect payment details. Safe to ship to the client.

Never expose a secret key in client-side code. Secret keys grant server-side access to your account. They belong only on your servers — never in browser JavaScript, mobile apps, public repositories, or anything a user can inspect. If a secret key leaks, revoke it immediately in the Tokeflow Dashboard. Use a public key for anything that runs in a browser.

The Bearer scheme

Server-side requests authenticate by sending your secret key in the Authorization header using the Bearer scheme:

Authorization: Bearer <secret_key>
curl https://api.tokeflow.com/api/v1/transactions \
  -H "Authorization: Bearer sk_live_mer_4f9a2c7e8b1d6035" \
  -H "Accept: application/json"

Load secret keys from environment variables or a secrets manager — never hardcode them. See Environments & API keys for how live keys are issued and rotated.

All endpoints live under https://api.tokeflow.com/api/v1. For the full request/response model and conventions, see the API overview.

Key anatomy

Every key encodes its type, environment, and scope in the prefix. The format is:

{sk|pk}_{environment}_{org|mer}_{random}

Worked example:

sk_live_mer_4f9a2c7e8b1d6035
│  │    │   └─ random — the unique, secret portion
│  │    └───── scope — org (Organization) or mer (Merchant)
│  └────────── environment — e.g. live
└───────────── key type — sk (secret) or pk (public)
SegmentValuesMeaning
Key typesk, pksk = secret (server-side). pk = public (client-safe).
Environmente.g. liveThe environment the key operates in.
Scopeorg, merorg = Organization-scoped. mer = Merchant-scoped.
Randomopaque stringThe unique secret portion. Treat the whole key as sensitive.

This means you can recognize at a glance what a key is:

  • pk_live_org_… — public, Organization key
  • pk_live_mer_… — public, Merchant key
  • sk_live_org_… — secret, Organization key
  • sk_live_mer_… — secret, Merchant key

Only the prefix is ever shown back to you in the Dashboard and in API responses (for example sk_live_mer_4f9a2c7e…). The full key is displayed exactly once, at creation time. Store it securely then — Tokeflow cannot show it again.

Organization keys vs Merchant keys

Tokeflow is multi-tenant: an Organization governs one or more Merchants (see Organizations & Merchants). Every key is scoped to exactly one entity, and that scope changes how merchant-specific resources are addressed.

Merchant key (…_mer_…)Organization key (…_org_…)
ScopeA single merchantEvery merchant in the organization
Merchant targetingInferred automatically — no merchant_id neededMust specify the target merchant via merchant_id
merchant_id on list/getNot used (ignored)Query parameter, e.g. ?merchant_id=mrc_…
merchant_id on createNot usedBody field
Best forA single merchant's own backendPlatform / multi-merchant orchestration

Merchant key — merchant is inferred

A Merchant key is already bound to one merchant, so you never pass a merchant_id. The example below lists that merchant's transactions:

curl "https://api.tokeflow.com/api/v1/transactions?limit=20" \
  -H "Authorization: Bearer sk_live_mer_4f9a2c7e8b1d6035" \
  -H "Accept: application/json"

Organization key — identify the merchant

An Organization key spans every merchant in the org, so for merchant-scoped resources you must say which merchant you mean. On reads, pass merchant_id as a query parameter:

curl "https://api.tokeflow.com/api/v1/transactions?merchant_id=mrc_8a3f12d9&limit=20" \
  -H "Authorization: Bearer sk_live_org_2b7e91c4a0f53d68" \
  -H "Accept: application/json"

On writes, pass merchant_id in the request body:

await fetch("https://api.tokeflow.com/api/v1/transactions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TOKEFLOW_ORG_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    merchant_id: "mrc_8a3f12d9",
    amount: 15000, // minor units → R$150.00
    currency: "BRL",
    payment_method: "credit_card",
    // …
  }),
});

With an Organization key, omitting merchant_id on a merchant-scoped request is a client error and returns 400 validation_error. With a Merchant key, any merchant_id you send is ignored — the key's own merchant always wins.

Scopes

Permissions are expressed as scopes in the form resource:action. A key is granted an explicit list of scopes when it is created in the Tokeflow Dashboard, and a request is authorized only if the key carries the scope the endpoint requires.

Common scopes:

ScopeGrants
transactions:readList and view transactions
transactions:writeCapture and refund transactions
customers:read / customers:writeRead / manage customers
orders:read / orders:writeRead / manage orders
products:read / products:writeRead / manage catalog products
offers:read / offers:writeRead / manage offers and prices
checkout:read / checkout:writeRead / manage checkout sessions
subscriptions:read / subscriptions:writeRead / manage subscriptions
webhooks:read / webhooks:writeRead / manage webhook configuration
tokens:readRead SDK tokens
reports:readView reports and analytics
audit_logs:readList and view audit logs

The :read / :write split lets you apply least privilege: issue read-only keys for reporting or analytics workloads, and reserve write scopes for the services that actually create or mutate resources.

Create one key per workload, with only the scopes that workload needs. Narrow, single-purpose keys limit blast radius if a key is ever compromised, and make audit logs easier to reason about.

Public keys: what they can do

Public keys (pk_…) are designed to be embedded in browser code. They are restricted to a small set of client-safe scopes — the operations the Bridge SDK needs to collect payment details and create tokens — and cannot read transactions, issue refunds, or touch any server-only resource.

Because of those restrictions, public keys are safe to expose in client-side code. The Bridge SDK presents the public key over SDK-specific headers rather than the Authorization header:

X-Public-Key: pk_live_mer_…
X-Session-Id: <uuid>
X-SDK-Version: <semver>

You normally never set these headers yourself — the SDK manages them. See the Bridge SDK overview for details.

Card data collected in the browser is captured by Tokeflow's secure fields, which run inside isolated, cross-origin iframes. Card data is encrypted in the browser and never touches your servers — so even a fully exposed public key cannot be used to read raw card details.

IP allowlisting (optional hardening)

For an extra layer of defense, you can restrict a key to a set of source IPs by configuring an allowed_ips allowlist on the key in the Tokeflow Dashboard. When set, requests from any other IP are rejected with 403 authorization_error, even if the key and its scopes are otherwise valid.

The allowlist accepts:

  • Exact IPv4 / IPv6 addresses — 203.0.113.10, 2001:db8::1
  • CIDR ranges — 203.0.113.0/24, 2001:db8::/32
  • Wildcards to allow all IPs — *, 0.0.0.0/0, or ::/0

IP allowlisting is most effective for secret keys used by backend services with stable egress IPs. Avoid pinning IPs on public keys — browser clients connect from many, ever-changing addresses.

401 vs 403

Tokeflow distinguishes authentication failures from authorization failures:

  • 401 authentication_error — Tokeflow could not establish who you are. The key is missing, malformed, revoked, or expired.
  • 403 authorization_error — Tokeflow knows who you are, but you are not allowed to do this. The key lacks the required scope, or the request came from an IP outside the key's allowed_ips.

A missing or invalid key returns 401:

{
  "error": {
    "type": "authentication_error",
    "code": "INVALID_API_KEY",
    "message": "Invalid or expired API key",
    "details": {},
    "request_id": "req_8f3c9a1e2d40",
    "timestamp": "2026-01-15T12:30:00.000Z"
  }
}

A valid key that lacks the required scope (or is blocked by IP allowlisting) returns 403:

{
  "error": {
    "type": "authorization_error",
    "code": "INSUFFICIENT_SCOPE",
    "message": "This API key is not permitted to perform this action",
    "details": { "required_scope": "transactions:write" },
    "request_id": "req_3a7d10b9c2e8",
    "timestamp": "2026-01-15T12:30:00.000Z"
  }
}

Treat 401 and 403 differently in your client. A 401 means fix the credentials (rotate or re-load the key). A 403 means fix the permissions — request the missing scope, or check the calling host against the key's IP allowlist. Never retry either blindly.

Next steps

On this page