Docs

Three ways to pay for a run: an agent API key, an x402 payment per request, or the MCP server.

Quick start

  1. Connect your wallet and top up with USDG on Robinhood Chain Testnet from Billing.
  2. Create an agent under Agents. Copy its key (shown once).
  3. Call the API with the key, or plug it into an MCP client.

REST API

Charged to the agent owner's balance within the agent's caps. Failed runs are refunded.

curl -X POST https://veltrix.codes/api/v1/run/flux-schnell \
  -H "Authorization: Bearer vx_live_..." \
  -H "content-type: application/json" \
  -d '{"prompt":"a red fox in snow, 35mm","image_size":"landscape_4_3"}'
{
  "id": "job_…",
  "status": "succeeded",
  "chargedMicro": 4200,            // micro-USD, 4200 = $0.0042
  "outputs": [{ "url": "https://…", "contentType": "image/jpeg", "kind": "image" }],
  "balanceMicro": 9995800
}
  • GET /api/v1/models
  • POST /api/v1/run/:model
  • GET /api/v1/jobs/:id
  • GET /api/v1/balance

x402 payments

Call /api/v1/run/:model without an API key and Veltrix answers 402 with the exact price. Sign one EIP-3009 TransferWithAuthorization for USDG and resend the request with an X-PAYMENT header. Veltrix submits it onchain and pays the gas. Network id: eip155:46630. If the run fails, the payment stays as Veltrix credit for your wallet.

import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.PK);
const url = "https://veltrix.codes/api/v1/run/flux-schnell";
const body = JSON.stringify({ prompt: "a red fox in snow" });

// 1. ask for the price
const r1 = await fetch(url, { method: "POST", body, headers: { "content-type": "application/json" } });
const { accepts: [req] } = await r1.json();               // status 402

// 2. sign the authorization
const now = Math.floor(Date.now() / 1000);
const authorization = {
  from: account.address, to: req.payTo, value: req.maxAmountRequired,
  validAfter: "0", validBefore: String(now + 300),
  nonce: "0x" + crypto.getRandomValues(new Uint8Array(32)).reduce((s, b) => s + b.toString(16).padStart(2, "0"), ""),
};
const signature = await account.signTypedData({
  domain: { name: req.extra.name, version: req.extra.version, chainId: 46630, verifyingContract: req.asset },
  types: { TransferWithAuthorization: [
    { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" },
    { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" } ] },
  primaryType: "TransferWithAuthorization",
  message: { ...authorization, value: BigInt(authorization.value), validAfter: 0n, validBefore: BigInt(authorization.validBefore) },
});

// 3. pay and run
const payment = btoa(JSON.stringify({ x402Version: 1, scheme: "exact", network: req.network, payload: { signature, authorization } }));
const r2 = await fetch(url, { method: "POST", body, headers: { "content-type": "application/json", "X-PAYMENT": payment } });
console.log(await r2.json());

MCP

Stateless Streamable HTTP. Tools: list_models, quote, generate, get_balance, get_job.

{
  "mcpServers": {
    "veltrix": {
      "type": "http",
      "url": "https://veltrix.codes/api/mcp",
      "headers": { "Authorization": "Bearer vx_live_..." }
    }
  }
}
claude mcp add --transport http veltrix https://veltrix.codes/api/mcp --header "Authorization: Bearer vx_live_..."

Model inputs

flux-schnell
prompt · string ≤ 2000 · required
image_size · landscape_4_3 | landscape_16_9 | portrait_4_3 | portrait_16_9 | square | square_hd
num_images · int 1–4
seed · int 0–2147483647
z-image-turbo
prompt · string ≤ 2000 · required
image_size · landscape_4_3 | landscape_16_9 | portrait_4_3 | portrait_16_9 | square | square_hd
num_images · int 1–4
seed · int 0–2147483647
wan-2.2-5b
prompt · string ≤ 1500 · required
minimax-speech
text · string ≤ 5000 · required
trellis
image_url · https URL or data URI · required

Errors

{ "error": { "code": "daily_cap_reached", "message": "Daily cap $5.00 would be exceeded" } }

400 invalid_input        401 invalid_api_key      402 insufficient_balance | daily_cap_reached | budget_exhausted
403 agent_paused | model_not_allowed               404 model_not_found      429 rate_limited
502 run failed (refunded)                          503 models_not_configured | x402_not_configured