Skip to main content
SouthPay runs a hosted Model Context Protocol server that exposes a merchant store as tools any MCP client can call.
It speaks the Streamable HTTP transport at /mcp. Each session is backed by its own isolated state, which is where a key set through the login tool is held. The server is a thin proxy over the agentic REST API with no business logic of its own. It holds no secrets: the only credential is the merchant’s spa_ agent key, validated by SouthPay on every call. Anything gated on the REST side is gated identically here, and session mandates and x402 settlements are not exposed as tools at all. Those are REST-only.

Connecting

There are three ways to authenticate, resolved in this order per tool call: a key set with login, then the session’s OAuth-minted key, then the Authorization bearer header.

Bearer key

Send an agent credential as a bearer token. A raw spa_ token skips the OAuth flow entirely.

Browser login (OAuth)

Connect a client with no token and the server runs an OAuth flow instead. It serves its own metadata discovery, dynamic client registration and token endpoint, which clients like claude.ai require, then forwards you to the SouthPay dashboard consent screen, where you pick the store and whether the session is live or test. On the way back it exchanges the code for an OAuth access token and calls POST /api/v2/oauth/agent_credentials to mint a scoped spa_ credential. That credential stays server-side; the MCP client only ever sees tokens issued by the SouthPay MCP server.
One credential exists per application, user, store and mode. Logging in again rotates that same credential, which invalidates the key minted by any earlier login. If you have wired a previous login’s key into something else, that thing stops working.

The login tool

If a client cannot send custom headers and cannot run OAuth, the agent can call login with the key pasted in. It is held for the session until logout. Prefer the bearer header for a live key. A key pasted into login passes through the model’s context.

Results and errors

Every tool returns plain JSON as structured content, plus a short human-readable message. Failures never raise a protocol error. They come back as data under an error key so the agent can read the denial and respond to it:
detail carries the API’s error envelope verbatim. Other error values you will see: not_connected when no key resolved, invalid_key when login is given something that does not start with spa_live_ or spa_test_, login_failed, tool_exception, and market_data_unreachable from research_token. Authorization denials (mandate_required, payout_controls_required, authorization_denied) are expected outcomes of the fail-closed design. Explain them; do not retry them.

Tools

Nineteen tools. Scopes are the agent credential scopes required; a credential limited to payments:* is rejected on the token and payout tools. Tools marked gated are refused server-side unless a spend limit or an authorization provider permits the action. See agent authorization.

Session

Account

Call this first. authorization.mandates tells you whether the gated tools can succeed at all on this store.

Payments

Each entry in deposit_addresses[] is {address, crypto_amount, crypto_amount_atomic, coin_symbol, chain_symbol, asset_id, exchange_rate}. crypto_amount is the tool’s own human rendering of crypto_amount_atomic at the asset’s decimals; it is not a field the REST API returns. create_payment, refund_payment and create_payout send an Idempotency-Key per call, generated by the server. wait_for_payment holds the request open, so keep the budget modest and call again rather than asking for the full 240 seconds. It accepts a reference as well as an id: on a 404 it retries the lookup as a reference filter. For durable notification across sessions, configure a webhook instead. Omit amount on refund_payment for a full refund; a decimal string gives a partial refund, and partial refunds apply to completed payments only. asset_id is only needed when a payment settled in more than one asset.

Tokens and balances

balance and amount are the tool’s decimal renderings of balance_atomic and amount_cents. settlement_balance is the store-wide value of stablecoin holdings in the store’s settlement currency, and stays store-wide even when asset_id narrows the balance list. amount_cents is null if the rate lookup fails. The accepted and available lists are filtered to the credential’s mode, so a test credential sees testnet assets only.

Payouts

Each row in limits[] carries asset_id, coin_symbol, chain_symbol, atomic_decimals, per_tx_cap_atomic, daily_cap_atomic, daily_spent_atomic, daily_remaining_atomic and controls_configured, plus decimal renderings as per_tx_cap, daily_cap, daily_spent and daily_remaining. Call get_payout_limits before create_payout. controls_configured: false on an asset means a payout in it will be denied with payout_controls_required, and staying inside daily_remaining avoids a daily_cap_exceeded denial. amount_atomic is base units, wei for ETH, so confirm the figure with a human before spending it.

Rates and market data

get_exchange_rate is SouthPay’s own rate, the one applied when pricing a payment. currency defaults to the store’s settlement currency and must be one of the ten accepted payment currencies. Omit asset_id for every accepted asset. research_token is public CoinGecko data proxied through SouthPay’s cached market data endpoint. It touches no store data and no funds, and needs no credential. Accepts a ticker such as "ETH" or a CoinGecko id such as "ethereum". Use get_exchange_rate, not this, for the rate a customer will be charged.

Picking up tool changes

Deploying the server updates it immediately, but MCP clients cache the tool list for the life of a session. The server advertises listChanged, and a fresh chat or re-added connector is the reliable way to see new tools.

Isolation

Tools only ever see the store and mode encoded in the credential. Payments, payouts, refunds and settlements are further scoped to the credential that created them, so an agent cannot read or cancel another integration’s payments on the same store. Agents cannot manage credentials, their own included.
Last modified on August 21, 2026