Skip to main content
An AI agent that controls a wallet on Base can mint its own Venice API key with no human in the loop. The agent acquires VVV, stakes it, signs a short-lived validation token issued by Venice, and posts the signed token back to receive a fresh API key tied to the staking wallet. This guide walks through the full flow end to end and covers the funding options for actually paying for inference once the key is minted.

Prerequisites

  • An EVM wallet on Base controlled by the agent (private key in an env var or secret manager).
  • A small amount of ETH on Base for gas (staking is two transactions: approve then stake).
  • Any non-zero amount of VVV to stake. The minting endpoint requires only that the wallet has a non-zero sVVV balance, so 1 VVV is enough to mint a key. See Paying for inference for what you need to actually call paid endpoints.
Use a dedicated agent wallet rather than a treasury wallet. The wallet’s private key signs every Venice token request, so its blast radius should be small.

Steps

1

Acquire VVV

Send VVV to the agent’s wallet, or have the agent swap on a DEX such as Aerodrome or Uniswap.VVV token contract on Base: 0xacfE6019Ed1A7Dc6f7B508C02d1b04ec88cC21bf
2

Stake VVV with Venice

Stake the VVV in the Venice Staking Smart Contract at 0x321b7ff75154472B18EDb199033fF4D116F340Ff. This is two transactions:
  1. approve(spender, amount) on the VVV token, where spender is the staking contract.
  2. stake(amount) on the staking contract.
Smart Contract Staking
When the second transaction confirms, the wallet’s VVV balance decreases and its sVVV balance increases by the same amount. The minting endpoint reads the sVVV balance to confirm the wallet is staked.
3

Request a validation token

Call GET /api/v1/api_keys/generate_web3_key to get a short-lived token signed by Venice. The endpoint is unauthenticated.
curl --request GET \
  --url https://api.venice.ai/api/v1/api_keys/generate_web3_key
The response contains a token field. The token expires 15 minutes after issuance, so sign and submit it well before then.
4

Sign the token with the staking wallet

Sign the raw token string with the wallet that holds the staked VVV. This is a standard personal_sign over the token bytes. Both ethers.Wallet.signMessage(token) and viem’s account.signMessage({ message: token }) produce the correct signature.
5

Mint the API key

POST the address, signature, and token to the same endpoint, along with the type of key you want.
curl --request POST \
  --url https://api.venice.ai/api/v1/api_keys/generate_web3_key \
  --header 'Content-Type: application/json' \
  --data '{
    "address": "<wallet address>",
    "signature": "<signed token>",
    "token": "<unsigned token>",
    "apiKeyType": "INFERENCE",
    "description": "Agent key minted on <date>"
  }'
Required fields: address, signature, token, apiKeyType (INFERENCE or ADMIN).Optional fields: description, expiresAt, consumptionLimit (caps total spend on this key, denominated in usd, vcu, or diem).On success the response contains the minted apiKey string. Store it in the agent’s secret store and use it as a normal Bearer token (Authorization: Bearer <key>).

End-to-end example

The example below uses a real wallet from an environment variable rather than a randomly generated one. A random wallet has no staked VVV and the mint will be rejected with the Wallet has no staked VVV on Base error.
import { ethers } from "ethers"

const wallet = new ethers.Wallet(process.env.WALLET_PRIVATE_KEY!)
const address = wallet.address

const tokenResponse = await fetch("https://api.venice.ai/api/v1/api_keys/generate_web3_key")
const { data: { token } } = await tokenResponse.json()

const signature = await wallet.signMessage(token)

const mintResponse = await fetch("https://api.venice.ai/api/v1/api_keys/generate_web3_key", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    address,
    signature,
    token,
    apiKeyType: "INFERENCE",
    description: "Agent key",
  }),
})

const result = await mintResponse.json()
if (!mintResponse.ok) {
  throw new Error(`Mint failed: ${result.error}`)
}

console.log("Minted key:", result.data.apiKey)

Error reference

The endpoint returns specific, actionable error messages. Map these in the agent so it can decide whether to retry, request a new token, or stop.
StatusError message containsWhat it meansWhat to do
400Invalid wallet addressThe address field is not a valid EVM address.Fix the address and resubmit.
400JWT has expiredThe validation token expired before you signed and submitted it.Request a new token, sign it, and submit immediately.
400JWT signature is invalidThe token was not signed by Venice (likely tampered with or fabricated).Always use a fresh token from the GET endpoint.
400JWT claims are invalidThe token’s issuer or audience does not match what Venice expects.Use the unmodified token returned by the GET endpoint.
400JWT is malformedThe submitted token is not a JWT.Ensure you are sending the exact token string returned by the GET endpoint.
400Wallet signature does not matchThe signature does not match the address for the given token.Sign the raw token bytes with the wallet that owns address.
400Could not verify wallet signatureRPC call to verify the signature failed (transient).Retry with backoff.
400Wallet has no staked VVV on BaseThe wallet has zero sVVV balance.Stake VVV first, then retry.

Paying for inference

Minting a key and being able to call paid endpoints with it are two separate things. A freshly minted key authenticates correctly but cannot call paid endpoints (such as /chat/completions) until the wallet’s account has a spendable balance. The minted key can spend from the user account in this priority order: DIEM, then bundled credits, then USD.
Funding sourceAutonomous?How
DIEM from VVV stakingYesThe wallet’s daily DIEM allocation is proportional to its share of the staking pool. The account needs at least 0.1 staked DIEM for any DIEM to be spendable. Larger stakes earn proportionally more daily DIEM, refreshed each epoch (00:00 UTC).
USD via StripeNo (browser)Sign into venice.ai with the same wallet (Sign-In-With-Ethereum). The dashboard finds the existing user record. Add credits in Settings, API.
Coinbase crypto subscriptionNo (browser)Same wallet sign-in, then subscribe through the dashboard. The flow redirects to Coinbase Commerce for the actual payment, so it cannot be driven from a script.
Coinbase onrampNo (browser)Same wallet sign-in, then use the onramp widget in the dashboard. Hosted on Coinbase’s UI.
If the agent needs a fully crypto-native, headless funding path, the cleanest options are:
  1. Stake more VVV so the daily DIEM allocation covers the agent’s spend. The minted key picks this up automatically.
  2. Use the x402 wallet flow instead of the API key. With x402 the agent signs a SIWE message per request, tops up directly with USDC on Base via POST /api/v1/x402/top-up, and pays per request. The x402 USDC balance is wallet-bound, not user-bound, so it does not show up as balance for the minted Bearer key, but it does let the same wallet pay for inference programmatically.

Crypto and Agents

Use Venice as both the model provider and the blockchain RPC layer for autonomous agents.

x402 Wallet Authentication

Pay per request with USDC on Base, no API key required.

Generate Web3 API Key Endpoint

Endpoint reference for the mint endpoint.

Standard API Key Guide

For users who prefer to mint a key from the dashboard.