Authentication

Flux API delivers published content. Each Flux API instance can be public or require API keys. When authentication is enabled, every request must present a valid Flux API key using one of the supported methods. One exception has no toggle: write requests (POST/PUT and the MCP write tools) always require an authenticated key with the matching create/update grants — public APIs accept anonymous reads only.

Overview

  • Configuration – Toggle authentication per API when creating or updating it through the Management API.
  • Keys – Generate Flux API keys through the dashboard or Management API. Each key contains a Base64 public key and a private key that is shown only once.
  • Methods – Flux API supports Secure (signature-based, for server-side and SDK integrations that can sign each request), Simple (static key pair, for clients where you write the Authorization header yourself), and Bearer (an opaque token bound to a key, for hosted connectors that can only send the Bearer scheme).

When an API is marked as “authentication required”, unauthenticated requests return 401 with error_code authentication_required.


API Key Components

When you create a Flux API key you receive:

  • Name
    public_key
    Type
    string
    Description

    Base64 encoded compressed P-256 public key. Appears in the Authorization header.

  • Name
    secret_key
    Type
    string
    Description

    Base64 encoded private key in DER format. Used to sign data for Secure auth. The service never stores the full secret, so keep it safe.

  • Name
    bearer_token
    Type
    string
    Description

    Optional, and issued separately — see Bearer Token Authentication. Never returned by a key read; only its first 12 characters are, as bearer_token_prefix.


Authentication Methods

  • Name
    Secure
    Description

    Signs each request using ECDSA P-256 + SHA256. Provides integrity, timestamp validation, and replay protection. Recommended for server-side and SDK integrations where code can compute a signature per request.

  • Name
    Simple
    Description

    Sends a static {public}:{private} key pair. The right choice for any client where you control the Authorization header — Claude Code, Cursor, custom clients, our SDKs. Pair it with a dedicated, narrowly scoped Flux key.

  • Name
    Bearer
    Description

    Sends an opaque fxk_ token bound to a Flux API key. For hosted connectors that cannot choose a scheme — they take a token field and always send Authorization: Bearer <token>. The token identifies the key; the key's role and grants are unchanged.


Secure Authentication

Secure authentication verifies a signature built from the request path, body hash, and timestamp.

Because each signature covers the specific request body and timestamp, Secure signatures are per-request and cannot be pre-computed into a static header (for example, an MCP client config that only replays fixed headers). For those clients, use Simple authentication instead.

Required Headers

  • Name
    Authorization
    Type
    string
    Required
    required
    Description

    Secure <public_key>:<signature> where <signature> is Base64-encoded ECDSA output.

  • Name
    Date
    Type
    string
    Required
    required
    Description

    ISO 8601 UTC timestamp, e.g., 2025-01-12T08:15:30Z. Requests older than 15 minutes are rejected.

Data to Sign

  1. Compute the SHA-256 hash of the request body. Use the empty string when no body is present (e3b0c442…b855 in hex).
  2. Concatenate the pieces:
data_to_sign = "<request_path>|<body_hash>|<timestamp>"
  • request_path – The exact path sent to Flux API, including the API prefix and resource path (e.g., /blog-api/articles/_search). Do not include the protocol or host.
  • body_hash – Lowercase hex digest of the SHA-256 hash.
  • timestamp – Same value as the Date header.
  1. Sign data_to_sign with your private key using ECDSA over the P-256 curve and SHA-256. Encode the signature in Base64 and include it in the Authorization header.
import crypto from 'crypto';
import fetch from 'node-fetch';

const publicKey = process.env.FLUX_PUBLIC_KEY;
const privateKey = process.env.FLUX_PRIVATE_KEY; // Base64 DER
const uri = '/blog-api/articles/_search';
const body = JSON.stringify({
  where: {
    $: { all_of: [{ status__eq: 'published' }] }
  }
});

const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
const dataToSign = `${uri}|${bodyHash}|${timestamp}`;

const sign = crypto.createSign('SHA256');
sign.update(dataToSign);
const signature = sign.sign({
  key: Buffer.from(privateKey, 'base64'),
  format: 'der',
  type: 'pkcs8'
}).toString('base64');

const headers = {
  Authorization: `Secure ${publicKey}:${signature}`,
  Date: timestamp,
  'Content-Type': 'application/json'
};

const response = await fetch(`https://7c9h4pwu.fxns.io${uri}`, {
  method: 'POST',
  headers,
  body
});
console.log(await response.json());

Simple Authentication

Simple authentication sends a static public/private key pair in a single header, exactly like a bearer token:

Authorization: Simple <public_key>:<private_key>

