Documentation Index
Fetch the complete documentation index at: https://docs.mobula.io/llms.txt
Use this file to discover all available pages before exploring further.
Agent endpoints are in beta. MPP equivalent: GET /agent/mpp/api-keys/revoke?api_key=... (this x402 route uses DELETE). Overview: Agentic payments.
Agents only. This endpoint is for agents (wallet-based, programmatic access). Dashboard customers manage API keys at admin.mobula.io, not via this x402 endpoint.
DELETE /agent/x402/api-keys/revoke revokes (soft-deletes) an API key that belongs to your agent. The key must belong to the same agent as the paying wallet. Cost: $0.001 USDC (facilitator fee). You must have an active plan.
Query parameters
| Parameter | Required | Description |
|---|
api_key | Yes | The API key to revoke (must belong to this agent) |
Request flow
- Call
DELETE /agent/x402/api-keys/revoke?api_key=<key> with no payment → 402 Payment Required with $0.001 USDC.
- Sign the payment and retry with an
x-payment header → 200 OK with { "deleted": true }.
404 — No agent for this wallet, or the given api_key does not belong to this agent.
Reference scripts
Uses the same wallet as for subscribe. Set API_KEY to the key you want to revoke.
Prerequisites: Solana wallet with USDC for $0.001 and SOL for fees.Full script (uses @solana/kit, @x402/core, @x402/svm). Inline base58ToBytes below, or use import { base58ToBytes } from './base58.ts' when running from scripts/src/x402/.import { createKeyPairSignerFromBytes } from '@solana/kit';
import { x402Client, x402HTTPClient } from '@x402/core/client';
import { registerExactSvmScheme } from '@x402/svm/exact/client';
const BASE_URL = process.env.API_URL ?? 'http://localhost:4058';
const PRIVATE_KEY_B58 = process.env.SOLANA_PRIVATE_KEY;
const API_KEY = process.env.API_KEY;
function base58ToBytes(b58: string): Uint8Array {
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
const map = new Uint8Array(256).fill(255);
for (let i = 0; i < ALPHABET.length; i++) map[ALPHABET.charCodeAt(i)] = i;
const bytes: number[] = [0];
for (const char of b58) {
const value = map[char.charCodeAt(0)];
if (value === undefined || value === 255) throw new Error(`Invalid base58: ${char}`);
let carry: number = value;
for (let j = bytes.length - 1; j >= 0; j--) {
carry += (bytes[j] as number) * 58;
bytes[j] = carry & 0xff;
carry >>= 8;
}
while (carry > 0) {
bytes.unshift(carry & 0xff);
carry >>= 8;
}
}
for (const char of b58) {
if (char !== '1') break;
bytes.unshift(0);
}
return new Uint8Array(bytes);
}
async function main() {
if (!PRIVATE_KEY_B58) {
console.error('SOLANA_PRIVATE_KEY required');
process.exit(1);
}
if (!API_KEY?.trim()) {
console.error('API_KEY required');
process.exit(1);
}
const signer = await createKeyPairSignerFromBytes(base58ToBytes(PRIVATE_KEY_B58));
const endpoint = `/agent/x402/api-keys/revoke?api_key=${encodeURIComponent(API_KEY)}`;
const probeRes = await fetch(`${BASE_URL}${endpoint}`, { method: 'DELETE' });
if (probeRes.status !== 402) {
console.error(probeRes.status, await probeRes.text());
process.exit(1);
}
const paymentRequiredHeader = probeRes.headers.get('payment-required');
if (!paymentRequiredHeader) {
console.error('Missing payment-required header');
process.exit(1);
}
const decoded = JSON.parse(Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8'));
const svmOption = decoded.accepts?.find((a: { network: string }) => a.network?.startsWith('solana:'));
if (!svmOption) {
console.error('No Solana option in 402');
process.exit(1);
}
const client = new x402Client();
registerExactSvmScheme(client, { signer });
const httpClient = new x402HTTPClient(client);
const paymentRequired = httpClient.getPaymentRequiredResponse((name) => probeRes.headers.get(name), decoded);
let paymentHeaders: Record<string, string>;
try {
const payload = await httpClient.createPaymentPayload(paymentRequired);
paymentHeaders = httpClient.encodePaymentSignatureHeader(payload);
} catch (err) {
console.error('Payment failed:', err);
process.exit(1);
}
const paidRes = await fetch(`${BASE_URL}${endpoint}`, { method: 'DELETE', headers: paymentHeaders });
const body = await paidRes.text();
if (paidRes.status !== 200) {
console.error(paidRes.status, body);
process.exit(1);
}
console.log(body);
process.exit(0);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
Prerequisites: EVM wallet on Base mainnet with USDC for $0.001.Full script:import { x402Client, x402HTTPClient } from '@x402/core/client';
import { registerExactEvmScheme } from '@x402/evm/exact/client';
import { privateKeyToAccount } from 'viem/accounts';
const BASE_URL = process.env.API_URL ?? 'https://api.mobula.io';
const PRIVATE_KEY = process.env.TEST_PRIVATE_KEY as `0x${string}` | undefined;
const API_KEY = process.env.API_KEY;
async function main() {
if (!PRIVATE_KEY) {
console.error('ERROR: Set TEST_PRIVATE_KEY=0x<your_private_key> (same wallet used to subscribe)');
process.exit(1);
}
if (!API_KEY || API_KEY.trim() === '') {
console.error('ERROR: Set API_KEY=<the_api_key_to_delete>');
process.exit(1);
}
const account = privateKeyToAccount(PRIVATE_KEY);
console.log(`[1] DELETE ${BASE_URL}/agent/x402/api-keys/revoke?api_key=*** (no payment)`);
const probeRes = await fetch(`${BASE_URL}/agent/x402/api-keys/revoke?api_key=${encodeURIComponent(API_KEY)}`, {
method: 'DELETE',
});
console.log(` Status: ${probeRes.status}`);
if (probeRes.status !== 402) {
const text = await probeRes.text();
console.log(` Body: ${text}`);
process.exit(1);
}
const paymentRequiredHeader = probeRes.headers.get('payment-required');
if (!paymentRequiredHeader) {
console.error(' Missing payment-required header');
process.exit(1);
}
const decoded = JSON.parse(Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8'));
const evmOption = decoded.accepts?.find((a: { network: string }) => a.network?.startsWith('eip155:'));
if (evmOption) {
console.log(`[2] Revoke API key price: $${Number(evmOption.amount) / 1_000_000} USDC`);
}
console.log('[3] Signing payment...');
const coreClient = new x402Client();
registerExactEvmScheme(coreClient, { signer: account });
const httpClient = new x402HTTPClient(coreClient);
const paymentRequired = httpClient.getPaymentRequiredResponse((name) => probeRes.headers.get(name), decoded);
let paymentPayload;
try {
paymentPayload = await httpClient.createPaymentPayload(paymentRequired);
} catch (err) {
console.error(' Payment creation failed:', err);
process.exit(1);
}
const paymentHeaders = httpClient.encodePaymentSignatureHeader(paymentPayload);
console.log(`[4] DELETE ${BASE_URL}/agent/x402/api-keys/revoke (with payment)`);
const paidRes = await fetch(`${BASE_URL}/agent/x402/api-keys/revoke?api_key=${encodeURIComponent(API_KEY)}`, {
method: 'DELETE',
headers: paymentHeaders,
});
const body = await paidRes.text();
console.log(` Status: ${paidRes.status}`);
console.log(' Result:', body || '(empty)');
if (paidRes.status === 404) {
console.error('\n No agent found for this wallet, or API key does not belong to this agent.');
process.exit(1);
}
if (paidRes.status !== 200) {
console.error('\n Request failed.');
process.exit(1);
}
const data = JSON.parse(body) as { deleted: true };
console.log('\n API key revoked.', data);
process.exit(0);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
To create a new API key: x402 Create API key. Full walkthrough: x402 Subscription and top-up.