Wallet Authentication
Authenticated endpoints require three HTTP headers. The signed message must match the format below and is valid for 60 seconds (single-use).
Headers
# Required headers for authenticated Vouch API endpoints X-Wallet-Pubkey: <base58-public-key> X-Wallet-Signature: <base58-ed25519-signature> X-Wallet-Timestamp: <unix-epoch-seconds>
Signed Message Format
# Canonical message format: sign this with your Ed25519 key
vouch-auth:<pubkey>:<timestamp>:<method>:<path>
Note: Signatures are valid for 60 seconds and can only be used once to prevent replay attacks.
Example Error Responses
401 Unauthorized
// Possible 401 error messages: { "error": "Missing auth headers. Required: X-Wallet-Pubkey, X-Wallet-Signature, X-Wallet-Timestamp" } { "error": "Invalid timestamp" } { "error": "Signature expired. Timestamp must be within 60 seconds." } { "error": "Invalid wallet pubkey" } { "error": "Invalid signature encoding" } { "error": "Invalid signature" } { "error": "Signature already used. Sign a new message with a fresh timestamp." }
Code Example
Note: <method> is the uppercase HTTP method (GET, POST, etc.). <path> is the request path without query parameters (e.g., /agents).
TypeScript / JavaScript
import nacl from 'tweetnacl'; import bs58 from 'bs58'; const pubkey = keypair.publicKey.toBase58(); const timestamp = Math.floor(Date.now() / 1000).toString(); const method = 'GET'; const path = '/agents'; const message = `vouch-auth:${pubkey}:${timestamp}:${method}:${path}`; const messageBytes = new TextEncoder().encode(message); const signature = nacl.sign.detached(messageBytes, keypair.secretKey); const signatureBase58 = bs58.encode(signature); const response = await fetch('https://api.vouchprotocol.xyz/agents', { headers: { 'X-Wallet-Pubkey': pubkey, 'X-Wallet-Signature': signatureBase58, 'X-Wallet-Timestamp': timestamp, }, });