Skip to main content

Pay with x402 (for agents)

Fluence accepts balance top-ups over the x402 protocol: an HTTP client pays USDC on Base and the amount is credited to a Fluence balance in the same request. No signup, email or console visit is needed — the paying wallet is the account. This is the shortest path for an AI agent or a script to get compute on Fluence:

  1. Top up the balance with USDC through x402. The first payment from a wallet creates a Fluence account bound to that wallet.
  2. Sign in with the same wallet (Sign-In with Ethereum) to get an access token.
  3. Call the Fluence API with that token.
  4. Rent compute with the funded balance: a CPU VM or a GPU instance.

Requirements​

  • An EVM wallet controlled by a private key (an externally owned account). Smart-contract wallets cannot sign in.
  • USDC on Base mainnet: network eip155:8453, token 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. No ETH is needed for gas — the x402 facilitator submits the transfer.
  • An x402 v2 client. The @x402/* packages (@x402/fetch, @x402/axios, with @x402/evm for EVM payments) speak v2. The unscoped x402-fetch and x402-axios packages are the older v1 line and cannot read the Fluence payment challenge.

The examples below are JavaScript modules for Node.js 20 or later. Install the packages they use:

npm install @x402/fetch @x402/evm viem

Save an example as a .mjs file and run it with node, passing the wallet key in the EVM_PRIVATE_KEY environment variable.

Limits​

ParameterValue
Minimum top-up10 USD
Maximum top-up1000 USD per request
PrecisionWhole cents (at most two decimal places)

1 USDC is credited as 1 USD.

Endpoints​

Base URL: https://api.fluence.dev

MethodPathAuthDescription
GET/v2/x402/payment-info?amountUsd=NNoneReturns the x402 payment challenge for N USD without paying
POST/v2/x402/top-up?amountUsd=NOptionalPaid endpoint: answers 402 Payment Required without a payment, settles and credits N USD with one
GET/v1/auth/siwe/nonceNoneIssues a single-use sign-in nonce
POST/v1/auth/siweNoneExchanges a signed Sign-In with Ethereum message for an access token and a refresh token
POST/auth/refreshNoneExchanges a refresh token for a new access token
GET/v2/users/balancesBearer or API keyYour balance
GET/v2/top-upsBearer or API keyYour top-ups, including x402 ones
GET/v1/users/meBearer or API keyYour account and the permissions it has
POST/v1/api_keysBearer or API keyCreates an API key

For complete request and response schemas, see the API reference.

Step 1: Top up​

Wrap fetch with an x402 client and call the top-up endpoint. The client handles the 402 → sign → retry exchange.

import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);

const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
// @x402/fetch refuses to pay more than $1 per payment unless you raise the cap.
spendControls: { maxAmountPerPayment: "$10" },
});

const response = await fetchWithPayment(
"https://api.fluence.dev/v2/x402/top-up?amountUsd=10",
{ method: "POST" },
);
console.log(response.status, await response.json());

Set maxAmountPerPayment to at least the amount you top up: with the default $1 cap the client refuses to sign, and nothing is paid.

A successful top-up returns 200:

{
"amountUsd": "10",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"transaction": "0x…",
"payer": "0x…"
}

transaction is the Base transaction hash; amountUsd is a decimal string. The PAYMENT-RESPONSE header carries the x402 settlement response.

Other responses:

StatusMeaningWhat to do
400Amount out of range or with more than two decimal places, or a malformed paymentFix the request
402Payment required, or the payment was rejected (for example, not enough USDC). The error field of the challenge says whyFix the cause and pay again
409The payment was submitted and is under reviewDo not sign a new payment — the transfer may already be on chain. Contact support with the payer address
503Top-ups are unavailable, or the payment did not reach the facilitator. Nothing was chargedRetry later
tip

Already have a Fluence account? Send your API key in the X-API-KEY header (or an access token in Authorization: Bearer) with the top-up request, and the amount is credited to that account instead of a wallet account.

Step 2: Sign in with the wallet​

Request a nonce, sign a Sign-In with Ethereum message with the wallet that paid, and exchange it for an access token.

import { privateKeyToAccount } from "viem/accounts";
import { createSiweMessage } from "viem/siwe";

const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);

const { nonce } = await (await fetch("https://api.fluence.dev/v1/auth/siwe/nonce")).json();

const message = createSiweMessage({
domain: "api.fluence.dev",
address: account.address,
uri: "https://api.fluence.dev",
version: "1",
chainId: 8453,
nonce,
});
const signature = await account.signMessage({ message });

const login = await fetch("https://api.fluence.dev/v1/auth/siwe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, signature }),
});
const { accessToken, refreshToken, userData } = await login.json();
  • The message domain must be exactly api.fluence.dev; any other domain is rejected.
  • A nonce works once and expires 10 minutes after it is issued (see expiresAt in the nonce response).
  • Send the message exactly as signed.
  • userData holds the account id and a placeholder email wallet-<address>@x402.invalid; the wallet address is the account's identity.

Access tokens are short-lived. To get a new one without signing again, send the refresh token (note the snake_case field names):

curl -X POST https://api.fluence.dev/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "<REFRESH_TOKEN>"}'

The response is {"access_token": "…"}. If the refresh token has expired too, sign in again.

Step 3: Use the API​

Send the access token as a bearer token. For example, check the balance:

curl https://api.fluence.dev/v2/users/balances \
-H "Authorization: Bearer <ACCESS_TOKEN>"

Your x402 top-ups are listed by GET /v2/top-ups with provider x402.

Create an API key​

A long-running agent can create an API key once and send it in the X-API-KEY header instead of refreshing tokens:

curl -X POST https://api.fluence.dev/v1/api_keys \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "my-agent",
"scopes": ["vms:read", "vms:write"],
"expiresAt": "2027-01-01T00:00:00Z"
}'
  • name: lowercase letters, digits and hyphens, up to 25 characters.
  • scopes: what the key may do; a key can only get permissions your account has. GET /v1/users/me lists them in permissions. A call outside the key's scopes returns 403.
  • expiresAt: an RFC 3339 time in the future.

The response carries the key in value and its id. The key is shown only once — store it. Delete a key with DELETE /v1/api_keys/<id>.

Step 4: Rent a VM​

With a funded balance you can deploy a CPU VM through the API. The full sequence, with request bodies, is in CPU Cloud → Deploy a VM:

  1. Pick a cluster and a VM configuration: GET /v1/clusters/resources.
  2. Check the price: GET /v1/prices/vm, or POST /v1/prices/cost for a total.
  3. Pick an OS image and its login user: GET /v1/storages/default_images.
  4. Register your SSH public key: POST /v1/ssh_keys (access token).
  5. Create the VM with a boot disk and a public IP: POST /v2/vms.
  6. Poll GET /v2/vms/<vm_id>?expand=publicIp until status is launched (a few minutes), then ssh <username>@<expanded.publicIp.address>.

Billing for agents​

  • Resources are billed per second of use and charged from the balance as they run. Nothing is reserved up front and there are no refunds to wait for.
  • A new VM is accepted only if the balance can keep all your resources, including the new ones, running for at least 6 hours.
  • Terminating a VM (POST /v2/vms/<vm_id>/terminate) stops billing for the VM only. Delete its public IP (DELETE /v1/public_ips/<id>) and boot disk (DELETE /v1/storages/<id>) to stop paying for them.
  • A wallet account has no email address, so it gets no low-balance warnings. Watch GET /v2/users/balances and top up again before it runs out.
  • If the balance runs out and the debt stays unpaid for 3 days or exceeds 5 USD, your VMs and public IPs are terminated; disks are deleted later if the debt is still not paid.

A wallet account can rent GPU instances too; see GPU Cloud.