Integrations

Frontend integration

Build a UI with wallet connect, USDT 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 USDT → Sign in (SIWE) → Create API key → Use API key for completions

Top-up and sign-in are independent — both require a connected wallet but can happen in either order.

PurposeLibrary
Wallet connectionwagmi, RainbowKit, ConnectKit
SIWE message constructionsiwe
Viem wallet clientviem
OpenAI-compatible clientopenai (point baseURL at the gateway)

Top up USDT balance

Top-up is a plain USDT transfer on Ethereum to the collection wallet. The indexer credits the sending address, so send from the wallet the user signs in with. See Supported networks for the chain, token and rate.

import { erc20Abi } from 'viem'

const USDT_ETHEREUM = '0xdAC17F958D2ee523a2206206994597C13D831ec7'

// Never hardcode the destination — it differs per deployment.
const { gatewayWalletAddress } = await (await fetch(`${BASE_URL}/v1/config`)).json()

await walletClient.writeContract({
  address: USDT_ETHEREUM,
  abi: erc20Abi,
  functionName: 'transfer',
  args: [gatewayWalletAddress, 5_000_000n], // 5 USDT, 6 decimals
})

// Poll the balance until the credit lands.
const { balance_usdc } = await (await fetch(`${BASE_URL}/v1/balance/${address}`)).json()
// $1 = 1_000_000 micro-USD

If a transfer is not credited, submit its hash to /v1/topup/direct.

SIWE authentication

SIWE proves wallet ownership for API key management. There is no server-side session — every call needs a fresh signed message (valid 1 hour).

import { SiweMessage } from 'siwe'
import { createWalletClient, custom } from 'viem'
import { base } from 'viem/chains'

const walletClient = createWalletClient({
  chain: base,
  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, // not verified by the gateway — any chain ID works
  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 1 hour.

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-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

ValueUnitDisplay
balance_usdcmicro-USD (6 decimals)/ 1_000_000 → USD
amount in topupUSD (float)e.g. 5 = $5
promptPricePer1MTokensUSD per 1M tokens—
total_charged_usdcmicro-USD/ 1_000_000 → USD

On this page