Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

x402 Payments

Inkd uses x402 — an open HTTP payment protocol built on the 402 Payment Required status code. Agents and developers pay for API calls directly with USDC. No API keys, no OAuth, no accounts.


How it works

Request without payment

Your client calls a paid endpoint. The server responds with 402 Payment Required and payment instructions.

POST /v1/projects HTTP/1.1
Content-Type: application/json
 
{ "name": "my-agent", ... }
HTTP/1.1 402 Payment Required
X-Payment-Required: {"amount":"100000","token":"USDC","network":"base"}

Sign the authorization

Your client signs an EIP-3009 transferWithAuthorization — a gasless USDC transfer from your wallet to the Treasury contract.

const authorization = await signTransferAuthorization({
  from:     yourWallet,
  to:       TREASURY_ADDRESS,
  value:    100000n,           // 0.10 USDC (6 decimals)
  validAfter:  0n,
  validBefore: BigInt(Math.floor(Date.now() / 1000) + 300),
  nonce:    randomBytes32(),
})

Retry with payment header

The client retries the request with the signed authorization in the X-PAYMENT header.

POST /v1/projects HTTP/1.1
X-PAYMENT: <base64-encoded EIP-3009 authorization>
Content-Type: application/json
 
{ "name": "my-agent", ... }

Server executes and responds

The server verifies the signature, executes the USDC transfer on-chain, and processes the request.

HTTP/1.1 201 Created
 
{ "projectId": "10", "txHash": "0xabc...", ... }

Why x402?

For AI agents

Agents hold wallets. x402 lets them pay directly — no human needs to create an API key, renew a subscription, or handle rate limits. A wallet with USDC is all that's required.

// No configuration. Just a wallet.
const client = new ProjectsClient({ privateKey: process.env.WALLET_KEY })
await client.createProject({ name: "my-agent", ... })

For developers

x402 is standard HTTP. Any language that can make HTTP requests and sign EIP-3009 authorizations can use the Inkd API.


EIP-3009

Inkd uses EIP-3009 transferWithAuthorization, which is native to USDC on Base.

Key properties:

  • Gasless for the payer — the API server submits the transaction and pays gas
  • Atomic — payment and API execution happen in the same server-side flow
  • Replay-safe — each authorization has a unique nonce and expiry

USDC EIP-712 domain on Base:

name:              "USD Coin"
version:           "2"
chainId:           8453
verifyingContract: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

Pricing

Prices are dynamic — charged based on actual resource cost plus a 20% protocol markup.

OperationCost
List projectsFree
Get projectFree
List versionsFree
Create project$0.10 USDC (min)
Push versionArweave storage + 20% markup (min $0.10)
Upload to ArweaveFree (cost included in version push fee)

For version pushes, the exact fee depends on file size. Use GET /v1/projects/estimate?bytes=N to get a price estimate before paying.


Revenue flow

Every payment flows through the protocol:

Agent pays USDC


InkdTreasury.settle(total, arweaveCost)

       ├── arweaveCost ──────────────► arweaveWallet
       │                               (covers Arweave storage)

       └── markup (20%)

              ├── 50% ──────────────► InkdBuyback
              │                       (auto-buys $INKD at $50 threshold)

              └── 50% ──────────────► Treasury Safe

Using x402 in your app

With the Inkd SDK (recommended)

The SDK handles x402 automatically:

import { ProjectsClient } from "@inkd/sdk"
 
const client = new ProjectsClient({
  privateKey: process.env.INKD_PRIVATE_KEY,
})
 
// Payment happens automatically
const project = await client.createProject({ name: "my-agent" })

With @x402/fetch

npm install @x402/fetch @x402/evm viem
import { wrapFetchWithPayment } from "@x402/fetch"
import { viemAdapter } from "@x402/evm"
import { createWalletClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { base } from "viem/chains"
 
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const wallet  = createWalletClient({ account, chain: base, transport: http() })
 
const fetchWithPayment = wrapFetchWithPayment(fetch, viemAdapter(wallet))
 
const res = await fetchWithPayment("https://api.inkdprotocol.com/v1/projects", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "my-agent", description: "...", license: "MIT", isPublic: true }),
})

Raw (any language)

  1. Make the initial request (no auth)
  2. Parse the 402 response to get payment amount and recipient
  3. Sign EIP-3009 transferWithAuthorization with your wallet
  4. Base64-encode the signed payload
  5. Retry with X-PAYMENT: <encoded> header