PadelSwap API — for AI builders

Build AI agents for the PadelSwap marketplace on behalf of a logged-in user. The rule: anything a user can do in the app, an agent can do through the API — browse, make and counter offers, manage listings and orders, open disputes, leave reviews. The single exception is the final payment, which is always confirmed by a human in the browser. REST + a fully MCP-compatible server.

How buyers and sellers communicate. There is no free-form pre-purchase messaging. Before a sale, a buyer negotiates with structured offers — each offer can carry a short note, and the seller can counter_offer back. Free-form chat (send_message) opens only after payment, scoped to that order, between the two parties. So: pre-purchase → offers; post-purchase → order chat.

Quickstart

  1. The end-user generates a token at padelswap.ee/profile/api-tokens and shares it with your agent. The Helpful assistant preset is a sensible default; the user can broaden it later.
  2. The token (psk_live_…) is shown only once at creation. Store it as a secret on your side.
  3. Send it as a Bearer header on every request.
Hello worldbash
# Read the authenticated user's profile
curl https://padelswap.ee/api/v1/me \
  -H "Authorization: Bearer psk_live_…"

# Browse listings
curl "https://padelswap.ee/api/v1/listings?category=RACKET&q=hack&limit=5" \
  -H "Authorization: Bearer psk_live_…"

Model Context Protocol (MCP)

The same capabilities are exposed as MCP tools at https://padelswap.ee/api/mcp. Any MCP-aware agent — Claude Desktop, Cursor, Copilot Workspaces — can discover and call them automatically.

Claude Desktop configjson
{
  "mcpServers": {
    "padelswap": {
      "url": "https://padelswap.ee/api/mcp",
      "headers": {
        "Authorization": "Bearer psk_live_…"
      }
    }
  }
}

After saving the config and restarting your agent, the full tool set becomes available — one MCP tool per user action, grouped below:

  • list_listings
    read:catalog
    Search active listings
  • get_listing
    read:catalog
    Full listing details by id
  • get_me
    read:self
    Authenticated user profile
  • list_my_listings
    read:self
    Listings owned by the user (any status)
  • list_my_orders
    read:self
    Orders where the user is buyer or seller (role-annotated)
  • list_my_favourites
    read:self
    Listings the user has favourited
  • favourite_listing
    write:favourites
    Add a listing to favourites
  • unfavourite_listing
    write:favourites
    Remove a listing from favourites
  • send_message
    write:messages
    Send a chat message on a paid order
  • create_listing
    write:listings
    Create a listing for sale (with inline base64 photos)
  • update_listing
    write:listings
    Edit title / description / price / condition / delivery / city
  • delete_listing
    write:listings
    Soft-delete one of the user’s listings
  • place_offer
    write:offers
    Place an offer on a listing (≥70% of ask, < ask)
  • accept_offer
    write:offers
    Seller accepts a pending offer
  • reject_offer
    write:offers
    Seller rejects a pending offer
  • counter_offer
    write:offers
    Seller counters a buyer’s offer
  • withdraw_offer
    write:offers
    Withdraw your own pending offer
  • mark_shipped
    write:orders
    Seller records the parcel as shipped
  • mark_received
    write:orders
    Buyer confirms receipt (opens 48h window)
  • start_meetup
    write:orders
    Seller starts the in-person meetup
  • verify_meetup
    write:orders
    Seller enters the buyer’s 4-digit code
  • switch_to_omniva
    write:orders
    Buyer switches a meetup to parcel-locker
  • retry_shipment
    write:orders
    Seller re-runs Omniva label generation
  • open_dispute
    write:disputes
    Buyer opens a dispute (48h window)
  • respond_dispute
    write:disputes
    Seller responds to a dispute
  • withdraw_dispute
    write:disputes
    Buyer withdraws their dispute
  • submit_review
    write:reviews
    Buyer reviews the seller (1–5 stars)
  • delete_review
    write:reviews
    Delete your own review (in window)
  • update_profile
    write:profile
    Set phone / Playtomic URL / chat-email toggle
  • follow_seller
    write:favourites
    Follow a seller’s new listings
  • unfollow_seller
    write:favourites
    Unfollow a seller
  • search_rackets
    read:catalog
    Find Padelful racket specs by name
  • compare_rackets
    read:catalog
    Compare 1–3 rackets side-by-side
  • prepare_checkout
    write:checkout
    Prepare a purchase. Returns a one-time URL the user opens to confirm payment.

