Skip to content
Spendkit
GuidesBuild with Spendkit
DEVELOPER GUIDE · WIRE EXAMPLES

Call the Spendkit MCP API

This page shows the actual HTTP requests a client sends. Your application can be written in any language that supports OAuth Client Credentials, JSON-RPC over HTTP, and an EVM wallet signer. Spendkit exposes payment tools through MCP; it does not expose a separate public REST payment API.

Want to start with a complete JavaScript, Python, Go, or C++ file? Copy an example →

Before sending requests

In the dashboard, create an Agent, bind and verify its Payment Wallet, set an onchain Spending Policy, and select Connect Agent. Save that connection's Client ID and Client Secret in your server or Runtime secret store. Use read-only access while building the first calls; authorization requires Full Runtime Access. Dashboard setup →

1. Exchange connection credentials for a token

Send a form-encoded OAuth 2.0 Client Credentials request. Both HTTP Basic and form-body credentials are supported. The example uses form fields. Omit scope to request the scopes assigned to the connection; any explicit scope must be a subset of those scopes.

curl -sS -X POST 'https://spendkit-alpha.vercel.app/oauth/token' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'client_id=<client-id>' \
  --data-urlencode 'client_secret=<client-secret>'

Response shape:

{
  "access_token": "ska_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "policy:read payment:preview payment:authorize payment:read payment:record"
}

Scopes in the response depend on the connection. Cache the token until shortly before expiry, then request another one. Keep the Client Secret out of browser code, model prompts, logs, and Git.

2. Discover the MCP server and tools

Put the returned access token in your Runtime's SPENDKIT_ACCESS_TOKEN variable for the examples below. Send JSON-RPC 2.0 to POST /mcp with that Bearer token. The current preferred protocol is 2026-07-28: start with server/discover, then tools/list. In a real program, let an MCP client manage the envelope. The headers and params._meta below show what a direct HTTP implementation must send.

curl -sS -X POST 'https://spendkit-alpha.vercel.app/mcp' \
  -H "Authorization: Bearer $SPENDKIT_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  --data '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"my-agent","version":"1.0.0"},"io.modelcontextprotocol/clientCapabilities":{}}}}'

For tools/list, send the same request with Mcp-Method: tools/list and "method":"tools/list". Its result.tools array contains the current names, descriptions, annotations, and JSON input schemas. Do not hard-code an old schema when discovery is available. Older compatible clients can use the supported 2025-11-25 initialize flow.

curl -sS -X POST 'https://spendkit-alpha.vercel.app/mcp' \
  -H "Authorization: Bearer $SPENDKIT_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: tools/list' \
  --data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"my-agent","version":"1.0.0"},"io.modelcontextprotocol/clientCapabilities":{}}}}'

3. Call a tool and read its result

Use tools/call. Modern MCP also requires Mcp-Method: tools/call and a Mcp-Name header matching params.name. This example is read-only and does not issue an authorization:

POST /mcp
Authorization: Bearer <access-token>
Content-Type: application/json
Accept: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: spendkit_preview_payment

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "spendkit_preview_payment",
    "arguments": {
      "amount": "0.01",
      "recipient": "0x1111111111111111111111111111111111111111",
      "token": "USDG"
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "my-agent", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

Example response structure, showing selected fields only:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "complete",
    "structuredContent": {
      "allowed": true,
      "reason": "policy_allows_payment",
      "amount": "0.01",
      "token": "USDG"
    },
    "content": [{ "type": "text", "text": "{...}" }]
  }
}

The actual structuredContent also includes Policy and wallet details plus spendkit.traceId; the text content contains the JSON form. Parse result.structuredContent when your client supports it. Check result.isError and structuredContent.allowed or accepted as appropriate. HTTP 200 alone does not mean Spendkit allowed a payment.

4. Complete a real payment in your Runtime

  1. Call spendkit_get_connection and spendkit_get_policy with {}. Match the returned Agent, network, bound wallet, and active Policy to your Runtime configuration.
  2. Call spendkit_preview_payment with {"amount":"0.01","recipient":"0x...","token":"USDG"}. Stop on DENY.
  3. Call spendkit_authorize_payment for the same payment, adding a stable idempotencyKey for this one logical payment. A successful response contains intentId, authorization, and transaction.
  4. In trusted wallet code, compare the authorization and transaction with the requested amount and recipient, the bound wallet, the current chain, and the Router from get_connection. Submit exactly the returned transaction.to, transaction.data, and transaction.value from transaction.from. The Payment Wallet pays gas; Spendkit Server does not broadcast.
  5. After wallet submission, call spendkit_record_payment with intentId and the actual txHash. Call spendkit_get_payment_status until the status is settled. An authorization or submitted transaction is not confirmation.

If a broadcast outcome is uncertain, query the existing intent and chain transaction before any retry. Never blindly submit the same payment again. The local reference client shows OAuth refresh, MCP transport, wallet checks, and payment orchestration.

Next: exact tool fields

See the required arguments, scopes, selected output fields, DENY behavior, and payment status values in the MCP tool reference. For model-assisted implementation, give your coding Agent llms-full.txt and the coding-Agent instructions.