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:
- Top up the balance with USDC through x402. The first payment from a wallet creates a Fluence account bound to that wallet.
- Sign in with the same wallet (Sign-In with Ethereum) to get an access token.
- Call the Fluence API with that token.
- 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, token0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. 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/evmfor EVM payments) speak v2. The unscopedx402-fetchandx402-axiospackages 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
| Parameter | Value |
|---|---|
| Minimum top-up | 10 USD |
| Maximum top-up | 1000 USD per request |
| Precision | Whole cents (at most two decimal places) |
1 USDC is credited as 1 USD.
Endpoints
Base URL: https://api.fluence.dev
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /v2/x402/payment-info?amountUsd=N | None | Returns the x402 payment challenge for N USD without paying |
POST | /v2/x402/top-up?amountUsd=N | Optional | Paid endpoint: answers 402 Payment Required without a payment, settles and credits N USD with one |
GET | /v1/auth/siwe/nonce | None | Issues a single-use sign-in nonce |
POST | /v1/auth/siwe | None | Exchanges a signed Sign-In with Ethereum message for an access token and a refresh token |
POST | /auth/refresh | None | Exchanges a refresh token for a new access token |
GET | /v2/users/balances | Bearer or API key | Your balance |
GET | /v2/top-ups | Bearer or API key | Your top-ups, including x402 ones |
GET | /v1/users/me | Bearer or API key | Your account and the permissions it has |
POST | /v1/api_keys | Bearer or API key | Creates 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:
| Status | Meaning | What to do |
|---|---|---|
400 | Amount out of range or with more than two decimal places, or a malformed payment | Fix the request |
402 | Payment required, or the payment was rejected (for example, not enough USDC). The error field of the challenge says why | Fix the cause and pay again |
409 | The payment was submitted and is under review | Do not sign a new payment — the transfer may already be on chain. Contact support with the payer address |
503 | Top-ups are unavailable, or the payment did not reach the facilitator. Nothing was charged | Retry later |
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
domainmust be exactlyapi.fluence.dev; any other domain is rejected. - A nonce works once and expires 10 minutes after it is issued (see
expiresAtin the nonce response). - Send the message exactly as signed.
userDataholds the accountidand a placeholder emailwallet-<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/melists them inpermissions. A call outside the key's scopes returns403.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:
- Pick a cluster and a VM configuration:
GET /v1/clusters/resources. - Check the price:
GET /v1/prices/vm, orPOST /v1/prices/costfor a total. - Pick an OS image and its login user:
GET /v1/storages/default_images. - Register your SSH public key:
POST /v1/ssh_keys(access token). - Create the VM with a boot disk and a public IP:
POST /v2/vms. - Poll
GET /v2/vms/<vm_id>?expand=publicIpuntilstatusislaunched(a few minutes), thenssh <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/balancesand 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.