Appearance
Frontend Integration
The gateway is a credit-based LLM inference API. Users fund a USDC 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.
Base URL
| Environment | Origin |
|---|---|
| Local dev | http://localhost:8080 |
| Staging | https://agent-router.gaib.cloud |
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 gateway) |
Top Up USDC Balance
Top-up uses the x402 protocol. The client pays USDC on Base before the balance is credited.
Step 1 -- Discover payment requirements
http
POST /v1/topup
Content-Type: application/json
{ "amount": 5 }Response (no payment header present):
json
// HTTP 402
{
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"maxAmountRequired": "5000000",
"asset": "0x...",
"payTo": "0x...",
"extra": { "name": "USDC", "decimals": 6 }
}]
}Step 2 -- Sign ERC-3009 and send payment
ts
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()
// balance_usdc: total balance in micro-USDC (6 decimals)
// $1 = 1_000_000 micro-USDCSIWE Authentication
SIWE proves wallet ownership for API key management. No server-side session -- every call needs a fresh signed message (valid 5 minutes).
ts
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: 1,
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,
})TIP
Generate a fresh issuedAt timestamp immediately before each API call. The server rejects SIWE messages older than 5 minutes.
API Key Management
API keys are sk-<64 hex chars> tokens. Returned once on creation -- store immediately.
Create a key
http
POST /v1/auth/keys
Content-Type: application/json
{
"message": "<siwe message string>",
"signature": "<hex signature>",
"label": "my-app"
}Response 201:
json
{
"id": 1,
"key": "sk-a3f9...",
"label": "my-app",
"created_at": "2026-04-07T..."
}List keys
http
GET /v1/auth/keys?message=<siwe>&signature=<sig>Revoke a key
http
DELETE /v1/auth/keys/:key_id
Content-Type: application/json
{
"message": "<siwe message string>",
"signature": "<hex signature>"
}Chat Completions
OpenAI-compatible. Pass the API key as a Bearer token.
http
POST /v1/chat/completions
Authorization: Bearer sk-a3f9...
Content-Type: application/json
{
"model": "gemini/gemini-2.5-flash",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Hello!" }
],
"max_tokens": 512,
"stream": false
}Using the OpenAI SDK
ts
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)
Set "stream": true for server-sent events:
ts
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) | balance_usdc / 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 |