# Klatchit — full reference for agents > Klatchit is a US marketplace where independent sellers list competing > offers against a shared product catalog. Every listing is one seller's offer on > a canonical product: prices, conditions, and stores differ, the product does > not. Payments go direct to the seller of the chosen offer. This file is the long form of https://www.klatchit.com/llms.txt. The endpoint sections below are generated from the same schemas the server validates with, so they cannot drift from the implementation. Machine-readable equivalent: https://www.klatchit.com/api/public/v1/openapi.json Base URL: https://www.klatchit.com/api/public/v1 Authentication: none. No key, no account, no OAuth. ## Conventions - Success responses are `{ "data": ..., "meta": ... }`. `meta.request_id` correlates with the `X-Request-Id` response header — quote it in bug reports. - All money is an INTEGER number of CENTS in USD. A field ending `_cents` is never a decimal: `4999` means $49.99. - A PRODUCT is addressed by its `slug`, which is also its canonical page https://www.klatchit.com/p/{slug}. An OFFER is addressed by its `listing_id`, which identifies one seller's offer — not the product. - Inventory depth is never exposed. An offer reports a boolean `in_stock`. - Timestamps are RFC 3339 / ISO-8601 in UTC. - Reads are shared-cacheable: `Cache-Control: public, s-maxage=60` (300s for the category tree). `POST /cart-links` is always `no-store`. - Send a descriptive `User-Agent` identifying your agent and a contact. ## Errors Every failure is `{ "error": { "type", "code", "message", "param"?, "request_id" } }` with the matching HTTP status. - 400 invalid_request_error / validation_error — bad or missing parameters; `param` names the offending field, and a multi-issue failure adds an `errors` array of `{ param, message }`. - 404 invalid_request_error / resource_missing — no such publicly listed resource. A product that exists but is not public answers 404 identically, so this endpoint is not an existence oracle. - 429 rate_limit_error / rate_limited — see Rate limits. - 500 api_error / internal_error — retry with backoff. Never carries detail. ## Rate limits Limits are PER CLIENT IP (first `X-Forwarded-For` hop), applied as a token bucket: burst up to the capacity, then a steady refill. Buckets are per endpoint, so search and product lookups do not compete. - Catalog endpoints (/search, /products/{slug}, /categories): burst 30, refill 5/second. - POST /cart-links: burst 10, refill 1/second. - MCP (/api/mcp/mcp): burst 30, refill 5/second, in its own bucket. A 429 carries `Retry-After` in seconds. Honour it; do not retry tighter. These are current operating values, not a contractual guarantee. ## Endpoints ### GET /api/public/v1/search Search the public catalog Full-text + faceted product search over purchasable offers. Identical ranking, filtering, and facet counts to the site's own /search page. Each result carries an absolute product-detail URL (`/p/{slug}`) you can fetch via GET /products/{slug}. Parameters: - q (query, string, optional) — Free-text query; omit to browse. - category (query, string, optional) — Category slug; scopes the search to that subtree. - brand (query, array, optional) — Brand slugs. Repeatable, or comma-separated. - condition (query, array, optional) — Offer conditions. Repeatable, or comma-separated. - price_min_cents (query, integer, optional) - price_max_cents (query, integer, optional) - sort (query, enum("relevance"|"price_asc"|"price_desc"|"newest"), optional, default "relevance") - page (query, integer, optional, default 1) - page_size (query, integer, optional, default 24) — Results per page. Values above 60 are clamped, not rejected; the effective value is echoed in meta.page_size. Responses: - 200 — A page of product cards plus facet counts. data: array id: string slug: string title: string url: string image_url: string|null brand: object|null slug: string|null name: string category_breadcrumb: array slug: string name: string min_price_cents: integer|null currency: string offer_count: integer rating_avg: number|null rating_count: integer meta: object page: integer page_size: integer total: integer page_count: integer facets: object category: array ... brand: array ... condition: array ... price: array ... request_id: string - 400 — Error envelope (see Errors below). - 429 — Rate limited — see Retry-After. - default — Error envelope (see Errors below). ### GET /api/public/v1/products/{slug} Retrieve a product and its purchasable offers The full public product record: attributes, images, brand, GTIN/MPN, category breadcrumb, and every currently purchasable offer (cheapest first) with the buy-box winner identified. Inventory depth is never exposed — an offer reports a boolean `in_stock`. A product that is not publicly listed returns 404. Parameters: - slug (path, string, required) — The product slug, as it appears in /p/{slug}. Responses: - 200 — The product. data: object id: string slug: string title: string description: string|null url: string brand: object|null slug: string|null name: string category_breadcrumb: array slug: string name: string gtin: string|null mpn: string|null attributes: object images: array url: string alt: string|null width: integer|null height: integer|null rating_avg: number|null rating_count: integer currency: string min_price_cents: integer|null offers: array listing_id: string price_cents: integer list_price_cents: integer currency: string condition: enum("new"|"like_new"|"very_good"|"good"|"acceptable"|"refurbished"|"used"|"for_parts") condition_label: string store_name: string store_slug: string in_stock: boolean on_sale: boolean buy_box_listing_id: string|null meta: object request_id: string - 404 — Error envelope (see Errors below). - 429 — Rate limited — see Retry-After. - default — Error envelope (see Errors below). ### GET /api/public/v1/categories Retrieve the active category tree Every active category as a nested tree. Use a node's `slug` as the `category` parameter on /search; `path` is the materialised ltree path (e.g. `electronics.audio.headphones`). Responses: - 200 — The category tree. data: array slug: string name: string path: string children: array slug: string name: string path: string children: array ... meta: object count: integer request_id: string - 429 — Rate limited — see Retry-After. - default — Error envelope (see Errors below). ### POST /api/public/v1/cart-links Create a cart link for a human to open Mint a short-lived link that puts specific offers into the cart of whoever OPENS it. This is a handoff, not a purchase: it takes no payment, places no order, reserves no stock, and creates no cart on our side — it returns a URL for a person to open in their own browser, where the items appear in their own cart to review and check out themselves. There is no API for buying on a shopper's behalf. Identify each line by the `listing_id` of one offer from GET /products/{slug} (a listing_id picks one seller's offer, not the product). Quantities are 1-99 and up to 20 lines; a repeated listing_id has its quantities summed. Lines that are not purchasable right now are dropped and reported in `rejected` — a generic reason only. If nothing is purchasable the response is a 400 whose error object carries the same `rejected` list. The link expires in 7 days, and price and availability are re-checked when it is opened, so nothing here is a price guarantee. Responses are never cached. Request body (application/json, required): items: array — Listings to prefill, 1-20 lines. listing_id: string (uuid) quantity: integer Responses: - 200 — The cart link. data: object url: string expires_at: string accepted: integer rejected: array listing_id: string reason: enum("unavailable"|"insufficient_stock") meta: object request_id: string - 400 — Error envelope (see Errors below). - 429 — Rate limited — see Retry-After. - default — Error envelope (see Errors below). ## MCP server The same catalog is available over the Model Context Protocol. - Endpoint: https://www.klatchit.com/api/mcp/mcp - Transport: streamable HTTP, `POST` only. A `GET` is answered 405 by design — the server is STATELESS, so there is no session id to carry and no event stream to hold open. Build one server instance per request. - Authentication: none. No cookie is read; the trust level is identical to the anonymous JSON API above. - Tools: - `search_products` (read-only) — args: q, category, brand[], condition[], max_price_cents, page. Returns product cards + facet counts. - `get_product` (read-only) — args: slug. Returns full detail plus every purchasable offer, each with its `listing_id`. - `get_categories` (read-only) — no args. Returns the active category tree. - `create_cart_link` — args: items[{ listing_id, quantity }]. Mints the handoff link described below. It is NOT a purchase. Connect from a client config: { "mcpServers": { "klatchit": { "type": "http", "url": "https://www.klatchit.com/api/mcp/mcp" } } } From Claude: Settings -> Connectors -> Add custom connector, paste https://www.klatchit.com/api/mcp/mcp. There is no authentication step. From the MCP inspector (`npx @modelcontextprotocol/inspector`): transport "Streamable HTTP", same URL. ## Authenticated access (connect your own account) Everything above is the ANONYMOUS surface — no account, no key. A Klatchit account holder can also connect THEIR OWN agent so it acts as them, within scopes they explicitly consent to: - Authenticated MCP (OAuth): https://www.klatchit.com/api/agent/mcp — add it as a custom connector; the client walks the OAuth sign-in and consent, and the connection then acts as the consenting user, clamped to the granted scopes. A buyer grant reads their own orders, cart, and returns; a store-member grant reads the store's order queue. - Agentic checkout is possible on that connection, but only when the marketplace has enabled it, the grant carries the `checkout:execute` scope, and the buyer has turned on agentic purchasing with a fresh web consent and per-order + rolling spending caps. It is dark by default — treat it as unavailable unless a tool call proves otherwise. - Step-by-step setup for Claude, ChatGPT, and your own code: https://www.klatchit.com/developers/connect The tool catalogue below lists exactly which tools each connection sees. ## Agent tool catalogue (MCP) The Model Context Protocol tools, partitioned by the LEAST principal each needs. The anonymous tools are what the public MCP above exposes; the rest are reachable only over the AUTHENTICATED MCP (https://www.klatchit.com/api/agent/mcp, OAuth) after the connecting user consents to the matching scope. tools/list is always filtered to the caller, so a connection never sees a tool it cannot use. ### Anonymous tools (public MCP + every authenticated connection) - `search_products` — Search the Klatchit marketplace catalog for products that are for sale right now from at least one seller. Returns a page of product cards — each with a slug, title, brand, canonical product-page URL, image, `min_price_cents` (the cheapest current offer, i.e. the buy-box price), `buy_box_listing_id` (the winning offer's listing_id, the default seller to buy from), and the number of competing seller offers — plus the total match count and facet counts you can use to narrow the next search. Call get_product with a returned slug to see full details and compare every offer (price, seller rating, condition, handling time, free shipping, shelf life) before choosing. Exact shipping cost and delivery are finalised at checkout. Only publicly listed, purchasable products are ever returned. - `get_product` — Fetch the full public detail for ONE product by its slug: description, images, specifications, brand, category trail, rating, and the complete list of currently purchasable offers from competing sellers. Offers are ranked buy-box-winner FIRST, and exactly one carries `is_buy_box: true` — the default offer to pick unless the buyer prioritises something specific. Each offer gives the full purchase-decision context: `price_cents` (the current effective price, sale-aware) and `list_price_cents` (regular), `on_sale` / `promo_percent_bps`, `condition`, `store_name` with `seller_rating_avg`/`seller_rating_count`, `in_stock`, `handling_days` (ships in N business days), `free_shipping`, and `expires_on` (shelf life, when disclosed). DEFAULT to the `is_buy_box` offer; switch to another only when the buyer prioritises price, speed (lower handling_days), seller rating, or condition. Exact shipping cost and delivery dates are NOT here — they are finalised at checkout (use get_checkout_preview). Get the slug from search_products. Reports not-found if the slug does not exist or the product is not publicly listed. - `get_categories` — List the marketplace's active category taxonomy as a tree. Each node has a slug, a display name, a dotted path (e.g. 'electronics.audio.headphones'), and its children. Call this when you need a valid category slug for search_products' 'category' parameter. Takes no arguments; the taxonomy changes rarely, so one call per conversation is enough. - `create_cart_link` — Create ONE link that, when the USER opens it in their own browser, puts the chosen offers into their own Klatchit shopping cart, ready for them to review and pay for. THIS DOES NOT BUY ANYTHING: you cannot purchase, reserve, or pay for anything, and this tool takes no payment and places no order — it only prepares a cart a human must then check out themselves. Give the returned 'url' to the user and tell them to open it. Identify each line by the 'listing_id' of a specific offer from get_product (a listing_id picks one seller's offer — not the product), plus a quantity of 1-99, up to 20 lines. Lines that are sold out or no longer for sale are dropped and reported in 'rejected'; check 'accepted' to see how many made it. The link expires in 7 days, and prices and stock are re-checked when the user opens it. - `search_help` — Search Klatchit's own help center — published help articles AND the FAQ — for how the marketplace actually works: policies, fees, returns, shipping, buying, selling, and developer/API topics. Returns a ranked list of hits. An ARTICLE hit has source 'article', a 'slug', a title, and a short snippet — pass EXACTLY that slug to get_help_article to read the full article. Most article hits also carry a 'url': when you point the user at an article, give them that url, NEVER the slug — a slug is an internal identifier they cannot open. A FAQ hit has source 'faq', a 'question', and its COMPLETE 'answer' inline — quote the answer directly; there is nothing further to fetch for a FAQ, and a FAQ has no url of its own. This is the ONLY authoritative source for what Klatchit's rules are — never state a fee, window, or policy you did not find here. - `get_help_article` — Fetch the full markdown body of ONE published Klatchit help article by its slug (the 'slug' field of a search_help hit whose source is 'article'). Returns the title, audience, the complete body, and — for articles that are also on the public web — a 'url' you can hand to the user. Quote from the body when answering a policy or how-to question. NEVER pass a FAQ id here: FAQ hits already include their complete 'answer' in the search_help results. Reports not-found if the slug does not exist, is a draft, or is outside your audience (the three are indistinguishable on purpose). - `get_connection_guide` — Return step-by-step instructions for connecting a client to Klatchit: which URL to paste, a copy-pasteable config or command snippet, and links to the docs and (for account access) the pages where the human mints an API key or grants an OAuth consent. Choose 'client' from claude, claude_code, chatgpt, openai_api, rest_api. 'access' is 'public' (the anonymous catalog — no account, no key) or 'account' (the human connects their OWN Klatchit account over OAuth, or a seller API key, so an agent can see their own data and, with consent, act for them). This tool NEVER mints a credential — it only tells the human where to do it themselves. Use it to walk a user through setup. ### Buyer account tools (authenticated MCP only — the signed-in shopper's own data) Each requires the buyer scope the equivalent account page needs; a connection that did not consent to it never sees the tool. - `get_my_orders` — List the signed-in user's own Klatchit orders, newest first: each row has the order id, human order number, status, total in cents, item count, how many sellers it spans, and the order date. Use the returned order id with get_order_detail to see the full breakdown. Returns only THIS user's orders. Prices are integer CENTS in USD (4999 = $49.99). - `get_order_detail` — Fetch the full detail of ONE of the signed-in user's orders by its order id (from get_my_orders): totals, shipping address, and every per-seller group with its line items (title, quantity, unit and line price in cents, fulfillment method, status). Answers not-found if the id is not a real order OR does not belong to this user — the two are indistinguishable on purpose, so this can never confirm another user's order exists. Prices are integer CENTS. - `get_order_tracking` — Fetch the shipping / fulfillment state of ONE of the signed-in user's orders by its order id (from get_my_orders). Returns a per-seller (sub-order) row with its current status and, where a shipment or pickup exists, the carrier, tracking number, and shipped/delivered timestamps (and, for local pickup, the ready-for-pickup, pickup-deadline, and picked-up times). Use this to answer 'where is my order' — get_order_detail lists what was bought; this says where each seller's part is. Answers not-found if the id is not a real order OR does not belong to this user (indistinguishable on purpose, so it can never confirm another user's order exists). Timestamps are ISO 8601 strings. - `get_cart` — Return the signed-in user's current shopping cart: per-seller groups with their line items (product title, condition, unit price, quantity, line total, stock/availability), plus subtotal, discounts, shipping, tax, and grand total — all in cents. Reflects live prices and stock. This is a READ ONLY view; it cannot add, change, remove, or check out anything. Prices are integer CENTS. - `get_my_returns` — List the signed-in user's own return requests, newest first, in bounded pages: each has a return id, the order number and order id it belongs to, the store name, status, reason, method, the refund amount in cents, and when it was requested. Returns up to `limit` returns (default 25, max 100) plus `next_cursor`: pass it back as `cursor` to fetch the next page (null means no more). Returns only THIS user's returns. Prices are integer CENTS. - `add_to_cart` — Add an offer (a specific seller's listing of a product) to the signed-in user's cart, then return the updated cart. `listing_id` is an offer id from get_product's offers (or a search result), NOT a product id. Prefer the offer marked `is_buy_box: true` (or a search card's `buy_box_listing_id`) unless the buyer has asked to prioritise price, speed, seller rating, or condition. `quantity` is a whole number of units, 1-99, defaulting to 1; adding an offer already in the cart increases its quantity. Fails if the offer is out of stock or not purchasable. Returns the full updated cart view (per-seller groups, line items, subtotal/shipping/tax/total) — all money in integer CENTS in USD (4999 = $49.99); final shipping and delivery are set at checkout. - `update_cart_item` — Set the quantity of a line already in the signed-in user's cart, identified by its offer's `listing_id`, then return the updated cart. `quantity` is a whole number 0-99; a quantity of 0 removes the line entirely (same as remove_cart_item). A quantity above available stock is clamped down to what's in stock. Returns the full updated cart view — all money in integer CENTS in USD. - `remove_cart_item` — Remove a line from the signed-in user's cart, identified by its offer's `listing_id`, then return the updated cart. Removing a line that is not in the cart is a no-op (still succeeds). Returns the full updated cart view — all money in integer CENTS in USD. - `apply_cart_coupon` — Apply a seller's coupon code to that seller's group in the signed-in user's cart. `seller_user_id` is the canonical seller id of the group the coupon belongs to (from the cart view's per-seller groups). The code is re-validated against the group's current subtotal. On success `applied` is true with the discount in cents and a label; if the coupon is invalid/expired/below the minimum spend, `applied` is false with a machine reason (the code is NOT stored). Either way the updated cart view is returned in `cart`. Money in integer CENTS in USD. - `remove_cart_coupon` — Remove any coupon applied to a seller's group in the signed-in user's cart, then return the updated cart. `seller_user_id` is the group's canonical seller id (from the cart view). A no-op if that group has no coupon. Returns the full updated cart view — money in integer CENTS in USD. - `request_return` — Request a return for one of the signed-in user's DELIVERED sub-orders (one seller's part of an order). `suborder_id` and each `suborder_item_id` come from get_order_detail's per-seller groups and line items. `reason` must be one of the allowed reasons; `items` lists which purchased lines and how many units of each to return. Fails (with a clear reason) if the order is not the user's, not delivered, past the seller's return window, or the seller does not accept returns. On success returns the new return id, whether the seller auto-authorized it, the return method (ship or pickup drop-off), any pickup drop-off code, and the full return detail. Refund amounts are integer CENTS in USD. - `get_watchlist` — List the products the signed-in user has saved to their watchlist, newest-saved first, in bounded pages. Each item has the product id, slug, title, image url, average rating and rating count, the current cheapest PURCHASABLE offer price (min_price_cents, or null when nothing is buyable right now), how many offers exist, and when it was saved. Returns up to `limit` items (default 25, max 100) plus `next_cursor`: pass it back as `cursor` to fetch the next page (null means no more). Use the product_id/slug with get_product to see full detail, or add_to_watchlist / remove_from_watchlist to change the list. Prices are integer CENTS in USD (4999 = $49.99). - `add_to_watchlist` — Save a product to the signed-in user's watchlist so they can track it. `product_id` is a PRODUCT id (from get_product or a search result / watchlist / order line), NOT an offer/listing id. Idempotent: saving a product that is already watched leaves it watched (never removes it). Returns the product id and the resulting watched state. - `remove_from_watchlist` — Remove a product from the signed-in user's watchlist. `product_id` is a PRODUCT id (as it appears in get_watchlist). Idempotent: removing a product that is not on the watchlist is a no-op and still succeeds. Returns the product id and the resulting watched state. - `get_addresses` — List the signed-in user's saved addresses (shipping/billing), default first. Each has an id, type, full name, optional company, street lines, city, region, postal code, country, optional phone, and whether it is the default. Returns only THIS user's addresses. Read-only — it cannot add, change, or delete an address. - `get_payment_methods` — List the signed-in user's saved payment cards for DISPLAY only: each has an opaque id, card brand, last 4 digits, and expiry month/year, plus whether it is the default. It NEVER returns a full card number, CVC, or the payment processor's token — none is available to emit. Use this to let the user recognise which card is on file (e.g. 'Visa ending 4242'). Read-only. - `get_agent_consent` — Report THIS connection's agentic-purchasing consent — the standing decision the human made about what an agent may buy on their behalf. When configured, returns the status and the caps the agent is bound by: per-order, daily, and weekly spend caps, the amount above which a purchase needs the human to confirm, the max orders per day, which categories are allowed, the address policy, and the display-only brand/last4 of the card on file. When purchasing was never enabled (or was revoked), returns configured:false / status 'not_configured'. This is READ ONLY and never authorizes or executes a purchase — use it to explain to the user what you are and aren't allowed to buy. Money is in integer CENTS in USD. - `write_review` — Post a verified-purchase review, on behalf of the signed-in user, for a product they have BOUGHT and had DELIVERED. `product_id` is a PRODUCT id (from an order line, get_product, or a search result — NOT an offer/listing id). `rating` is a whole number of stars 1-5; `title` and `body` are optional free text. The review posts under the user's own account, marked a verified purchase. Fails with a clear reason if the user has no delivered purchase of the product (not eligible), has already reviewed it (one review per product), or the rating is out of range. Returns the new review id. - `create_address` — Save a NEW address to the signed-in user's address book. Provide the full address (recipient name, street, city, and — outside single-line locales — region/postal code/country). Set `is_default` to make it the default for its type (shipping or billing), which demotes the previous default of that type. Returns the new address id and the user's full updated address list. NOTE: when you are a connected app and pass `is_default: true`, the address is saved but making it the default is a sensitive change — the result is `{kind: "step_up_required", url, ...}` (NOT an error). Show the `url` to the human so they can approve it (they re-enter their password); the default takes effect only after they approve. Saving without `is_default` never requires this. - `update_address` — Update one of the signed-in user's saved addresses, identified by `address_id` (from get_addresses). This REPLACES the whole address, so send every field you want to keep — an omitted optional field is cleared. Set `is_default` to make it the default for its type (demoting the previous default). Answers not-found if the id is not a real address of THIS user. Returns the address id and the user's full updated address list. NOTE: when you are a connected app and pass `is_default: true`, the edit is saved but making it the default is a sensitive change — the result is `{kind: "step_up_required", url, ...}` (NOT an error); show the `url` to the human to approve (they re-enter their password), and the default takes effect only after they approve. - `delete_address` — Delete one of the signed-in user's saved addresses by `address_id` (from get_addresses). Scoped to the caller: deleting an id that is not the user's own is a no-op that still succeeds (`deleted` is false). If the deleted address was the default and another of the same type remains, one is auto-promoted to default so checkout is never left without one — its id is returned as `promoted_id` (null when nothing was promoted). Returns whether a row was removed, the promoted id, and the user's full updated address list. - `update_profile` — Update the signed-in user's editable profile fields — their display name and/or phone number. Only the fields you pass are changed. This tool CANNOT change the account email: an email change is a sensitive, identity-level action that must go through Klatchit's verified email-change flow (a confirmation link sent to the current address), so `email` is not an accepted field and passing it is rejected. Returns the fields that were applied. - `create_store` — Turn the signed-in user into a Klatchit SELLER by creating their store — the very first step of selling. Pass the store 'name' (2–120 chars) and optionally a 'slug' for the store URL (auto-derived from the name when omitted). This promotes the user from buyer to seller and makes them the store's owner. A user who already owns a store is told so (a person has one primary store). After it succeeds, call get_stripe_onboarding_link to set up payouts and get_seller_onboarding_status to see everything still required to go live. Returns the new store's id and slug. ### Seller store tools (authenticated MCP only — one authorized store) Each requires the store member's live role AND the consented scope, clamped to the store the grant names. - `list_open_orders` — List the active store's orders that need attention, newest first, ONE BOUNDED PAGE at a time (never the whole order history). Defaults to PAID orders awaiting fulfillment; pass 'status' to see another state. Each row has the suborder id, order id and number, buyer email, status, fulfillment method (ship or pickup), item count, total in cents, and carrier/tracking if set. Use 'limit' (1-100, default 25) to size the page and pass the returned 'next_cursor' back as 'cursor' to fetch the next page; 'next_cursor' is null on the last page. Scoped to the store the connection was authorized for; returns nothing from any other store. Prices are integer CENTS in USD. - `get_shipment_workload` — Return the active store's current fulfillment workload as counts: how many paid orders are awaiting shipment ('to_ship'), how many pickup orders are awaiting handoff ('to_handoff'), and how many returns are awaiting the seller's review ('returns_to_review'). One aggregate over the store's live orders and returns — a fast at-a-glance queue size. Scoped to the authorized store only. - `get_my_listings` — List the active store's own listings ONE BOUNDED PAGE at a time (never the whole catalog), with the product title, condition, price and sale price (integer CENTS), on-hand stock, SKU, and status (draft, active, paused, out_of_stock, archived). Pass 'search' to match a product title or SKU. Use 'limit' (1-100, default 25) to size the page and pass the returned 'next_cursor' back as 'cursor' to fetch the next page; 'next_cursor' is null on the last page. Pages walk the store's listings in a stable update order. This is where you get the LISTING IDs the write tools (update_listing, publish_listing, set_listing_status, update_inventory) act on. Scoped to the authorized store; never returns another store's listings. - `get_listing` — Fetch one of the active store's listings by its id (from get_my_listings), with the full editable field set the write tools accept — price and sale price, price guardrails, sale window, stock, SKU, handling days, shipping template, consumable disclosure, and status. Prices are integer CENTS. An id from another store, or an unknown id, returns not_found. - `create_listing` — Create a DRAFT listing for the active store against a catalog product. Identify the product by 'product_id' (a Klatchit catalog product) OR by 'identifier' (a UPC/EAN/ISBN/GTIN barcode, which is matched to the catalog); supply exactly one. 'price_cents' is required and is an integer in CENTS (4999 = $49.99); 'stock' defaults to 0. The listing starts as a draft and is NOT purchasable until you publish it (publish_listing) — publishing runs the store's purchasability, category, and brand gates. A price outside the store's guardrails, a gated category/brand, or an unmatched barcode is reported back so you can fix and retry. Returns the created listing. - `update_listing` — Edit an existing listing's price, condition, SKU, handling days, shipping template, price guardrails, sale window, or consumable disclosure. Only the fields you pass are changed; omitted fields are left untouched. Prices are integer CENTS. Stock is NOT changed here — use update_inventory so the movement ledger stays complete. An id from another store, or an unknown id, returns not_found; a price outside the store's guardrails is reported so you can retry. Returns the updated listing. - `set_repricer_config` — Enable, disable, or reconfigure a listing's automatic repricer. When enabled, a strategy is required: 'match_lowest' sets the price to the cheapest competing offer; 'beat_by' undercuts the cheapest competitor by 'beat_by_cents' (integer cents ≥ 1, REQUIRED for this strategy); 'win_buy_box' undercuts the current buy-box WINNER (which respects seller-health demotion, so it is not always the cheapest) by 'beat_by_cents', or by the store's default step if omitted. The listing's min_price_cents / max_price_cents guardrails are the inviolable floor and ceiling — the repricer never prices outside them (set them with update_listing). Offers from the same economic entity (the seller itself, and any store displayed as Klatchit for the product's brand) are never treated as competition. Pass enabled=false to turn repricing off. An id from another store returns updated=false. - `publish_listing` — Activate a draft or paused listing so it becomes purchasable. This runs the store's purchasability gate (the store must be able to accept charges) plus the category, brand, and consumable-disclosure gates — if any fails the reason is returned so you can fix it (e.g. finish onboarding, disclose an expiry date) and retry. An id from another store, or an unknown id, returns not_found. Returns the now-active listing. - `set_listing_status` — Move a listing to a new status: 'active' (publish — runs the same purchasability/category/brand gates as publish_listing), 'paused' (temporarily hide from buyers, reversible), 'archived' (retire it), or 'draft' (back to unpublished). Use this to unpublish (pause) or archive a listing. An id from another store, or an unknown id, returns not_found; a failed activation gate is reported so you can retry. Returns the listing at its new status. - `update_inventory` — Adjust a listing's on-hand stock by a signed DELTA and record the movement in the stock ledger (which is why this is a delta, not an absolute set — the ledger stays complete). Identify the listing by 'listing_id' OR its 'sku'. Use a positive delta to restock and a negative delta to remove units; stock can't go below zero (an over-decrement is refused). Crossing zero flips the listing between active and out_of_stock automatically. Returns the new on-hand balance. - `ship_order` — Mark a SHIP suborder (from list_open_orders) as shipped, recording the carrier and tracking number, and (unless you pass notify:false) email the buyer their shipped notice. Re-shipping the same suborder with the SAME carrier + tracking is treated as a retry (no second shipment, no duplicate email); re-shipping with DIFFERENT tracking is refused so the buyer's tracking is never overwritten. A pickup order cannot be shipped — use mark_ready_for_pickup. Returns the updated order and whether the buyer was notified. - `mark_ready_for_pickup` — Mark a PAID local-PICKUP suborder ready for collection. This issues the buyer a one-time pickup code (emailed to them — the code is the buyer's only copy and is NEVER returned here) and arms the no-show timer, exactly as the seller dashboard does. A ship order cannot be made ready for pickup — use ship_order. An id from another store, or an unknown id, returns not_found. Returns the updated order and whether the buyer's code email was sent (if notified is false, the buyer must be reached another way — the code cannot be reissued). - `add_order_note` — Append an INTERNAL note to an order's event trail (from list_open_orders). The note is for the store's own records and is not shown to the buyer. An id from another store, or an unknown id, returns not_found. Returns ok on success. - `authorize_return` — Authorize a REQUESTED return (a return awaiting the seller's review; find these via get_shipment_workload's returns_to_review count and the store's returns queue). For a mail-back return the buyer is emailed the return instructions; for an in-person drop-off return the buyer is emailed a one-time drop-off code (the buyer's only copy — never returned here) that the seller enters at the counter to start the refund. Only a 'requested' return can be authorized. Returns the updated return and whether the buyer was notified. - `decline_return` — Decline a REQUESTED return with a reason note, and email the buyer that the return was not authorized. Only a 'requested' return can be declined. The note is shown to the buyer as the decline reason. Returns the updated return and whether the buyer was notified. - `receive_return` — Mark an AUTHORIZED return received, which ISSUES THE REFUND on receipt (item value + tax, plus original shipping when the return completes the order, minus any restocking fee). Because it moves money, this requires BOTH orders:write and refunds:create. For an in-person drop-off return you must pass the buyer's one-time 'dropoff_code'. Only an authorized/shipped return can be received; the refund is idempotent (a retry never double-refunds). The buyer is emailed their refund notice. Returns the updated return and whether the buyer was notified. - `create_refund` — Refund an order (a suborder id from list_open_orders). Omit 'amount_cents' for a FULL refund of the remaining balance, or pass an integer CENTS amount for a partial refund. Pass a stable 'idempotency_key' so a retry with the same key never issues a second refund (the money provider is deduped by it). An over-refund or an unrefundable order is reported so you can adjust. Returns the created refund. - `get_statement` — Return the store's disbursement statement over a date range (default: the trailing 30 days) straight from the ledger — category totals, the platform commission, net payout, and a per-suborder fee breakdown, all in integer CENTS. This is the money view for reconciling payouts. Pass 'from'/'to' as YYYY-MM-DD to bound the window. - `get_sales_report` — Return the store's sales report over a date range (default: the trailing 30 days). group_by='product' (default) gives per-product units, gross, returns, net, views, and conversion; group_by='day'|'week'|'month' gives per-period orders, units, gross, refunds, and net. Money is integer CENTS. Pass 'from'/'to' as YYYY-MM-DD to bound the window. - `create_coupon` — Create a discount code for the active store. 'code' is what buyers type (unique per store, case-insensitive). 'type' is 'percent' (supply 'percent_bps' in BASIS POINTS — 1000 = 10%) or 'fixed' (supply 'amount_off_cents' in CENTS). Optional gates: 'min_spend_cents' (minimum group subtotal), 'max_redemptions' (global cap), 'max_per_user' (per-buyer cap), and a 'starts_at'/'ends_at' window (ISO 8601). The coupon is active by default. A duplicate code or a missing value for the chosen type is reported so you can fix it and retry. Returns the created coupon. - `update_coupon` — Edit an existing coupon (by 'coupon_id' from list_coupons). ONLY the fields you pass are changed; every field you omit keeps its current value (this is a partial merge, NOT a full replace). Pass an explicit null on an optional field (e.g. 'ends_at': null, 'max_redemptions': null) to clear it. Changing 'type' to 'percent' requires percent_bps to be set (existing or provided); to 'fixed' requires amount_off_cents. To just pause/resume a coupon use set_coupon_active instead. A coupon_id from another store, or an unknown id, returns not_found. Returns the updated coupon. - `list_coupons` — List the active store's discount codes, newest first, ONE BOUNDED PAGE at a time, with the code, type and value (percent basis points or CENTS off), minimum spend, caps, how many times each has been redeemed, its active flag and any scheduled window. Use 'limit' (1-100, default 25) to size the page and pass the returned 'next_cursor' back as 'cursor' to fetch the next page; 'next_cursor' is null on the last page. This is where you get the COUPON IDs the write tools (update_coupon, set_coupon_active, delete_coupon) act on. Scoped to the authorized store; never returns another store's coupons. - `get_coupon` — Fetch one of the active store's coupons by its id (from list_coupons), with its full definition — code, type and value, minimum spend, global and per-user caps, redemption count, active flag, and scheduled window. A coupon_id from another store, or an unknown id, returns not_found. - `set_coupon_active` — Activate ('active': true) or pause ('active': false) one of the active store's coupons by id (from list_coupons) — a reversible on/off switch that leaves the rest of the coupon untouched (unlike update_coupon, which replaces the whole definition). A paused coupon stops applying at checkout but keeps its redemption history. A coupon_id from another store, or an unknown id, reports updated: false. - `delete_coupon` — Permanently delete one of the active store's coupons by id (from list_coupons). This removes the code entirely — to keep the code but stop it applying, use set_coupon_active with active: false instead. A coupon_id from another store, or an unknown id, reports deleted: false. - `get_account_health` — Return the active store's account-health standing — exactly what the /sell/health dashboard shows. Includes the current health tier (healthy, at_risk, warned, restricted, suspended), the four performance metrics vs their targets (order defect rate, late shipment rate, cancellation rate, valid tracking rate) each with its raw numerator/denominator, threshold, whether it meets target, and its trend delta, plus the monitored-only signals and any active platform notices. Percentages are numbers; 'below_floor' means the store has too few orders for that metric to be enforced yet. Read-only, scoped to the authorized store. - `create_product` — Propose a NEW catalog product for the marketplace. The product is SUBMITTED FOR REVIEW: it lands in a 'pending' state, is NOT yet in the catalog, and a Klatchit admin must approve it before anyone (including this store) can create a listing on it — exactly like adding a product in the seller dashboard. Identify the category and brand by their ids: use list_categories to find a 'category_id' and find_brand to find a 'brand_id' (both are required, and the brand must already exist — this tool never creates a brand). If you supply a 'gtin' (UPC/EAN/ISBN/GTIN barcode) that already matches a catalog product, NO new product is created and you are told to create a listing on the existing one instead (barcodes are unique across the catalog). Optional 'attributes' are category-specific fields (a flat map of string/number/boolean values) validated against the category. This tool CANNOT approve or publish a product; it only feeds the moderation queue. Returns the created product's id, slug, and review state. - `add_product_images` — Attach one or more images (up to 8) to a catalog product THIS store authored — in any state (pending review, rejected, or active). Identify it by 'product_id' (from create_product) and pass public http(s) 'image_urls'; each is fetched server-side behind an SSRF guard and added to the product's photos, with the first image on a product becoming its default. A product this store did NOT author (or an unknown id) is refused — holding an id is never enough to attach images to a product you don't own. A URL that can't be fetched is reported per-URL without failing the others. Returns per-URL results and the product's new image count. - `list_categories` — Look up catalog CATEGORY ids to pass to create_product. Pass 'query' to match a category by name (case-insensitive substring) — each result has the category id, its name, its slug, and a 'breadcrumb' showing its place in the tree (e.g. 'Electronics > Audio > Headphones'). Omit 'query' to list the top of the category tree. Use 'limit' (1-100, default 50) to bound the result count. The returned 'id' is what create_product's 'category_id' expects. Read-only. - `find_brand` — Look up an existing catalog BRAND id to pass to create_product. Pass 'query' as a brand name (case-insensitive substring) or a brand id; each result has the brand id and name. create_product requires an EXISTING brand — this tool never creates one, so if a brand isn't found the seller must add it in the web dashboard first. Use 'limit' (1-50, default 20) to bound the result count. The returned 'id' is what create_product's 'brand_id' expects. Read-only. - `get_stripe_onboarding_link` — Start (or resume) Stripe Connect payout onboarding for the active store and return the HOSTED onboarding URL. IMPORTANT: identity verification (KYC) is performed by Stripe on their own hosted page and CANNOT be completed by this tool — you MUST give the returned 'onboarding_url' to the human and have them open it in a browser to enter their business and bank details. This tool only creates the connected account (if one doesn't exist yet) and hands back the link; the link is single-use and short-lived, so fetch a fresh one each time the human needs it. Once they finish on Stripe, call get_seller_onboarding_status to confirm charges and payouts are enabled. If it returns ok:false, relay the message: reason 'payments_unconfigured' is a platform-side step, 'house_store' means the store doesn't use Stripe, and 'stripe_unavailable' / 'reconnect_unavailable' are surfaced as clean guidance (never a raw provider error). Requires the payments:manage permission (store owner). - `get_seller_onboarding_status` — Report exactly where the active store is in going live so you can tell the human precisely what's left. Returns whether its Stripe connected account exists and can accept charges ('charges_enabled') and receive payouts ('payouts_enabled'), whether Stripe still needs details ('details_submitted'), whether the connection was revoked ('disconnected'), and the list of setup steps still missing ('remaining_steps', each with a human label — e.g. business address, return policy, a card on file, a verified bank). Most important: when charges_enabled is false, the human still needs to finish Stripe's HOSTED identity verification via get_stripe_onboarding_link. 'payments_pending' true means Stripe isn't configured on this deployment yet (a platform step). 'next_action' summarizes the single most useful thing to do next. Requires the payments:manage permission (store owner). ### Checkout (authenticated MCP only — gated) Reachable only when the marketplace has enabled agentic checkout, the grant carries `checkout:execute`, and the human has turned on agentic purchasing with a fresh web consent and spending caps. It is dark by default; the in-app assistant and any session-derived principal can NEVER hold this scope. - `place_checkout_order` — Place a Klatchit order from the signed-in user's cart, charging the card the human authorized for agentic purchasing. REQUIRES the human's standing purchase consent and the checkout:execute scope. Depending on the human's guardrails the result status is one of: 'placed' (charged), 'requires_confirmation' (the human must approve it in their account first — poll the returned confirmationId), 'requires_authentication' (the human must verify their card on the web), 'payment_failed' (declined), 'needs_reconciliation' (manual review), or 'blocked' (a spending or velocity limit; the reason says which). Pass a stable idempotency_key so a retry never places a second order. Never charges more than the human's per-order limit. Prices are integer CENTS in USD (4999 = $49.99). - `get_checkout_preview` — Re-derive the signed-in user's cart total AND the guardrail verdict WITHOUT placing an order — call this before place_checkout_order to know what will happen and to get a trustworthy total. REQUIRES the human's checkout consent and the checkout:execute scope. `decision` is one of: 'auto' (place_checkout_order would charge immediately), 'confirm' (it would create a confirmation the human must approve), or 'block' (a spending/velocity/policy limit would stop it — `reason`/`reasons` say which). `total_cents` is the re-derived grand total to pass back as expected_total_cents on the order; `price_drift` is true if that total differs from any expected_total_cents you passed here. All money is integer CENTS in USD (4999 = $49.99). This does NOT reserve stock, hold the price, or move any money. - `get_checkout_confirmation` — Poll the status of a checkout confirmation the human must approve, by the `confirmationId` that place_checkout_order returned when its status was 'requires_confirmation'. REQUIRES the human's checkout consent and the checkout:execute scope. When `found` is true, `status` is one of: 'pending' (the human has not decided yet), 'approved' (they approved it — `order_id` is the placed order), 'declined' (they rejected it), or 'expired' (they did not decide in time). The human approves or declines in their own account on the web; the agent never holds the approval. `found` is false if no such confirmation belongs to this connection (a confirmation from another connection is indistinguishable from a missing one — no existence oracle). The frozen totals are integer CENTS in USD. ## Cart links: the handoff to a human 1. Find offers with `GET /search`, then `GET /products/{slug}`, and collect the `listing_id` of each chosen offer. 2. `POST /cart-links` with 1-20 lines, quantity 1-99 each. A repeated `listing_id` has its quantities summed. 3. Lines that are not purchasable right now are DROPPED and reported in `rejected` with a coarse reason (`unavailable` or `insufficient_stock`) — a precise reason would leak non-public seller state. Read `accepted` before claiming success. If nothing is purchasable the response is a 400 whose error object carries the same `rejected` list. 4. Give the returned `url` to your user and tell them to open it. The items appear in THEIR cart, in their own browser, for them to review and pay for. 5. The link expires in 7 days (`expires_at`), and price and availability are re-checked when it is opened. Quote prices as current, never guaranteed. ## Buying There is no way to buy through this API, by design. `POST /cart-links` mints a link that a PERSON opens in their own browser to find the chosen offers waiting in their own cart, which they then review and pay for themselves. It takes no payment, places no order, and reserves no stock, and price and availability are re-checked when the link is opened. Hand the link to your user; never report a purchase you have not made. ## What is exposed Exactly what an anonymous visitor can see: publicly listed products, active categories, public prices, and purchasable offers. Seller-internal data — inventory counts, contact details, order and payout information — is not available through any public endpoint or MCP tool. The authenticated seller API (https://www.klatchit.com/developers) is a separate surface with its own keys and is not described here. ## More - Index: https://www.klatchit.com/llms.txt - Human documentation: https://www.klatchit.com/developers/public - OpenAPI 3.1: https://www.klatchit.com/api/public/v1/openapi.json - Product feed (Google Shopping XML): https://www.klatchit.com/feeds/google-shopping - Crawling rules: https://www.klatchit.com/robots.txt - Terms: https://www.klatchit.com/legal