The header never changes between requests, so it works in any client that can send a static Authorization header.

When to use it

Use Simple authentication wherever you write the Authorization header yourself — Claude Code, Claude Desktop, Cursor, agent frameworks, and custom clients. These replay a fixed header on every call and cannot compute a per-request signature, so Secure authentication is not an option there.

It is not an option for a hosted connector that only accepts a token value and sends it as Authorization: Bearer <token> — the scheme is fixed by the connector, so a Simple ... header can never be produced. Use a Bearer token there.

Server-side code and SDK integrations that can sign each request should prefer Secure authentication, which adds integrity, timestamp validation, and replay protection.

Security note

Simple authentication does not sign requests: the same static credential is presented on every call. Mitigate this the way you would any long-lived API token:

  • Scope the key. Issue a dedicated Flux API key limited to only the APIs and collections the agent needs.
  • Always use TLS. Send the header only over HTTPS so the credential is never exposed in transit.
  • Rotate and revoke. Revoke a key immediately if it leaks, and rotate keys periodically.
  • Cap spend. Set a spend cap on the key so a compromised credential cannot run up unbounded usage.

Bearer Token Authentication

A bearer token is an opaque credential bound to an existing Flux API key:

Authorization: Bearer fxk_A7fQ2mXe...

It exists for clients that give you a single token field and always send it with the Bearer scheme — most notably the Claude API MCP connector (mcp_servers[].authorization_token). Those clients cannot send Simple or Secure at all, which previously left every authentication-required Flux API unreachable from them.

What it changes, and what it does not

  • Name
    Identifies a key
    Description

    The token resolves to the Flux API key it was issued for. Role, grants, and per-collection allowed_methods are exactly the key's own — a token confers nothing extra and restricts nothing.

  • Name
    Independent of the key pair
    Description

    Issuing, re-issuing or revoking a token leaves public_key, secret_key and role untouched. Simple and Secure keep working throughout.

  • Name
    One per key
    Description

    Re-issuing replaces the previous token. There is no list of tokens to manage.

  • Name
    Shown once
    Description

    Only a hash is stored. The plaintext appears once, in the issue response — the same contract secret_key already has. A lost token is re-issued, not recovered.

Issue, rotate and revoke

Tokens are managed through the Management API — see Flux API Keys → Bearer token.

# Issue (or replace) the token for a key
curl -X POST https://api.foxnose.net/v1/7c9h4pwu/permissions/flux-api/api-keys/dw2qC5qRwxuZ/bearer-token/ \
  -H "Authorization: Bearer $MANAGEMENT_ACCESS_TOKEN"

# Revoke it. The key and its role keep working.
curl -X DELETE https://api.foxnose.net/v1/7c9h4pwu/permissions/flux-api/api-keys/dw2qC5qRwxuZ/bearer-token/ \
  -H "Authorization: Bearer $MANAGEMENT_ACCESS_TOKEN"

Re-issuing is how you cut off a connector without disturbing anything else. The previous token stops working, while every integration using the key's public/private pair carries on. That is the whole reason the token is a separate credential rather than a field on the key.

Security note

The same considerations as Simple apply — the token is a long-lived static credential. In addition:

  • Issue one only when a client needs it. A key has no token until you ask for one.
  • Re-issue on suspicion. It is cheap and touches nothing else.
  • Recognise it in logs and config. The fxk_ prefix makes a leaked token identifiable at a glance; the dashboard and API show only the first 12 characters (bearer_token_prefix).

Error Responses

When authentication fails, the Flux API returns 401 with JSON content similar to:

{
  "message": "Invalid signature",
  "error_code": "authentication_required",
  "detail": null
}

Common reasons:

  • Missing Authorization or Date header.
  • Incorrect path or body hash used during signature generation.
  • Timestamp outside the ±15 minute window.
  • Keys revoked or not authorized for the requested API prefix.
  • A Bearer credential that is not an fxk_ token (for example a JWT meant for the Management API).

Verify all inputs before retrying to avoid throttling.

WWW-Authenticate

Every 401 carries a challenge listing the schemes a client can send a header for:

WWW-Authenticate: Simple realm="flux-api", Secure realm="flux-api"

Bearer is accepted but is deliberately absent from that list. The MCP specification tells a client that receives a Bearer challenge to begin OAuth discovery at /.well-known/oauth-protected-resource. Flux does not serve that document yet, so advertising Bearer would replace a clear 401 with a failed discovery round-trip. Bearer tokens work regardless of what the challenge says; this page is what tells you so.

The header will list Bearer — with a resource_metadata parameter — once OAuth support ships.

Was this page helpful?