Protocol version 2024-11-05 · transport: http+jsonrpc · methods: initialize, ping, tools/list, tools/call.

Auth + scopes

Tokens are scoped — the agent gets exactly the permissions you grant when creating it, nothing more. The available scopes:

ScopeIn presetWhat it does
read:catalogallBrowse listings, search, fetch racket specs.
read:selfallThe user's profile, orders, and favourites.
write:favouriteshelpful + fullAdd or remove favourites; follow or unfollow sellers.
write:messageshelpful + fullSend chat messages on the user’s orders (post-payment order chat only).
write:reviewshelpful + fullLeave, edit, or delete the user’s review of a seller.
write:listingsfullCreate, edit, and remove the user’s own listings.
write:offersfullPlace / withdraw offers as a buyer; accept / reject / counter offers as a seller.
write:ordersfullOrder lifecycle: mark shipped, confirm receipt, meetup start + code, switch to Omniva, retry shipment.
write:disputesfullOpen, respond to, or withdraw a dispute on an order.
write:profilefullUpdate phone, Playtomic URL, and the chat-email notification toggle.
write:checkoutfullPrepare a checkout and receive a one-time confirm URL. The user confirms payment in their browser.

A scope mismatch returns 403 missing_scope. Pick a preset when creating the token, or hand-pick scopes via the custom option.

Purchases — the human in the loop

Agents can prepare a purchase, but cannot complete it. The API has no "confirm" or "capture" endpoint. prepare_checkout reserves the listing, sets up the payment, and returns a one-time URL. The user opens the URL in their own browser and clicks Confirm and pay there. Money never moves without an explicit human click.

Prepare a checkoutbash
curl -X POST https://padelswap.ee/api/v1/orders/checkout-prepare \
  -H "Authorization: Bearer psk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "listingId": "00000000-0000-0000-0000-000000000000",
    "delivery": {
      "method": "OMNIVA",
      "locker": { "zip": "10116", "name": "Solaris pakiautomaat" }
    }
  }'
Response (201)json
{
  "data": {
    "order_id": "…",
    "confirm_url": "https://padelswap.ee/checkout/confirm/tk_…",
    "expires_at": "2026-05-21T19:30:00Z",
    "total_cents": 10465,
    "currency": "EUR",
    "breakdown": {
      "item_cents": 9900,
      "buyer_protection_cents": 565,
      "shipping_cents": 0
    }
  }
}

Show the confirm_url to the user. They have 15 minutes to open it and complete the purchase. The URL is one-time-use; reopening after a successful purchase redirects to the order.

REST endpoints

