Docs
API 2026-05-27.dahlia Dashboard

Get started

Authenticate with a secret or restricted key, give each platform its own named key, and read the errors a key can get.

Every /v1 request carries one of your store's secret or restricted keys. Fluveo reads the key, finds the store it belongs to, and answers only with that store's objects.

Know your keys#

A secret key belongs to exactly one store. Your store's id starts with mch_, and you can read it with GET /v1/account.

KeyWhat it does today
sk_test_...Secret key, test mode. Every call works on test data, and no real money moves.
rk_test_...Restricted key, test mode. It calls only the endpoints its permissions allow. See Restricted keys.
sk_live_..., rk_live_...Live mode. Fluveo does not serve live mode yet, so a request with your store's live key answers 403.
pk_test_..., pk_live_...Publishable key, for the browser. fluveo.js uses it to show the payment form on your own page and to confirm the payment of a PaymentIntent whose client secret the page holds. It creates, lists and changes nothing: every other /v1 request that sends it answers 403 with code secret_key_required.

Each object carries livemode. With a test key it is always false.

Get a key from the Dashboard#

  1. Sign in to the Fluveo Dashboard.
  2. Open Settings in the sidebar and go to the API keys section. Check that the bottom of the sidebar shows Test mode.
  3. Under Standard keys, click Create secret key, give it a name, such as the platform account that will use it, click Create, copy the key and click Done. The Dashboard shows the full key only once. After that it lists only the name and a masked hint.

There is no API call that creates a key.

One key per platform account#

A store can hold many secret keys and many restricted keys at the same time, each with its own name. Give each platform account its own key: if your store sells through three accounts on a checkout platform, create three keys named after them. Each key works on its own:

  • Rotate key replaces one key with a new one that works at once. In Expiration you choose when the old key stops working: Now, or in 1 hour, 24 hours, 3 days or 7 days. Until then both keys work, so you can move each platform to the new key without downtime. The new secret key is named Secret key and the new publishable key Publishable key; a new restricted key keeps the old key's name and permissions.
  • Edit key changes a secret or restricted key's name and note, and a restricted key's permissions.
  • Expire key stops a secret or restricted key at once and cannot be undone. A publishable key cannot be expired; rotate it instead.

Rotating, editing or expiring one key never changes any other key. The list shows each key's name, creation date, last use and, after a rotation, the time left before the old key expires. There is no limit on how many secret or restricted keys a store creates.

Restricted keys#

A restricted key (rk_test_...) works like a secret key, but only on the endpoints its permissions allow. Use one when a platform, a report or a tool needs only part of your store, such as reading payments without creating them.

Create one in Settings > API keys > Restricted keys > Create restricted key: give it a name and choose None, Read or Write for each resource. Read allows the GET requests of the resource. Write allows every request of the resource, reads included. Every resource left at None, and every /v1 URL no resource covers, answers 403 to a restricted key.

Permissions lists each resource with its paths and the permission every operation of the reference needs. A resource's name there can differ from its Dashboard label: Prices is plan, and Charges and Refunds is charge. Paths match leading whole URL segments. The first matching resource on the Permissions page applies, so /v1/customers/{customer}/payment_methods needs payment_method, not customer. A request a key's permissions do not allow answers 403 before Fluveo does anything else, and the message names the permission that would allow it:

403 Forbidden
{
  "error": {
    "type": "invalid_request_error",
    "message": "The provided key 'rk_test_****abcd' does not have the required permissions for this endpoint on account 'mch_...'. Having the 'rak_payment_intent_write' permission would allow this request to continue."
  }
}

The permission in the message is rak_<resource>_read or rak_<resource>_write, with the resource names Permissions lists. An endpoint no permission covers answers the same 403 with "This is a restricted API key, but the required permissions are not available for use by restricted keys." in place of the last sentence.

A path under a resource the key may call that is not an endpoint, such as /v1/payment_intents/{intent}/unknown, answers 404 "Unrecognized request URL" once the key holds that resource's permission, as it does for a secret key. Without the permission it answers the 403 above.

A restricted test key created with This key is for an AI agent checked is an agent key. The API applies the agent policy to its calls, and it is the key Fluveo MCP takes. MCP authentication shows how to make, use and revoke one.

Send the key#

Put the key in the Authorization header as a Bearer token.

Authenticated request
curl https://api.fluveo.com/v1/balance \
  -H "Authorization: Bearer sk_test_..."
const response = await fetch("https://api.fluveo.com/v1/balance", {
  headers: { Authorization: "Bearer sk_test_..." },
});
console.log(response.status, await response.json());
import requests

response = requests.get(
    "https://api.fluveo.com/v1/balance",
    headers={"Authorization": "Bearer sk_test_..."},
)
print(response.status_code, response.json())
<?php
$ch = curl_init("https://api.fluveo.com/v1/balance");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer sk_test_..."],
]);
echo curl_exec($ch);

HTTP Basic also works: the key is the user name and the password is empty, as in curl -u sk_test_...: https://api.fluveo.com/v1/balance. The trailing colon tells curl the password is empty. Use one scheme per request.

Request bodies are form-encoded (application/x-www-form-urlencoded). Nested values use brackets: metadata[order_id]=1042 for a map and payment_method_types[]=pix for a list.

One store per key#

Fluveo has no header that switches to another account. A request that sends a header meant to act for another account answers 400 with that header in param, whatever its value. To act for another store, use that store's own key.

This is also how platforms work. A checkout platform never holds one key for many stores: each merchant pastes its own Fluveo key into the platform. See Platforms.

When the key is wrong#

CaseStatusError
No Authorization header, or an empty key401invalid_request_error, message "You did not provide an API key. ..."
A key in the wrong shape or an unknown key, a live or publishable one included401invalid_request_error, message "Invalid API Key provided: sk_test_****abcd" with the key masked
An expired key: expired in the Dashboard, or rotated and past the expiration you chose401invalid_request_error, code api_key_expired, message "Expired API Key provided: sk_test_****abcd" with the key masked
Your store's publishable key403invalid_request_error, code secret_key_required, message "This API call cannot be made with a publishable API key. Please use a secret API key."
A restricted key on an endpoint its permissions do not allow403invalid_request_error, the message in Restricted keys
Your store's sk_live_ or rk_live_ key403invalid_request_error, message "Live mode is not available yet: this API serves test mode only. Use your test secret key (sk_test_...)."
A valid key on a route your store may not use403invalid_request_error, the "not available" message below
A header that acts for another account400invalid_request_error with that header in param

A 403 "not available" refusal carries code not_available:

403 Forbidden
{
  "error": {
    "type": "invalid_request_error",
    "code": "not_available",
    "message": "This API is not available on this account. If its setup is not finished or is still under review, finish it in the Dashboard under Settings and try again once the account is approved."
  }
}

Test keys work from the moment your store exists. Activation in the Dashboard under Settings opens live mode only: if it is not finished or is still in review, your live keys wait for it.

Every answer carries a Request-Id header (req_...). Keep it when you report a problem. An error of a call your store's log keeps also carries error.request_log_url, a link to that call's log in the Dashboard; see Request logs. The full list of errors is on Errors.

Test it#

  • Call GET /v1/account with your test key. You get 200 and an id that starts with mch_.
  • Call it with no Authorization header. You get 401.
  • Call it with your store's publishable key (pk_test_...). You get 403 with code secret_key_required.
  • Create a restricted key with PaymentIntents: Read. GET /v1/payment_intents answers 200 and POST /v1/payment_intents answers 403 naming rak_payment_intent_write.

Next#