Customer sessions

A customer session lets your backend act as one buyer, and only that buyer. It is what makes a customer account you build yourself as safe as the one Flint hosts: ownership checks stay on Flint's side, so a mistake in your front end cannot show one buyer another buyer's order.

You authenticate the buyer however you already do. Flint never sees your password, your identity provider, or your login form.

Mint a session#

Your backend calls Flint with a secret API key holding customers.sessions.write, naming the customer it has just authenticated.

Bash
curl -X POST https://api.withflintpay.com/v1/customer-sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "customer_id": "cus_1kmn0aExample" }'
JSON
{
  "customer_session_id": "cses_1kmn0aExample",
  "customer_id": "cus_1kmn0aExample",
  "secret": "flint_cses_example",
  "expires_at": "2026-08-10T18:00:00Z",
  "refresh_token": "flint_cref_example",
  "refresh_token_expires_at": "2026-08-17T17:00:00Z"
}

secret is the working credential. Send it as Authorization: Bearer to /v1/me and nowhere else; no other endpoint accepts it.

There is no scope argument. What a customer session can do is fixed by Flint, and it is the same set whether you host the account or Flint does.

Three lifetimes, three jobs#

CredentialRequest fieldDefaultRange
account_urlaccount_url_expires_in_seconds15 minutes1 minute to 1 hour
secretexpires_in_seconds1 hour5 minutes to 24 hours
refresh_tokenrefresh_expires_in_seconds7 days1 hour to 30 days

The defaults are the right answer for most integrations. Shorten expires_in_seconds if you want tighter blast radius per request; lengthen refresh_expires_in_seconds if you want buyers to stay signed in for longer between logins.

account_url only comes back when the merchant uses Flint's hosted account. It is a one-time link that signs the buyer straight in, which is how you hand a buyer from your site to Flint's account without a second login. Redirect to it immediately. It is the shortest-lived credential in the response for a reason, and it does not belong in stored state or an email.

Refresh rotates both credentials#

When secret expires, trade the refresh token for a new pair. This call takes no API key, so the path that keeps a buyer session alive never needs your secret key.

Bash
curl -X POST https://api.withflintpay.com/v1/customer-sessions/refresh \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: refresh-cses-1kmn0aExample-004" \
  -d '{ "refresh_token": "flint_cref_example" }'

Idempotency-Key is required here, not optional.

Every refresh invalidates the token it consumed. Store the new pair before you use it, and serialize refreshes for one session, because two parallel refreshes holding the same token means the second one is a replay.

Reusing a rotated refresh token revokes everything

Presenting a refresh token that has already been exchanged returns CUSTOMER_SESSION_REFRESH_REUSED and revokes the whole session family, including the credentials the legitimate holder is using.

This is the designed response to a stolen token: Flint cannot tell the thief from the buyer, so it ends both. Treat it as a security signal, not a transient failure. Sign the buyer out and make them log in again. Do not retry.

Handle the four failures#

ErrorMeaningWhat to do
CUSTOMER_SESSION_EXPIREDThe secret aged outRefresh, then retry the original call
CUSTOMER_SESSION_REFRESH_EXPIREDThe refresh token aged outRe-authenticate the buyer, mint a new session
CUSTOMER_SESSION_REFRESH_REUSEDA rotated token came backRe-authenticate. The family is already revoked
INVALID_CUSTOMER_SESSIONMalformed or unknown credentialRe-authenticate

Only the first is worth retrying. The other three all mean the same thing to your application: this buyer needs to log in again.

Revoke on sign-out and on suspicion#

Revoke one session when a buyer signs out of one device:

Bash
curl -X POST https://api.withflintpay.com/v1/customer-sessions/cses_1kmn0aExample/revoke \
  -H "Authorization: Bearer YOUR_API_KEY"

Revoke every session for a customer when the buyer changes their password, when you close their account, or when you think the account has been taken over:

Bash
curl -X POST https://api.withflintpay.com/v1/customers/cus_1kmn0aExample/sessions/revoke \
  -H "Authorization: Bearer YOUR_API_KEY"

Neither call signs the buyer out of Flint's own hosted account, and signing out there does not revoke your customer sessions. The two are independent, which matters if you run both surfaces during a migration.

Keep the secret on your server#

Session secrets are server-side credentials

A customer session secret is a server-side credential with exactly the same handling rules as an API key. Not in browser code, not in a mobile bundle, not in a URL, not in a log line.

Your browser calls your backend; your backend calls Flint. Flint does not currently accept a customer session from a public client.

Rate limits#

Customer sessions are rate limited per session and per customer, separately from your merchant key. One buyer refreshing in a loop cannot consume your integration's budget.

Next steps#

Rate this doc