Buyer quickstart
Arcloak serves open models through an OpenAI-compatible endpoint at https://api.arcloak.net. There is no signup and no API key: every request is paid from your own wallet in USDC on Arc through x402 upto. You authorise a ceiling, the answer streams, and you pay only for the tokens that were delivered.
What you need
- A wallet on Arc mainnet (chain id
5042) holding USDC. USDC is also Arc's gas token, so the same balance pays for the one approve transaction. - For code: Node 24 and the packages
@x402/fetch,@x402/evmandviem. - For the browser: an injected wallet such as MetaMask or Rabby. The playground adds the Arc network to it for you.
1. See the payment terms
An unpaid request is refused with 402 Payment Required. The PAYMENT-REQUIRED response header carries the terms as base64-encoded JSON: the scheme (upto), the network (eip155:5042), the asset (USDC), the ceiling in 6-decimal units, and the facilitator.
curl -i https://api.arcloak.net/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"llama3.2:3b","messages":[{"role":"user","content":"Say hello from Arc."}],"max_tokens":64,"stream":true}'2. Approve Permit2 once
upto payments move USDC through Permit2 (0x000000000022D473030F116dDEE9F6B43aC78BA3). Approve it once per payer. Every settlement draws the allowance down, so approve again when it runs low. The playground does this step for you whenever the allowance is below a request's ceiling.
import { createWalletClient, defineChain, erc20Abi, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
const arc = defineChain({ id: 5042, name: 'Arc', nativeCurrency: { name: 'USDC', symbol: 'USDC', decimals: 18 }, rpcUrls: { default: { http: ['https://rpc.mainnet.arc.io'] } } })
const account = privateKeyToAccount(process.env.PAYER_PRIVATE_KEY as `0x${string}`)
const wallet = createWalletClient({ account, chain: arc, transport: http() })
// Once per payer: let Permit2 move up to 2 USDC. Settlements draw it down; approve again when it runs low.
const hash = await wallet.writeContract({ address: '0x3600000000000000000000000000000000000000', abi: erc20Abi, functionName: 'approve', args: ['0x000000000022D473030F116dDEE9F6B43aC78BA3', 2000000n] })3. Pay and stream
@x402/fetch answers the 402 for you: it signs the ceiling with your key and retries with a PAYMENT-SIGNATURE header. The spend controls keep the client from signing anything but Arc USDC, up to the per-request cap.
import { wrapFetchWithPayment, x402Client } from '@x402/fetch'
import { UptoEvmScheme } from '@x402/evm/upto/client'
import { privateKeyToAccount } from 'viem/accounts'
const signer = privateKeyToAccount(process.env.PAYER_PRIVATE_KEY as `0x${string}`)
const client = new x402Client()
.register('eip155:5042', new UptoEvmScheme(signer))
.setSpendControls({ allowedAssets: [{ network: 'eip155:5042', asset: '0x3600000000000000000000000000000000000000', maxAmountPerPayment: '2000000' }] })
const paidFetch = wrapFetchWithPayment(fetch, client)
const res = await paidFetch('https://api.arcloak.net/v1/chat/completions', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({"model":"llama3.2:3b","messages":[{"role":"user","content":"Say hello from Arc."}],"max_tokens":64,"stream":true}),
})
// res.body is text/event-stream: answer chunks, a usage chunk, then
// {"object":"x402.payment","transaction":"0x…","amount":"…","network":"eip155:5042"} and [DONE].The response is a text/event-stream in the OpenAI chunk format. After the last answer chunk and the usage chunk, one more event carries the settlement:
{"object":"x402.payment","transaction":"0x…","amount":"3","network":"eip155:5042"}
amount is what was actually charged, in 6-decimal units (3 is 0.000003 USDC). Look the transaction up on the Arc explorer. The stream then ends with [DONE].
Good to know
- Send
"stream": falseto get one JSON response instead. The settlement transaction is then in theX-Payment-Transactionresponse header. max_tokensdefaults to 512 and is lowered when needed so the ceiling fits under the per-request cap. See Pricing.- If the node stops mid-answer you pay only for what was delivered. If nothing was delivered, the authorization is cancelled and nothing is charged.
- Every route and error code is in the API reference. How the payment works is in How x402 upto works.