For agents/index.md/llms.txt/docs/api.md/openapi.json
SoukbaseHumans sell. Agents buy.

Soukbase API

Base URL: https://soukbase.com. Machine description: /openapi.json (catalog at /.well-known/api-catalog). Reading needs no authentication. Sellers sign in with a wallet signature: see /.well-known/auth.md. There are no API keys. Checkout is not on this domain; it runs at the seller's APIbase Integrator shop. Read-only MCP: POST /mcp (tools: search_listings, get_listing, get_seller, list_categories, get_checkout, trust_get; same limits as the read group).

All bodies are JSON. All prices are strings with two decimals in USDC, for example "12.50". Every error has the same shape: {"error": "...", "code": "...", "field": "...", "next_action": "..."}. Match on code; the wording of error can change.

Routes

<!-- generated:routes -->

RouteAuthWhat it does
POST /api/v1/auth/noncenoneGet a single-use nonce for a sign-in message
POST /api/v1/auth/siwewallet signature in bodyExchange a signed session message for a bearer token
GET /api/v1/handles/{handle}noneCheck if a seller handle is free
POST /api/v1/sellerswallet signature in bodyCreate a seller from a signed wallet message
GET /api/v1/sellers/mesession tokenRead your own seller record: profile, shop connection and your listings
PATCH /api/v1/sellers/mesession tokenChange your profile; connect, change or disconnect your Integrator shop (checked against the shop at once; not reserved until verified)
GET /api/v1/sellers/{handle}noneRead a seller's public record
POST /api/v1/listingssession tokenCreate a listing and send it to review
PATCH /api/v1/listings/{id}session tokenChange a listing you own
GET /api/v1/listingsnoneSearch live listings
GET /api/v1/listings/{id}none (token adds owner view)Read one listing (JSON plus schema.org JSON-LD)
GET /api/v1/listings/{id}/publish-requestsession tokenGet the Integrator catalog request for an approved listing
GET /api/v1/trust/{sellerHandle}nonetrust_get for a seller (fields in /docs/trust.md)
GET /api/v1/trust/listing/{id}nonetrust_get for one live listing, with an optional quote check (fields in /docs/trust.md)
POST /api/v1/reportsnoneReport a listing or an order
GET /api/v1/statsnonePublic counts: sellers, listings, paid orders by class (what each means: /docs/status.md)
GET /aup.jsonnoneThe prohibited-goods rule list, check ids and its ruleset_hash
GET /openapi.jsonnoneThis API as OpenAPI 3.1

<!-- /generated:routes -->

POST /hooks/telegram is the operator's review webhook. It accepts only Telegram's calls with the bot's secret header and answers 403 to anyone else; it is not part of the API.

POST /hooks/integrator is the Integrator's order webhook (signed, see /docs/sellers.md). The public listing feed is GET /feed/products.jsonl, GET /feed/products.json (live listings only, licence CC BY 4.0 via soukbase.com, keep source_url and source_hash) and GET /feed/newest.xml (Atom, the 50 newest). All three answer ETag, If-None-Match and If-Modified-Since with 304.

Limits

<!-- generated:limits --> Rate limits. Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a refused request gets HTTP 429 and Retry-After (seconds until the window resets). For reports only accepted reports count toward the daily limit; refused attempts are counted separately, per IP.

GroupLimitCounted perApplies to
read120 per 1 minuteIPevery GET under /api/v1 and /openapi.json
sellerWrite60 per 1 hourwalletcreating a seller, creating a listing, changing a listing
reports10 per 24 hourswallet (IP when no wallet is given)accepted reports only; a refused report does not count
reportsRefused100 per 1 hourIPrefused report attempts (invalid input, unknown listing); a guard against floods, never touches the 10 per day
nonce30 per 1 minuteIPgetting a nonce and signing in

Seller limits (until tier T3, then the second row):

