@solinkify/gate-sdk
v0.1.1
Published
AI agent SDK — auto-pay HTTP 402 paywalls and access Solinkify Gate protected content on Solana.
Maintainers
Readme
@solinkify/gate-sdk
Ethical AI agent SDK — access creator-gated content by paying automatically on Solana.
@solinkify/gate-sdk is the client counterpart to @solinkify/gate. When an AI agent hits a 402 Payment Required wall, this SDK handles the entire payment flow automatically: parse the manifest, lock SOL into an on-chain Escrow PDA, retry the request with payment proof, receive the content, then release the payment to the creator.
No manual steps. No custodial wallets. Fully on-chain.
How It Works
Agent calls fetchProtected(url)
│
▼
GET url → 402 Payment Required + PaymentManifest
│
▼
Parse manifest (price, escrow_address, endpoint_id)
│
▼
Price ≤ maxPricePerRequest? ──No──▶ throw Error (spending cap)
│ Yes
▼
lock_payment() on-chain
SOL → Escrow PDA (Solana smart contract)
│
▼
Retry GET url
headers: X-Solinkify-Payment + X-Solinkify-Payer
│
▼
200 OK → Content received
│
▼
Agent calls release_payment() on-chain
│
┌────┴──────────────┬──────────────────┐
▼ ▼ ▼
99% → Creator 0.75% → Operator 0.25% → Admin
│
▼
Escrow PDA closed → rent refunded → AgentQuick Start
1. Install
npm install @solinkify/gate-sdk @solana/web3.js2. Load your agent keypair and fetch
import { GateClient } from '@solinkify/gate-sdk';
import { Keypair } from '@solana/web3.js';
import fs from 'fs';
// Load agent keypair (CLI wallet or managed key)
const secret = JSON.parse(fs.readFileSync('/path/to/keypair.json', 'utf-8'));
const agentKeypair = Keypair.fromSecretKey(Uint8Array.from(secret));
const client = new GateClient({
wallet: agentKeypair,
rpcUrl: 'https://api.devnet.solana.com',
maxPricePerRequest: 0.01, // Will refuse to pay more than this (SOL)
});
const { response, paymentId, escrowPDA } = await client.fetchProtected(
'https://your-site.com/articles/ai-research'
);
const content = await response.json();
console.log(content);3. Expected output
→ Requesting content...
← HTTP 402 received. Price: 0.001 SOL
→ Paying 0.001 SOL to escrow...
← Payment confirmed (signature: 3xKp1a...)
→ Retrying with payment proof...
← Content received! (11740 bytes)API Reference
new GateClient(config)
| Option | Type | Default | Description |
|---|---|---|---|
| wallet | Keypair | required | Agent's Solana keypair. Used to sign lock_payment and release_payment transactions. |
| rpcUrl | string | required | Solana RPC endpoint (e.g. https://api.devnet.solana.com). |
| maxPricePerRequest | number | 0.01 | Maximum SOL the agent is willing to pay per request. Requests priced above this throw an error without touching the chain. Can also be set via GATE_MAX_SPEND_SOL env var. |
| retries | number | 1 | Number of retry attempts for lock_payment if the transaction fails (max 3). Uses exponential backoff. |
| apiUrl | string | 'https://api.solinkify.com' | Solinkify backend URL. Use the default unless self-hosting. |
Environment variables (optional overrides):
| Variable | Description |
|---|---|
| GATE_MAX_SPEND_SOL | Fallback spending cap if maxPricePerRequest is not set in config. |
| GATE_RPC_URL | Fallback RPC URL if rpcUrl is not set in config. |
client.fetchProtected(url, options?)
Fetches a URL with automatic payment handling.
const result = await client.fetchProtected(url, fetchOptions?);Parameters:
| Parameter | Type | Description |
|---|---|---|
| url | string | URL to fetch. Must be protected by @solinkify/gate. |
| options | RequestInit | Optional standard fetch options (headers, method, body, etc.). |
Returns: Promise<FetchProtectedResult>
interface FetchProtectedResult {
response: Response; // The final HTTP response (content)
paymentId?: string; // UUID of the payment (present if payment was made)
escrowPDA?: string; // Base58 address of the Escrow PDA (present if payment was made)
}Behavior:
- If the URL returns
200directly (not gated), returns immediately — no payment made. - If the URL returns
402withoutX-Solinkify-Gateheader (not a Solinkify gate), returns the 402 response as-is — no payment made. - If the URL returns
402withX-Solinkify-Gateheader, triggers the full payment flow. - If price exceeds
maxPricePerRequest, throws immediately without touching the chain.
Payment Flow Details
Step 1 — Parse the 402 manifest
parser.ts validates the 402 response from @solinkify/gate:
// Checks for X-Solinkify-Gate header to confirm this is a Solinkify gate
isGateway402(response) // → boolean
// Parses and validates the JSON payment manifest
parse402(response) // → Promise<PaymentManifest>A valid manifest looks like:
{
"error": "Payment Required",
"message": "Content protected by Solinkify Gate",
"price": 0.001,
"currency": "SOL",
"payment": {
"escrow_address": "YOUR_CREATOR_WALLET_ADDRESS",
"payment_id": "",
"endpoint_id": "techpulsedaily",
"expires_at": 1715000000
},
"info": {
"sdk": "https://npm.im/@solinkify/gate-sdk",
"docs": "https://solinkify.io/gate"
}
}Note on
escrow_address: Despite the name, this field contains the creator's wallet address — not an Escrow PDA address. The SDK reads it ascreatorPubkeyand uses it to derive the actual Escrow PDA via["gate_endpoint", creator_pubkey, endpoint_id]and["gate_escrow", agent_pubkey, payment_id]. Funds flow through the PDA and are released to this creator wallet uponrelease_payment().
Note on
endpoint_id: This is the unique identifier the creator registered when setting up their gate on the Solinkify Gate dashboard. It is stored on-chain as part of theendpoint_configPDA and is used by the SDK to derive the correct PDA for payment. The creator sets this once during registration — the SDK receives it automatically from the402manifest. Must be unique within the creator's account, but does not need to be globally unique across all creators.
Step 2 — Lock payment on-chain (payer.ts)
buildAndSubmitPayment() constructs and submits the lock_payment Anchor instruction:
- Generates a unique
payment_id(UUID v4, dashes stripped → 32 hex chars, fits Solana PDA seed limit) - Derives the
endpoint_configPDA from[gate_endpoint, creator_pubkey, endpoint_id] - Derives the
escrowPDA from[gate_escrow, agent_pubkey, payment_id] - Builds and signs the
lock_paymenttransaction - Submits and confirms on-chain
PDA seeds:
endpoint_config PDA: ["gate_endpoint", creator_wallet, endpoint_id]
escrow PDA: ["gate_escrow", agent_wallet, payment_id]Program ID (Devnet): 2q2KWtZbKakmsuoGNVtc9fkPvcs5xdmtVzPHb5zUXXt8
Step 3 — Retry with payment proof
After lock_payment confirms, the SDK retries the original request with two headers:
X-Solinkify-Payment: <payment_id> // UUID (32 hex chars)
X-Solinkify-Payer: <agent_pubkey> // Base58 agent wallet address@solinkify/gate middleware verifies these headers against the on-chain escrow state before serving content.
Step 4 — Release payment
After content is received, the agent calls release_payment() on-chain. The smart contract enforces the fee split:
Escrow amount → 99% Creator wallet
→ 0.75% Operator treasury
→ 0.25% Admin treasury
Escrow PDA closed → rent refunded → AgentTimeout path: If the agent fails to call release_payment(), anyone can trigger it after 3600 seconds. The same fee split applies. This ensures creators are never locked out of their funds.
Spending Cap Safety
The SDK enforces a spending cap at two levels:
// 1. Config-level cap (applies to all requests in this client instance)
const client = new GateClient({
wallet: agentKeypair,
rpcUrl: '...',
maxPricePerRequest: 0.005,
});
// 2. Per-call override (cannot exceed the config-level cap)
// Not supported at fetchProtected() level — set maxPricePerRequest at init.If the manifest price exceeds the cap, the SDK throws before any on-chain transaction:
Error: Price 0.01 SOL exceeds spending cap 0.005 SOLYou can also set the cap via environment variable:
GATE_MAX_SPEND_SOL=0.005 node agent.jsError Reference
| Error message | Cause | Action |
|---|---|---|
| config.wallet is required | No keypair passed to constructor | Pass a valid Keypair |
| config.rpcUrl is required | No RPC URL passed | Pass a valid Solana RPC URL |
| Price X SOL exceeds spending cap Y SOL | Manifest price > maxPricePerRequest | Raise the cap or skip this URL |
| endpoint_id missing from 402 manifest | Creator's @solinkify/gate is outdated | Creator needs to add endpointId to their config |
| Solana RPC error: ... | RPC connection failed | Check rpcUrl, try a different endpoint |
| lock_payment failed: ... | On-chain transaction rejected | Check agent wallet balance |
| Transaction not confirmed: ... | Transaction timed out | Usually transient — retry |
| Payment submitted but access denied | Payment verified but middleware rejected | Check payment_id format or contact creator |
| Invalid payment manifest from server | Server returned malformed 402 body | Site may not be using @solinkify/gate |
Requirements
- Node.js 18+
- A funded Solana keypair (agent wallet)
@solana/web3.jsv1
Related
@solinkify/gate— Creator middleware (the server side)- Solinkify Gate Docs
- Smart Contract (Devnet)
Known Limitations
These are current MVP limitations planned for post-hackathon:
- Devnet only — The smart contract is currently deployed on Solana Devnet. Mainnet deployment is planned post-audit.
@solana/web3.jsv1 only — The SDK depends on@solana/web3.jsv1. v2 has breaking API changes and is not currently supported.- Single request at a time —
fetchProtected()does not support concurrent requests to the same gated URL. Call sequentially or use separateGateClientinstances.
License
MIT — see LICENSE
Part of the Solinkify ecosystem.
