Zion

Client Integration (AI Agents)

How AI Agents interact with the Zion Protocol.

Client-Side Integration

The Zion SDK allows AI Agents and developers to easily monetize their APIs using the Zion X-402 Facilitator. It handles the HTTP 402 Payment Required protocol, standardized payment requests, verification, and settlement on the Solana blockchain.

[!NOTE] Subscription Model (0% Platform Fee) Zion operates on a subscription-based model with 0% platform fees. Developers receive 100% of payments directly. Usage is tracked for subscription tier limits (Free: 10,000 txn/month).

Usage (Client-Side / AI Agent)

The SDK provides a ZionAgent class that makes integrating paid APIs as simple as a standard fetch call. It automatically handles the entire 402 Payment Required flow, including:

  1. Detecting 402 responses
  2. Creating and signing the transaction
  3. Submitting to the Solana blockchain
  4. Retrying the request with payment proof

1. Initialize the Agent

Initialize the agent with your Solana private key.

import { ZionAgent } from "@ziongateway/sdk";

const agent = new ZionAgent({
    privateKey: process.env.SOLANA_PRIVATE_KEY!, // Base58 encoded
    rpcUrl: "https://api.mainnet-beta.solana.com" // Optional
});

2. Make Requests

Use agent.fetch() exactly like the standard fetch API. If payment is required, it happens automatically in the background.

// Just call fetch - payment happens automatically if needed
const response = await agent.fetch("https://api.merchant.com/premium-data");

if (response.ok) {
    const data = await response.json();
    console.log("Received paid content:", data);
}

// Optional: Check if a payment was made
if (response.paymentMade) {
    console.log(`Paid via tx: ${response.txSignature}`);
}

That's it! No transaction building, no buffer decoding, no manual retries.


Manual Integration (Advanced)

If you prefer to build the transaction manually (or are using a different language), follow these steps when you receive a 402 Payment Required response.

1. Handling the 402 Response

The server returns payment requirements in x402 v2 format:

{
  "scheme": "exact",
  "payTo": "FjK...",       // Developer's Wallet
  "amount": "100000",      // Atomic units (0.1 USDC)
  "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC Mint
  "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", // CAIP-2 format
  "maxTimeoutSeconds": 3600
}

[!IMPORTANT] x402 v2 Changes

  • Field name changed: recipientpayTo
  • Network uses CAIP-2 format: solana:<genesisHash>
  • Payment goes 100% to developer (no treasury split)

2. Creating the Transaction

Transfer the exact amount to the payTo address. No fee splitting required.

import { PaymentBuilder } from "@ziongateway/sdk";

const instructions = await PaymentBuilder.createExactInstructions({
    sender: wallet.publicKey.toBase58(),
    recipient: requirements.payTo,
    amount: requirements.amount,
    asset: requirements.asset,
    connection
});

const tx = new Transaction().add(...instructions);
const signature = await wallet.sendTransaction(tx, connection);
await connection.confirmTransaction(signature, "confirmed");

3. Creating the Payment Header

After confirmation, create the payment header and retry the request. The header is a base64-encoded JSON object containing the transaction signature.

const paymentHeader = PaymentBuilder.createHeader({
    transaction: signature,
    resource: "https://api.example.com/endpoint",
    paymentRequirements: requirements
});

const response = await fetch("/api/protected", {
    headers: { "X-Payment": paymentHeader }
});

Header Payload Structure (x402 v2):

{
  "x402Version": 2,
  "resource": {
    "url": "https://api.example.com/endpoint"
  },
  "accepted": {
    "scheme": "exact",
    "payTo": "DeveloperWalletAddress",
    "amount": "100000",
    "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
    "maxTimeoutSeconds": 3600
  },
  "payload": {
    "transaction": "base64EncodedTxOrSignature"
  }
}

Manual Integration (Without SDK)

If you don't want to use the Zion SDK, you can manually build the transaction using @solana/web3.js and @solana/spl-token.

import { Connection, Transaction, PublicKey } from "@solana/web3.js";
import { 
    createTransferInstruction, 
    getAssociatedTokenAddress 
} from "@solana/spl-token";

async function payAndFetch(url: string, wallet: any) {
    // 1. Initial Request
    let response = await fetch(url);

    if (response.status === 402) {
        // Parse Payment-Required header (x402 v2)
        const paymentRequiredHeader = response.headers.get("Payment-Required");
        const requirements = JSON.parse(atob(paymentRequiredHeader));
        
        // 2. Build Transaction (100% to developer)
        const connection = new Connection("https://api.mainnet-beta.solana.com");
        const transaction = new Transaction();
        const assetMint = new PublicKey(requirements.asset);
        const senderPubkey = wallet.publicKey;

        // Get ATAs
        const sourceATA = await getAssociatedTokenAddress(assetMint, senderPubkey);
        const recipientPubkey = new PublicKey(requirements.payTo);
        const recipientATA = await getAssociatedTokenAddress(assetMint, recipientPubkey);

        // Single transfer: 100% to developer (no fee split)
        transaction.add(
            createTransferInstruction(
                sourceATA, 
                recipientATA, 
                senderPubkey, 
                BigInt(requirements.amount)
            )
        );

        // 3. Sign & Send
        const { blockhash } = await connection.getLatestBlockhash();
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = senderPubkey;

        const signature = await wallet.sendTransaction(transaction, connection);
        await connection.confirmTransaction(signature, "confirmed");

        // 4. Create x402 v2 Payment Header
        const paymentPayload = {
            x402Version: 2,
            resource: { url },
            accepted: requirements,
            payload: { transaction: signature }
        };
        const paymentHeader = btoa(JSON.stringify(paymentPayload));

        // 5. Retry with X-Payment header
        response = await fetch(url, {
            headers: { "X-Payment": paymentHeader }
        });
    }

    return response.json();
}

On this page