Integrations
Frontend integration
Build a UI with wallet connect, x402 top-up, SIWE sign-in, and completions.
The gateway is a credit-based LLM inference API. Users fund a balance, authenticate with their wallet (SIWE), get an API key, and call the inference endpoint.
User flow
Connect wallet → Top up USDC → Sign in (SIWE) → Create API key → Use API key for completionsTop-up and sign-in are independent — both require a connected wallet but can happen in either order.
Recommended libraries
| Purpose | Library |
|---|---|
| Wallet connection | wagmi, RainbowKit, ConnectKit |
| SIWE message construction | siwe |
| Viem wallet client | viem |
| x402 payment | @x402/fetch or @x402/client |
| OpenAI-compatible client | openai (point baseURL at the gateway) |
Top up USDC balance
Top-up uses x402 — the client pays USDC on Base before the balance is credited.
import { withPaymentInterceptor } from '@x402/fetch'
const fetchWithPayment = withPaymentInterceptor(fetch, walletClient)
const res = await fetchWithPayment(`${BASE_URL}/v1/topup`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: 5 }),
})
const { balance_usdc, credited_usdc } = await res.json()
// $1 = 1_000_000 micro-USDCSee Top up (x402) for the underlying two-step protocol.
SIWE authentication
SIWE proves wallet ownership for API key management. There is no server-side session — every call needs a fresh signed message (valid 5 minutes).
import { SiweMessage } from 'siwe'
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'
const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum),
})
const [address] = await walletClient.getAddresses()
const siweMsg = new SiweMessage({
domain: window.location.host,
address,
uri: window.location.origin,
version: '1',
chainId: 8453, // Base mainnet
nonce: crypto.randomUUID().replace(/-/g, '').slice(0, 16),
issuedAt: new Date().toISOString(),
statement: 'Sign in to the AI Gateway',
})
const message = siweMsg.prepareMessage()
const signature = await walletClient.signMessage({ account: address, message })Generate a fresh issuedAt timestamp immediately before each API call. The server rejects SIWE messages older than 5 minutes.
Chat completions in the browser
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: `${BASE_URL}/v1`,
apiKey: 'sk-a3f9...',
dangerouslyAllowBrowser: true,
})
const response = await client.chat.completions.create({
model: 'gemini/gemini-2.5-flash',
messages: [{ role: 'user', content: 'Hello!' }],
})Streaming (SSE)
const res = await fetch(`${BASE_URL}/v1/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ model, messages, max_tokens: 512, stream: true }),
})
const reader = res.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
for (const line of chunk.split('\n')) {
if (!line.startsWith('data: ')) continue
const data = line.slice(6)
if (data === '[DONE]') break
const delta = JSON.parse(data).choices[0].delta.content ?? ''
// append delta to your UI
}
}Units & conversions
| Value | Unit | Display |
|---|---|---|
balance_usdc | micro-USDC (6 decimals) | / 1_000_000 → USD |
amount in topup | USD (float) | e.g. 5 = $5 |
promptPricePer1MTokens | USD per 1M tokens | — |
total_charged_usdc | micro-USDC | / 1_000_000 → USD |