Pay a Suede x402 endpoint in five steps
Suede AI sells music, video and image generation per call over x402. There is no account and no API key: your wallet signs a USDC payment on Base, and the same HTTP request carries it. This guide follows the flow the live endpoints at app.suedeai.ai run, checked on 2026-10-11.
What you need
- USDC on Base mainnet in a wallet you control. The lowest-priced call, Agent Image, costs $0.15.
- A way to sign EIP-712 typed data, such as viem, ethers or eth-account. The suede-ai Python SDK does the signing for you.
- No ETH for gas. Under the x402
exactscheme the facilitator submits the transfer and pays the gas. Your signature fixes the amount and the recipient, and the facilitator cannot change either.
1. Read the price from the discovery document
Prices are enforced on the server, so read them before you sign. The x402 discovery document lists every paid resource:
curl -s https://app.suedeai.ai/.well-known/x402 \
| jq -r '.resources[] | "\(.method) \(.resource) \(.priceUsd)"'
On 2026-10-11 it returned three resources:
POST https://app.suedeai.ai/create-music $0.50
POST https://app.suedeai.ai/agent/video $4.99
POST https://app.suedeai.ai/agent/image $0.15
The discovery page covers the other machine-readable formats, including OpenAPI, the product catalog and the MCP server.
2. Send the request and read the 402 challenge
Send the request you want to buy, with no payment attached:
curl -i -X POST https://app.suedeai.ai/agent/image \
-H 'Content-Type: application/json' \
-d '{"prompt":"Album cover, neon city at night, rain on glass","aspectRatio":"1:1","outputFormat":"png"}'
The server answers 402 Payment Required. The challenge comes twice: as base64-encoded JSON in the PAYMENT-REQUIRED header, and as the same JSON in the body. Trimmed to the fields you sign against:
HTTP/2 402
content-type: application/json
payment-required: eyJ4NDAyVmVyc2lvbiI6IDIs...
suede-api-version: 1
{
"x402Version": 2,
"error": "PAYMENT-SIGNATURE header is required",
"resource": { "url": "https://app.suedeai.ai/agent/image" },
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"maxAmountRequired": "150000",
"amount": "150000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x10FF767043A1723E0BB5B9207bC37D3442cC9E4F",
"maxTimeoutSeconds": 300,
"extra": { "name": "USD Coin", "version": "2", "decimals": 6, "priceUsd": "$0.15" }
}]
}
| Field | What it tells you |
|---|---|
network | eip155:8453 is Base mainnet. |
asset | The USDC contract on Base. It is also the EIP-712 verifyingContract. |
amount | The price in USDC base units. USDC has 6 decimals, so 150000 is $0.15. |
payTo | The address that receives the payment. |
maxTimeoutSeconds | 300 seconds on all three endpoints. |
extra.name, extra.version | The EIP-712 domain name and version: "USD Coin" and "2". |
To read the header copy instead of the body, decode it:
curl -s -o /dev/null -D - -X POST https://app.suedeai.ai/agent/image \
-H 'Content-Type: application/json' -d '{"prompt":"test"}' \
| grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' \
| base64 -d | jq '.accepts[0]'
An unpaid request gets the 402 even when the body is empty, so the challenge does not mean your body is valid. Check it against the endpoint reference before you sign.
3. Sign an EIP-3009 authorization
USDC supports EIP-3009 transferWithAuthorization, so paying is a signature over EIP-712 typed data. You do not send a transaction yourself. Fill the typed data from the accepts entry:
domain
name "USD Coin" accepts[0].extra.name
version "2" accepts[0].extra.version
chainId 8453
verifyingContract 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 accepts[0].asset
primaryType TransferWithAuthorization
from address your wallet
to address 0x10FF767043A1723E0BB5B9207bC37D3442cC9E4F accepts[0].payTo
value uint256 150000 accepts[0].amount
validAfter uint256 a Unix time slightly in the past
validBefore uint256 a Unix time later than now + maxTimeoutSeconds
nonce bytes32 32 random bytes
For reference, the suede-ai SDK sets validAfter to 60 seconds before now and validBefore to now plus the larger of 600 seconds and maxTimeoutSeconds plus 60.
4. Retry with the PAYMENT-SIGNATURE header
Wrap the signature in an x402 version 2 PaymentPayload. The accepted field repeats the accepts entry you chose:
{
"x402Version": 2,
"resource": { "url": "https://app.suedeai.ai/agent/image" },
"accepted": {
"scheme": "exact",
"network": "eip155:8453",
"amount": "150000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x10FF767043A1723E0BB5B9207bC37D3442cC9E4F",
"maxTimeoutSeconds": 300,
"extra": { "name": "USD Coin", "version": "2", "decimals": 6, "priceUsd": "$0.15" }
},
"payload": {
"signature": "0x...",
"authorization": {
"from": "0xYourWallet",
"to": "0x10FF767043A1723E0BB5B9207bC37D3442cC9E4F",
"value": "150000",
"validAfter": "1791746000",
"validBefore": "1791746660",
"nonce": "0x..."
}
}
}
Base64-encode that JSON and send the identical request again with it in PAYMENT-SIGNATURE. Add an X-Idempotency-Key: if you resend the same key and body within the cache window, you get the original response back without a second charge.
curl -i -X POST https://app.suedeai.ai/agent/image \
-H 'Content-Type: application/json' \
-H "PAYMENT-SIGNATURE: $PAYMENT" \
-H 'X-Idempotency-Key: cover-art-0001' \
-d '{"prompt":"Album cover, neon city at night, rain on glass","aspectRatio":"1:1","outputFormat":"png"}'
A paid call answers 202 Accepted with a job to poll. The discovery document gives this example body:
{
"jobId": "image-job-example",
"status": "queued",
"provider": "suede",
"pollUrl": "https://app.suedeai.ai/agent/image/image-job-example"
}
Paid calls are asynchronous by default because a render takes minutes and payment clients time out in about 30 seconds. To block until the asset is ready, add ?async=false to the URL. Only the literal value true selects asynchronous mode: any other value, including 1, yes and on, counts as false and blocks.
The same header also accepts a Skyfire pay+jwt or kya-pay+jwt token in place of an x402 payload.
5. Poll for the result
Polling is free: the job was paid for when it was created. Request pollUrl until the job reaches a final state.
curl -s https://app.suedeai.ai/agent/image/<jobId>
| Endpoint | Poll route | Finished when | Asset field |
|---|---|---|---|
| /agent/image | GET /agent/image/{jobId} | status is completed or failed | imageUrl |
| /agent/video | GET /agent/video/{jobId} | status is completed or failed | videoUrl |
| /create-music | GET /api/songs/{songId} | model_version is no longer pending | audio_url |
For music, audio_url holds a placeholder cover image until the track is rendered, so check model_version, not audio_url. A value of failed is final. The errors page covers rate limits, retries and what is never charged.
Skip the signing code
The suede-ai Python SDK runs steps 2 to 4 for you and returns the 202 body:
pip install suede-ai
from suede_ai import SuedeClient
with SuedeClient(wallet_private_key=PRIVATE_KEY) as suede:
job = suede.agent_image(prompt="Album cover, neon city at night, rain on glass")
print(job["pollUrl"])
Sell your own x402 endpoint
The same rail works in the other direction. x402 Agent Builder: Get Paid Per Call: build an AI agent on a visual canvas, set a price per call, and get paid in USDC on Base through x402 once you enable payments. Dry runs need no wallet.
Sources
Every value on this page comes from these documents, fetched on 2026-10-11, and from live requests to the endpoints on the same day.
- app.suedeai.ai/.well-known/x402: prices, payment requirements and example responses.
- app.suedeai.ai/openapi.json: asynchronous execution, the
asyncparameter, idempotency and poll routes. - x402 HTTP transport, version 2: the
PAYMENT-REQUIREDandPAYMENT-SIGNATUREheaders and thePaymentPayloadshape. - x402 exact scheme on EVM: EIP-3009 signing and who pays gas.
- EIP-3009: the
TransferWithAuthorizationtype. - suede-ai 0.4.0 on PyPI: the SDK's signing window.