Authentication
All Intel endpoints require authentication. Two methods are supported.
API Key
The simplest method. Pass your key in the X-Api-Key header on every request. Credits are deducted from the key owner's account on each successful response.
curl https://api.sova-intel.com/api/v1/intel/wallet/WALLET_ADDRESS/hud \
-H "X-Api-Key: ak_your_key"
Keys have the format ak_<hex>. Contact the Sova team to get one.
X402 — Autonomous Solana USDC Payment
Pay per-call directly from a Solana wallet. No pre-funded account, no key required. The protocol is x402 v2.
How it works
- Probe — call any endpoint with no auth → receive HTTP
402 - Read payment instructions from the
PAYMENT-REQUIREDresponse header (base64 JSON) - Build a USDC transaction paying the required amount to the treasury address
- Resend the original request with
payment-signatureheader
Each payment signature is single-use (24-hour replay protection).
Step 1 — Probe
curl -i https://api.sova-intel.com/api/v1/intel/wallet/WALLET_ADDRESS/hud
# → HTTP 402
# → Header: PAYMENT-REQUIRED: <base64>
Decoded PAYMENT-REQUIRED header:
{
"x402Version": 2,
"accepts": [{
"scheme": "exact",
"network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"amount": "15000",
"payTo": "<TREASURY_PUBKEY>",
"maxTimeoutSeconds": 60
}]
}
Cache accepts[0].payTo — it never changes per deployment.
Step 2 — Build and send payment
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
import {
TOKEN_PROGRAM_ID,
createTransferCheckedInstruction,
getAssociatedTokenAddress,
} from "@solana/spl-token";
const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
async function buildPayment(
connection: Connection,
keypair: Keypair,
payTo: string,
amountBaseUnits: bigint,
): Promise<string> {
const agentAta = await getAssociatedTokenAddress(USDC_MINT, keypair.publicKey);
const treasuryAta = await getAssociatedTokenAddress(USDC_MINT, new PublicKey(payTo));
const { blockhash } = await connection.getLatestBlockhash("confirmed");
const tx = new Transaction({ recentBlockhash: blockhash, feePayer: keypair.publicKey });
tx.add(createTransferCheckedInstruction(
agentAta, USDC_MINT, treasuryAta,
keypair.publicKey, amountBaseUnits, 6, [], TOKEN_PROGRAM_ID,
));
tx.sign(keypair);
return Buffer.from(JSON.stringify({
x402Version: 2,
scheme: "exact",
network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
payload: { transaction: tx.serialize().toString("base64") },
})).toString("base64");
}
Always derive the treasury ATA with getAssociatedTokenAddress(USDC_MINT, new PublicKey(payTo)). Do not use the raw payTo pubkey as the transfer destination.
On success, the response includes: X-Payment-Response: <txSignature>
X402 Constants
| Constant | Value |
|---|---|
| USDC mint | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDC decimals | 6 |
| Amount | Read from PAYMENT-REQUIRED header (string → BigInt) |
| Network | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (mainnet) |
Common X402 Errors
| Error message | Fix |
|---|---|
transfer destination does not match treasury ATA | Derive ATA — don't use raw payTo pubkey |
USDC amount N < required M | Use amount from PAYMENT-REQUIRED payload exactly |
transaction already used (replay attack) | Use a fresh transaction + fresh blockhash per request |
transaction confirmation failed | Expired blockhash or RPC error — retry with new tx |
Using X402 with the SDK
Pass a buildPayment callback to the client — the SDK handles the 402→pay→retry loop automatically:
import { SovaIntelClient } from "@sova-intel/sdk";
const client = new SovaIntelClient({
baseUrl: "https://api.sova-intel.com/api/v1",
auth: {
kind: "x402",
buildPayment: async (payTo, amountBaseUnits) => {
// build and return base64 payment header
return buildPayment(connection, keypair, payTo, amountBaseUnits);
},
},
});
See the SDK Quickstart for the complete setup.