Getting Started
The zkStash REST API provides a powerful interface for interacting with the memory layer directly. This guide covers the essential concepts of authentication and payments required to use the API.
Base URL
All API requests should be made to:
https://api.zkstash.aiAuthentication
zkStash uses a wallet-based authentication mechanism. Every request must be signed by a valid EVM or Solana wallet.
Required Headers
Include the following headers in every request:
| Header | Description |
|---|---|
x-wallet-address | Your public wallet address (e.g., 0x... or SolanaAddress...). |
x-wallet-signature | A cryptographic signature of the request details. |
x-wallet-timestamp | The current Unix timestamp (in milliseconds). Must be within 2 minutes of the server time. |
Generating the Signature
To generate the x-wallet-signature, you must sign a canonical message constructed from the request details.
1. Construct the Canonical Message
The message format is:
METHOD|PATH|BODY_HASH|TIMESTAMP- METHOD: The HTTP method in uppercase (e.g.,
POST,GET,PATCH). - PATH: The request path (e.g.,
/memories). Do not include query parameters. - BODY_HASH: The SHA-256 hash of the request body (hex string).
- If there is no body (e.g., GET requests), use the hash of an empty string.
- TIMESTAMP: The exact timestamp value used in the
x-wallet-timestampheader.
2. Sign the Message
Sign the canonical message string using your wallet’s private key.
- EVM: Use
signMessage(). - Solana: Use
signMessages().
Example (Node.js)
const crypto = require('crypto');
const ethers = require('ethers'); // For EVM
const nacl = require('tweetnacl'); // For Solana
const bs58 = require('bs58');
async function generateHeaders(method, path, body, privateKey, chain = 'evm') {
const timestamp = Date.now().toString();
// 1. Hash the body
const bodyString = body ? JSON.stringify(body) : '';
const bodyHash = crypto.createHash('sha256').update(bodyString).digest('hex');
// 2. Create Canonical Message
const message = `${method.toUpperCase()}|${path}|${bodyHash}|${timestamp}`;
let signature;
let address;
if (chain === 'evm') {
const wallet = new ethers.Wallet(privateKey);
address = wallet.address;
signature = await wallet.signMessage(message);
} else {
// Solana implementation
const keyPair = nacl.sign.keyPair.fromSecretKey(bs58.decode(privateKey));
address = bs58.encode(keyPair.publicKey);
const messageBytes = new TextEncoder().encode(message);
const signatureBytes = nacl.sign.detached(messageBytes, keyPair.secretKey);
signature = Buffer.from(signatureBytes).toString('base64');
}
return {
'x-wallet-address': address,
'x-wallet-timestamp': timestamp,
'x-wallet-signature': signature,
'Content-Type': 'application/json'
};
}Payment Flow (x402)
zkStash implements the x402 protocol for metered API usage. This allows for permissionless, pay-as-you-go access using crypto.
402 Payment Required
If your account (wallet) does not have enough free credits, the API will return a 402 Payment Required status. The response body will contain the payment requirements.
Example 402 Response:
{
"x402Version": 1,
"error": "X-PAYMENT header is required",
"accepts": [
{
"network": "solana-devnet",
"token": "USDC",
"amount": "0.1",
"recipient": "CKPKJWNdJEqa81x7CkZ14BVPiY6y16Sxs7owznqtWYp5"
},
{
"network": "base-sepolia",
"token": "USDC",
"amount": "0.1",
"recipient": "0x..."
}
]
}Handling Payments
- Check for 402: Inspect the response status code.
- Select Network: Choose a supported network from the
acceptslist (e.g.,solana-devnet). - Execute Transaction: Send the specified
amountoftokento therecipientaddress on-chain. - Retry with Proof: Retry the original API request, adding the
x-paymentheader.
The x-payment header should contain a base64-encoded JSON object with the transaction details (proof).
Supported Networks:
solana-devnetbase-sepolia
Response Codes
| Status | Meaning | Description |
|---|---|---|
200 | OK | The request was successful. |
400 | Bad Request | Invalid parameters or body schema. Check the error message for details. |
401 | Unauthorized | Invalid signature, timestamp expired (> 2 mins), or malformed headers. |
402 | Payment Required | Insufficient credits. Payment required to proceed. |
404 | Not Found | The requested resource (memory, schema) does not exist. |
500 | Internal Error | Something went wrong on the server. |