MethodPathScopeDescription
GET/api/v1/meread:selfThe authenticated user’s profile
GET/api/v1/me/listingsread:selfListings owned by the user (any status)
GET/api/v1/me/ordersread:selfOrders the user is buyer or seller on. Optional ?side=buyer|seller
GET/api/v1/me/favouritesread:selfListings the user has favourited
POST/api/v1/me/favouriteswrite:favouritesAdd a listing to favourites
DELETE/api/v1/me/favourites/{listingId}write:favouritesRemove a listing from favourites
GET/api/v1/listingsread:catalogActive listings (paginated). Filters: ?q, ?category, ?limit, ?offset
GET/api/v1/listings/{id}read:catalogFull listing details
POST/api/v1/messageswrite:messagesSend a chat message on one of the user’s paid orders
POST/api/v1/listingswrite:listingsCreate a listing for sale (with inline base64 photos)
PATCH/api/v1/listings/{id}write:listingsEdit one of the user’s listings (title, description, price, condition, …)
DELETE/api/v1/listings/{id}write:listingsSoft-delete a listing (status → REMOVED)
POST/api/v1/offerswrite:offersPlace an offer on a listing as a buyer
POST/api/v1/offers/{id}/acceptwrite:offersSeller accepts a pending offer (24h payment window starts)
POST/api/v1/offers/{id}/rejectwrite:offersSeller rejects a pending offer
POST/api/v1/offers/{id}/counterwrite:offersSeller counters with a new linked offer (body: offeredCents, message?)
POST/api/v1/offers/{id}/withdrawwrite:offersWithdraw your own pending offer
PATCH/api/v1/mewrite:profileUpdate phone / playtomicUrl / chatEmailNotifications (any subset)
POST/api/v1/me/followswrite:favouritesFollow a seller (body: sellerId)
DELETE/api/v1/me/follows/{sellerId}write:favouritesUnfollow a seller
POST/api/v1/orders/{id}/mark-shippedwrite:ordersSeller marks shipped (body: tracking)
POST/api/v1/orders/{id}/mark-receivedwrite:ordersBuyer confirms receipt → DELIVERED + 48h window
POST/api/v1/orders/{id}/start-meetupwrite:ordersSeller starts the meetup (unlocks code entry)
POST/api/v1/orders/{id}/verify-meetupwrite:ordersSeller enters the buyer’s 4-digit code (body: code)
POST/api/v1/orders/{id}/switch-to-omnivawrite:ordersBuyer switches a meetup to parcel-locker (body: locker)
POST/api/v1/orders/{id}/retry-shipmentwrite:ordersSeller re-runs Omniva label generation
POST/api/v1/orders/{id}/disputewrite:disputesBuyer opens a dispute (body: reason, message, evidenceUrls?)
POST/api/v1/orders/{id}/dispute/respondwrite:disputesSeller responds to the dispute (body: message, evidenceUrls?)
POST/api/v1/orders/{id}/dispute/withdrawwrite:disputesBuyer withdraws the dispute
POST/api/v1/orders/{id}/reviewwrite:reviewsBuyer submits / edits a review (body: stars, text?)
DELETE/api/v1/orders/{id}/reviewwrite:reviewsDelete your review on this order
POST/api/v1/orders/checkout-preparewrite:checkoutPrepare a purchase. Returns a one-time confirm URL (15-min TTL).

Error format

Every error returns a JSON body with a stable code and a human-readable message:

Errorjson
{
  "error": {
    "code": "missing_scope",
    "message": "Token is missing required scope: write:checkout."
  }
}

Codes you'll see:

  • missing_token · malformed_token · invalid_token · revoked_token · expired_token — auth issues
  • missing_scope — token lacks the required scope
  • rate_limited — quota hit; see Rate limits below
  • bad_request · not_found · forbidden · precondition_failed — request issues
  • db_error · upstream_error — server-side

Rate limits

Each token: 60 requests per minute and 5,000 requests per day. Over the cap returns 429 rate_limited with a Retry-After header in seconds.

For partner integrations needing higher limits, write to support@padelswap.ee.

Activity log

Every authenticated request is recorded — endpoint, scope used, HTTP status, IP, and user-agent. Users review their own activity at Profile → AI tokens → Activity. Revoked tokens stop working within 30 seconds.

Design principles

  • Personal access tokens, not OAuth. The AI is the user's own agent — tokens are issued and revoked by the user, like an SSH key. No third-party app flow, no consent screens, no client registration.
  • Payment confirmation stays in the browser. Even with write:checkout, the AI can only prepare a purchase. The final confirm is a real click in the user's logged-in browser session. AI mistakes cap at a 10-minute listing reservation; money never moves without a human.
  • Same handlers behind REST and MCP. Pick the transport that fits your stack. Auth, scopes, rate limits, and audit log are identical across both.
Versioned at /api/v1. Breaking changes ship as /api/v2 with at least a six-month overlap window. Questions or feedback — support@padelswap.ee.