TierPrice per listingOpen listingsUnits per order
T1, T21.00 to 20.00 USDC51
T31.00 to 200.00 USDC (the Integrator's new-merchant cap)not limitednot limited by Soukbase

An open listing is one that is in review, approved and waiting to publish, live or paused. Rejected and removed listings do not count. Tier T0 cannot list.

Field limits.

<!-- /generated:limits -->

Examples

Sign in and create a seller

Get a nonce, build the EIP-4361 message from the values in the answer, sign it with the wallet (EIP-191 personal_sign), and send both. The exact message and statement are in /.well-known/auth.md.

curl -s https://soukbase.com/api/v1/auth/nonce \
  -H 'content-type: application/json' \
  -d '{"address":"0xYourWalletAddress","purpose":"seller_bind"}'
{"nonce":"3f9c...","purpose":"seller_bind","expires_at":"2026-11-02T12:10:00.000Z","domain":"soukbase.com","uri_origin":"https://soukbase.com","chain_ids":[8453],"documents":[{"name":"seller-agreement","resource":"https://soukbase.com/legal/seller-agreement.md#sha256=..."}]}
curl -s https://soukbase.com/api/v1/sellers \
  -H 'content-type: application/json' \
  -d '{"handle":"acme-data","display_name":"Acme Data","contact_email":"owner@acme.example","country":"DE","consents":{"agreement":true,"sanctions":true,"public_receipts":true},"siwe":{"message":"...","signature":"0x..."}}'
{"handle":"acme-data","display_name":"Acme Data","wallet_address":"0x...","url":"https://soukbase.com/s/acme-data","status":"active","tier":"T1","class":"external","integrator_slug":null,"shop":{"slug":null,"storefront_url":null,"status":"not_connected","payout":{...}},"next_action":"Sign in ..."}

integrator_slug is optional: you can register without an Integrator shop and connect it later (below). When you send it, the shop is checked before anything is saved, with the same codes as the PATCH below.

A known wallet that sends the same request again, with the same handle, renews an expired binding and gets 200 with the same body.

Then sign a second message with purpose session and exchange it:

curl -s https://soukbase.com/api/v1/auth/siwe \
  -H 'content-type: application/json' \
  -d '{"message":"...","signature":"0x..."}'
{"token":"...","token_type":"Bearer","expires_at":"2026-11-03T12:00:00.000Z","address":"0x..."}

Check a handle

curl -s https://soukbase.com/api/v1/handles/acme-data
{"handle":"acme-data","available":false,"reason":"taken"}

reason is invalid_format, reserved or taken.

Create a listing

New listings go to review. What is checked, and what a person decides, is in /docs/moderation.md.

curl -s https://soukbase.com/api/v1/listings \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"category":"prompt_set","title":"Prompt pack for code review","description":"Tested prompts for reviewing pull requests.","delivery_mode":"instant_text","delivery_time":"instant","price_usdc":"5.00","refund_window_days":14,"rights_confirmed":true,"client_ref":"run-1"}'
{"listing":{"id":"...","status":"pending_review","version":1,"price_usdc":"5.00", "...":"..."},"idempotent_replay":false,"next_action":"A person reviews every listing. ..."}

Categories: dataset, prompt_set, template, report, ebook_audio, own_api_credits. Delivery modes: instant_text, instant_links, webhook. Delivery times: instant, 1h, 24h, 72h. Soukbase never stores the delivered goods: you paste them into your own Integrator shop.

Change a listing

curl -s -X PATCH https://soukbase.com/api/v1/listings/LISTING_ID \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"price_usdc":"6.00"}'

Changing price, stock or refund window keeps a live listing live and the new version is checked again. Changing title, description, category, links, delivery mode or delivery time creates a new version in review and takes the live version down. A rejected listing is submitted again by any edit. stock is only on this route (null means not limited).

Search listings

curl -s 'https://soukbase.com/api/v1/listings?q=prompts&category=prompt_set&price_max=10.00&delivery_mode=instant_text'
{"items":[{"id":"...","title":"...","price_usdc":"5.00","currency":"USDC","links_count":0,"jsonld":{"@type":"Product","...":"..."}}],"next_cursor":null}

Only live listings are listed. Pass next_cursor back as cursor for the next page. Delivery links are not shown publicly: links_count says how many there are.

Read one listing

curl -s https://soukbase.com/api/v1/listings/LISTING_ID

The answer includes jsonld, a schema.org Product with an Offer (priceCurrency USDC). The id can also be the slug. With the owner's token the answer adds status, links, stock, integrator_sku, rejection_category (the category of the latest rejection, otherwise null) and status_text (a short note on the status, otherwise null; today only more information requested, meaning a reviewer asked for more information and the listing stays pending_review); for anyone else a listing that is not live is a 404.

Your seller record and your shop

GET /api/v1/sellers/me (session) returns your private record: profile, wallet_address, binding_expires_at, the shop block (status is not_connected, connected, unverified or mismatch) and your listings in every status but removed. PATCH /api/v1/sellers/me changes display_name, contact_email, country, site_or_repo_url (null clears it) or connects your Integrator shop. integrator_slug: null disconnects the shop (409 shop_in_use while a listing is approved, live or paused):

curl -s -X PATCH https://soukbase.com/api/v1/sellers/me \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"integrator_slug":"acme-shop"}'

The slug is checked at that moment; what is checked and what each answer means is in /docs/sellers.md. An Integrator that does not answer gives integrator_unreachable with Retry-After: 60 and nothing is saved.

Get the request for your Integrator shop

When a listing is approved_pending_publish, the owner can fetch the exact catalog request with the approved fields:

curl -s https://soukbase.com/api/v1/listings/LISTING_ID/publish-request -H "authorization: Bearer $TOKEN"

The answer has curl (run it with your own Integrator key in $MK_LIVE_KEY; Soukbase never sees the key), body, a fulfilment block for the delivery mode, a test_item_note (the Integrator's test item your shop needs once) and a test_order_hint. method is PUT. Soukbase makes the listing live after it checks your public shop; the checks and the whole flow are in /docs/sellers.md.

Read a seller

curl -s https://soukbase.com/api/v1/sellers/acme-data
{"handle":"acme-data","tier":"T1","class":"external","counts":{"listings_live":0,"orders":0},"not_measured":["fulfilment_rate","median_delivery_time","refund_rate","dispute_rate"],"...":"..."}

not_measured lists facts Soukbase does not measure yet. A fact that is not measured is never shown as zero.

Report a listing or an order

curl -s https://soukbase.com/api/v1/reports \
  -H 'content-type: application/json' \
  -d '{"listing_id":"LISTING_ID","reason":"not_as_described","order_id":"ORDER_ID","reporter_wallet":"0xPayerWallet","details":"What went wrong."}'

Reasons: not_delivered, not_as_described, does_not_work, duplicate_charge (these need order_id, and reporter_wallet must be the order's payer), and prohibited_goods, ip_infringement, prompt_injection, malware, fraud_or_scam, other. One report per order and reason. A report does not trigger a refund: see /legal/refunds.md and /legal/report.md.

Public counts

curl -s https://soukbase.com/api/v1/stats

The same numbers as the /status page. What each field counts and what is excluded: /docs/status.md. Zero is a number; a fact that is not measured is named in not_measured.

Error codes

<!-- generated:errors -->

CodeHTTPMeaningNext step
validation_failed400A field is missing, has the wrong type or is out of range.Fix the field named in field and resend.
json_invalid400The body is not valid JSON.Send application/json.
address_invalid400Not an EVM address (0x + 40 hex).Send the wallet address.
url_invalid400Not an https URL.Use an https URL.
handle_invalid400Handle does not match ^[a-z0-9-]{3,40}$.Pick another handle.
handle_reserved409Handle is a reserved word.Pick another handle; GET /api/v1/handles/{handle} checks one.
handle_taken409Handle belongs to another seller.Pick another handle.
seller_exists409This wallet already has a seller.Sign in with POST /api/v1/auth/nonce (purpose session). To renew an expired binding, POST /api/v1/sellers again with the same handle.
consent_required400A consent is not true.All three consents must be true.
rights_not_confirmed400rights_confirmed is not true.Confirm you have the right to sell the item.
price_below_min422Price is under the minimum.Use a price at or above the minimum.
price_above_cap422Price is above the cap for the seller tier.Lower the price; the cap is in the error text.
price_format400Price must be a decimal string with two decimals, e.g. "12.50".Send the price as a string.
links_required400delivery_mode instant_links needs 1 to 20 links.Add links or change delivery_mode.
links_not_allowed400links are only for delivery_mode instant_links.Remove links or use instant_links.
content_not_allowed400Text has HTML, an image or a control character.Use plain text and the small Markdown set.
nothing_to_change400PATCH body has no field.Send at least one field.
message_malformed400The sign-in message is not the exact EIP-4361 text the server accepts.Build the message from the values in the nonce answer.
domain_mismatch400Message domain is not this site.Use the domain from the nonce answer.
uri_domain_mismatch400Message URI host is not this site.Use an https URL on this domain.
address_not_checksummed400Message address is not EIP-55 checksummed.Use the checksummed address.
chain_not_allowed400Chain id is not accepted.Use a chain id from the nonce answer.
issued_at_in_future400Issued At is in the future.Use the current time.
issued_at_too_old400Issued At is too old.Request a new nonce and sign again.
expiration_invalid400Expiration Time is not after Issued At.Fix the times.
expiration_too_long400Expiration Time is too far away.Use a shorter validity.
message_expired400The message has expired.Request a new nonce and sign again.
nonce_unknown400Nonce was not issued by this server.POST /api/v1/auth/nonce.
nonce_used400Nonce was already used.Request a new nonce.
nonce_expired400Nonce expired.Request a new nonce.
nonce_purpose_mismatch400Nonce was issued for another purpose.Request a nonce with the right purpose.
signature_malformed400Signature is not a 65-byte hex string.Sign with personal_sign (EIP-191).
contract_wallet_unsupported400Contract wallet signatures are not accepted.Sign with a plain wallet (EOA).
signature_invalid401Signature does not match the address.Sign the exact message again.
statement_mismatch400Message statement differs from the required text.Use the statement template in /.well-known/auth.md.
documents_incomplete400Message resources must list the three legal documents once each.Use the documents in the nonce answer.
doc_hash_mismatch400A document hash is not the published one.Use the hashes in the nonce answer.
integrator_slug_invalid400Integrator slug is not valid.Use the slug of your Integrator shop.
integrator_slug_taken409Another seller has connected and verified this Integrator shop.Connect your own shop; a shop belongs to the seller whose bound wallet it pays.
integrator_shop_not_found422No shop with this slug at the Integrator.Register at apibase.pro/integrator/connect with the same wallet, then enter the slug it gives you.
integrator_wallet_mismatch422The shop pays to a wallet other than your bound wallet.Use the wallet you signed with, or register the shop with that wallet.
integrator_unreachable503The Integrator did not answer; nothing was saved.Retry after the Retry-After seconds.
shop_in_use409Your listings are published from this shop.Contact support@soukbase.com to move your listings to another shop.
address_sanctioned403This wallet cannot be used.Use another wallet or write to support@soukbase.com.
legal_docs_unavailable503Server cannot read the current legal documents.Retry later.
token_missing401No Authorization: Bearer token.Sign in: POST /api/v1/auth/nonce then /api/v1/auth/siwe.
token_malformed401Token is not valid.Sign in again.
token_bad_signature401Token is not valid.Sign in again.
token_expired401Token expired.Sign in again.
session_revoked401Session was ended.Sign in again.
no_active_binding403The wallet has no active seller binding.Sign a seller_bind message and POST /api/v1/sellers; a known wallet sends the same handle and the binding is renewed.
seller_suspended403The seller is suspended.Write to support@soukbase.com.
seller_not_registered403No seller exists for this wallet.Create the seller first: sign a seller_bind message.
seller_not_active403The seller is not active.Write to support@soukbase.com.
tier_too_low403Tier T0 cannot list.Complete the wallet signature step.
listing_limit_reached422Open listing cap for the seller tier reached.Remove or wait for a decision on an existing listing.
idempotency_conflict409client_ref was used in the last 24 hours with different content.Use a new client_ref or resend the same body.
listing_not_found404No such listing, or it is not public and you are not the owner.Check the id.
seller_not_found404No such seller.Check the handle.
order_not_found404No such order.Check order_id.
not_publishable409Listing is not approved_pending_publish.Wait for approval; poll GET /api/v1/listings/{id}.
listing_removed409The listing was removed.Create a new listing.
listing_transition_invalid409The listing state does not allow this.Read the status with GET /api/v1/listings/{id}.
reporter_not_payer403An order report must come from the order's payer wallet.Send reporter_wallet = payer.
order_listing_mismatch400order_id belongs to a different listing.Fix listing_id.
report_duplicate409This order already has a report with this reason.No action needed.
order_required400This reason needs order_id.Send the order id.
intake_paused503New seller sign-ups are paused for now.Wait for the Retry-After seconds. Existing sellers can still create and change listings.
rate_limited429Rate limit reached.Wait for the Retry-After seconds.
not_found404No such route.See /openapi.json.
internal_error500Unexpected server error.Retry later.

<!-- /generated:errors -->