{"openapi":"3.1.0","info":{"title":"Fast Shop API","version":"1.4.0","description":"Order physical products from 1-800-Flowers via AI agents using x402 payments.\n\nThis shop is registered for 1-800-Flowers (`shop_id=1800flowers`).\n\nWorkflow: `GET /search` (free) -> `GET /products/{product_id}` (free, details + offers) -> `POST /quote` (free) -> `POST /orders` (x402, returns 202) -> `GET /orders/:id` (track) -> `POST /orders/:id/cancel` (if pending).\n\nx402 payment: requests returning 402 require fastUSD payment on Fast fast-mainnet. Use `x402Pay` from `@fastxyz/x402-client` to handle automatically.\n\nAuth: wallet signature (Ed25519) for `GET /orders`, `GET /orders/:id`, `POST /orders/:id/cancel`. Format: `Authorization: Bearer base64(address:timestamp).base64(signature)`.\n\nRate limits: 60 req/min. Max 10 active orders per wallet.\n\nFull guide with code examples: `GET /skill.md`."},"servers":[{"url":"/","description":"Current server"}],"components":{"schemas":{"SearchResponse":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}}},"required":["results"]},"SearchResult":{"type":"object","properties":{"product_id":{"type":"string","description":"Retailer product ID"},"title":{"type":"string"},"url":{"type":"string","description":"Direct product URL, ready for POST /quote"},"price":{"type":"number","nullable":true,"description":"Product price in cents"},"retailer":{"type":"string","description":"Retailer identifier"},"image":{"type":"string","nullable":true,"description":"Product image URL"},"num_reviews":{"type":"number","nullable":true,"description":"Number of customer reviews"},"stars":{"type":"number","nullable":true,"description":"Star rating (may be null for some retailers)"},"fresh":{"type":"boolean","description":"Grocery/fresh item flag"},"source":{"type":"string","description":"Search backend source (for example, brave)"},"price_confidence":{"type":"string","description":"Price freshness hint for indexed/search-derived prices"}},"required":["product_id","title","url","price","retailer","image","num_reviews","stars"],"additionalProperties":{"nullable":true}},"Error":{"type":"object","properties":{"error":{"type":"string"},"detail":{"type":"string"}},"required":["error"]},"ProductDetails":{"type":"object","properties":{},"additionalProperties":{"nullable":true}},"QuoteResponse":{"type":"object","properties":{"quote_id":{"type":"string","format":"uuid"},"product_total_cents":{"type":"integer","example":1828,"description":"Sum of product prices from search results"},"max_price_cents":{"type":"integer","example":2828,"description":"Budget including shipping/tax buffer (calculated automatically)"},"order_fee_cents":{"type":"integer","example":100,"description":"$1 order processing fee"},"total_cents":{"type":"integer","example":2928,"description":"Total payment: max_price + order fee"},"price_usdc":{"type":"string","example":"29.28"},"fast_address":{"type":"string","example":"fast1..."},"network":{"type":"string","example":"fast-testnet"},"expires_at":{"type":"string","format":"date-time"},"note":{"type":"string","example":"Product: $18.28. Shipping/tax buffer added automatically. $1 order fee. Unused budget refunded after placement."}},"required":["quote_id","product_total_cents","max_price_cents","order_fee_cents","total_cents","price_usdc","fast_address","network","expires_at","note"]},"CreateQuoteRequest":{"type":"object","properties":{"products":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Product"},{"type":"object","properties":{"price_cents":{"type":"integer","minimum":100,"example":1828,"description":"Product price in cents from search results (required, minimum $1)"}},"required":["price_cents"]}]},"minItems":1},"shipping_address":{"$ref":"#/components/schemas/Address"}},"required":["products","shipping_address"]},"Product":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://www.1800flowers.com/lovely-lavender-medley-191173"},"quantity":{"type":"integer","minimum":1,"maximum":100,"default":1},"price_cents":{"type":"integer","minimum":100,"example":1828,"description":"Product price in cents from search results (minimum $1)"},"variant":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]},"description":"Per-product options. SKU-shape entries (size, color, etc.) pass through to the retailer. Reserved labels for 1-800-Flowers: 'delivery_date' (YYYY-MM-DD) and 'gift_message' (string) — extracted by the adapter and forwarded to the retailer's gift fields."}},"required":["url"]},"Address":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"address_line1":{"type":"string"},"address_line2":{"type":"string"},"city":{"type":"string"},"state":{"type":"string","minLength":2,"maxLength":2,"example":"NY","description":"2-letter US state code"},"postal_code":{"type":"string"},"phone_number":{"type":"string","example":"2125551234","description":"US phone number — any common format; server normalises to 10 digits"},"country":{"type":"string","enum":["US"],"default":"US","description":"Only US shipping is supported"}},"required":["first_name","last_name","address_line1","city","state","postal_code","phone_number"]},"QueuedOrderResponse":{"type":"object","properties":{"order_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending"]}},"required":["order_id","status"]},"PaymentRequired":{"type":"object","properties":{"error":{"type":"string"},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string"},"network":{"type":"string"},"maxAmountRequired":{"type":"string"},"payTo":{"type":"string"},"asset":{"type":"string"},"resource":{"type":"string"},"description":{"type":"string"},"mimeType":{"type":"string"},"maxTimeoutSeconds":{"type":"number"}},"required":["scheme","network","maxAmountRequired","payTo","asset","resource","description","mimeType","maxTimeoutSeconds"]}}},"required":["error","accepts"]},"CreateOrderRequest":{"type":"object","properties":{"quote_id":{"type":"string","format":"uuid"}},"required":["quote_id"]},"OrderDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"quote_id":{"type":"string","format":"uuid"},"fulfillment_id":{"type":"string","nullable":true},"status":{"type":"string","enum":["pending","processing","placed","failed","cancelled","submission_unknown"]},"max_price_cents":{"type":"integer"},"actual_total_cents":{"type":"integer","nullable":true},"price_components":{"type":"object","nullable":true,"properties":{"subtotal":{"type":"integer"},"shipping":{"type":"integer"},"tax":{"type":"integer"},"total":{"type":"integer"}},"required":["subtotal","shipping","tax","total"],"description":"Actual price breakdown from retailer (available after order is placed)"},"payer_address":{"type":"string","nullable":true},"payment_nonce":{"type":"integer","nullable":true},"tracking_numbers":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/TrackingNumber"}},"fulfillment_items":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/OrderItem"}},"fulfillment_status":{"type":"string","nullable":true},"products":{"type":"array","nullable":true,"items":{"$ref":"#/components/schemas/Product"}},"shipping_address":{"allOf":[{"$ref":"#/components/schemas/Address"},{"nullable":true}]},"error_type":{"type":"string","nullable":true},"error_message":{"type":"string","nullable":true},"refund_status":{"type":"string","nullable":true,"enum":["pending","sent","failed",null],"description":"Refund status: pending (claimed), sent (on-chain), failed (will retry)"},"refund_nonce":{"type":"integer","nullable":true},"refund_amount_usdc":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","quote_id","fulfillment_id","status","max_price_cents","actual_total_cents","payer_address","tracking_numbers","error_type","error_message","created_at","updated_at"]},"TrackingNumber":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"carrier":{"type":"string","example":"ups"},"tracking_number":{"type":"string","example":"1Z999AA10123456784"},"tracking_url":{"type":"string","nullable":true,"example":"https://www.ups.com/track?tracknum=1Z999AA10123456784"},"created_at":{"type":"string","format":"date-time"}},"required":["id","carrier","tracking_number","tracking_url","created_at"]},"OrderItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"quantity":{"type":"integer"},"variant":{"type":"array","nullable":true,"items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]}},"status":{"type":"string","enum":["pending","processing","ordered","shipped","delivered","cancelled","failed"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","url","quantity","status","created_at","updated_at"]},"OrderListResponse":{"type":"object","properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/OrderDetail"}}},"required":["orders"]},"CancelOrderResponse":{"type":"object","properties":{"order_id":{"type":"string"},"status":{"type":"string","enum":["cancelled"]},"refund_status":{"type":"string"}},"required":["order_id","status","refund_status"]},"WebhookEvent":{"type":"object","properties":{"event":{"type":"string","enum":["order.started","order.placed","order.failed","order.tracking_received"],"example":"order.placed"},"order_id":{"type":"string"},"data":{"type":"object","properties":{"price_components":{"type":"object","properties":{"subtotal":{"type":"number"},"shipping":{"type":"number"},"tax":{"type":"number"},"total":{"type":"number"}},"required":["total"],"additionalProperties":{"nullable":true}},"tracking_numbers":{"type":"array","items":{"type":"object","properties":{"carrier":{"type":"string"},"tracking_number":{"type":"string"}},"required":["carrier","tracking_number"]}},"items":{"type":"array","items":{"nullable":true}},"error_type":{"type":"string"},"error":{"type":"string"}}}},"required":["event","order_id"]},"HealthResponse":{"type":"object","properties":{"status":{"type":"string","example":"ok"}},"required":["status"]}},"parameters":{}},"paths":{"/search":{"get":{"tags":["Search"],"summary":"Search products on a retailer (free)","description":"Search for products on supported retailers. Walmart, Amazon, and 1-800-Flowers results can be used for POST /quote; Home Depot is search/product-details only. 1-800-Flowers search uses Brave indexed product data when configured. The default retailer is this shop's registered retailer (per SHOP_ID). Free, no payment or wallet required. Returns structured product data with prices in cents and direct product URLs. Rate limit: 60 requests/minute.","parameters":[{"schema":{"type":"string","example":"airpods"},"required":true,"name":"q","in":"query"},{"schema":{"type":"string","enum":["amazon","walmart","homedepot","1800flowers"],"default":"1800flowers","example":"1800flowers","description":"Retailer to search. Defaults to `1800flowers` (this shop)."},"required":false,"description":"Retailer to search. Defaults to `1800flowers` (this shop).","name":"retailer","in":"query"},{"schema":{"type":"string","example":"10","description":"Max results to return (default 10, max 20)"},"required":false,"description":"Max results to return (default 10, max 20)","name":"max_results","in":"query"}],"responses":{"200":{"description":"Product search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"Missing or invalid query parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Search failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/products/{product_id}":{"get":{"tags":["Products"],"summary":"Get product details and offers (free)","description":"Get full product details and seller offers for a product. Returns description, images, features, and all seller offers with prime/shipping/pricing info. Use the product_id from search results. Free, no payment required. Rate limit: 60 requests/minute.","parameters":[{"schema":{"type":"string","example":"191173","description":"Retailer product ID (e.g. 1-800-Flowers product ID)"},"required":true,"description":"Retailer product ID (e.g. 1-800-Flowers product ID)","name":"product_id","in":"path"},{"schema":{"type":"string","enum":["amazon","walmart","homedepot","1800flowers"],"default":"1800flowers","example":"1800flowers","description":"Retailer identifier. Defaults to `1800flowers` (this shop). For 1-800-Flowers, the response is sourced from the search cache (no Zinc product API exists)."},"required":false,"description":"Retailer identifier. Defaults to `1800flowers` (this shop). For 1-800-Flowers, the response is sourced from the search cache (no Zinc product API exists).","name":"retailer","in":"query"}],"responses":{"200":{"description":"Product details with offers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductDetails"}}}},"400":{"description":"Missing or invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found (e.g. 1-800-Flowers product missing from search cache)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Failed to fetch product data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/quote":{"post":{"tags":["Orders"],"summary":"Get a price quote","description":"Create a price quote for an order on this shop. Provide products with price_cents from search results. A retailer-aware shipping/tax buffer is added automatically, plus a $1 order fee. The response shows product_total_cents, max_price_cents (with buffer), and total_cents. Any unused budget is refunded automatically in fastUSD after the order ships. Quotes expire after 10 minutes. Only US shipping addresses are supported.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateQuoteRequest"}}}},"responses":{"201":{"description":"Quote created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Enrichment rejected the request — e.g., 1-800-Flowers product was not seen via /search, the cached price is stale (>24h), or it differs from the agent-supplied price by more than 10%. Re-run /search and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders":{"post":{"tags":["Orders"],"summary":"Place an order (x402 payment required)","description":"Place an order using the x402 payment protocol. Use `x402Pay({ url, method: \"POST\", body, wallet })` from `@fastxyz/x402-client` to handle payment automatically -- it detects the 402, pays the quote amount, and retries with the certificate. Do NOT manually construct the X-PAYMENT header. See GET /skill.md for full setup and working code. After the order is placed, any unused budget is refunded automatically. The $1 order fee is non-refundable on placed orders. If an order fails, you receive a full refund including the order fee. Order lifecycle: pending -> processing -> placed (success) or failed (refunded) or cancelled (user cancelled, fully refunded). If submission is uncertain (202), poll GET /orders/:id to resolve. Rate limit: 60 requests/minute.","parameters":[{"schema":{"type":"string","description":"x402 payment certificate (base64-encoded)"},"required":false,"description":"x402 payment certificate (base64-encoded)","name":"x-payment","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}}},"responses":{"202":{"description":"Order accepted and queued for processing. Poll GET /orders/:id for status updates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueuedOrderResponse"}}}},"400":{"description":"Invalid request or malformed X-PAYMENT header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required — send Fast fastUSD and include the certificate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRequired"}}}},"404":{"description":"Quote not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Quote already used — returns existing order_id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"Quote expired — create a new quote","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many active orders or daily spend limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Order placement failed — payment is refunded automatically","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Orders"],"summary":"List your orders","description":"Returns all orders for the authenticated wallet address.\n\nRequires wallet signature authentication via the Authorization header.\n\n**Format:** `Authorization: Bearer <base64(address:timestamp)>.<base64(signature)>`\n\nWhere:\n- `address`: your Fast wallet address (`fast1...`)\n- `timestamp`: current unix timestamp in seconds\n- `signature`: Ed25519 signature of `address:timestamp` using your wallet key\n\nTokens expire after 60 seconds.\n\n**Example using @fastxyz/sdk:**\n\n```ts\nimport { FastWallet, FastProvider } from '@fastxyz/sdk';\n\nconst provider = new FastProvider({ network: 'mainnet' });\nconst wallet = await FastWallet.fromPrivateKey(privateKey, provider);\n\nconst timestamp = Math.floor(Date.now() / 1000).toString();\nconst message = `${wallet.address}:${timestamp}`;\nconst { signature } = await wallet.sign({ message });\n\nconst payload = Buffer.from(message).toString('base64');\nconst sig = Buffer.from(signature, 'hex').toString('base64');\nconst token = `${payload}.${sig}`;\n\nconst response = await fetch(`${baseUrl}/orders`, {\n  headers: { 'Authorization': `Bearer ${token}` },\n});\n```\n\nReplace `baseUrl` with the server URL where you discovered this API.","parameters":[{"schema":{"type":"string","example":"20","description":"Max results per page (default 50, max 200)"},"required":false,"description":"Max results per page (default 50, max 200)","name":"limit","in":"query"},{"schema":{"type":"string","example":"0","description":"Number of results to skip"},"required":false,"description":"Number of results to skip","name":"offset","in":"query"},{"schema":{"type":"string","example":"Bearer ZmFzdDFhYmMuLi46MTcxMjM0NTY3OA==.c2lnbmF0dXJl","description":"Bearer token: base64(address:timestamp).base64(signature)"},"required":false,"description":"Bearer token: base64(address:timestamp).base64(signature)","name":"authorization","in":"header"}],"responses":{"200":{"description":"Order list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderListResponse"}}}},"401":{"description":"Missing or invalid wallet signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders/{id}":{"get":{"tags":["Orders"],"summary":"Get order status","description":"Returns the current status of an order, including tracking numbers and refund info. If an order is already placed but tracking has not been stored yet, the server refreshes it from the fulfillment provider on demand. Only the wallet that placed the order can view it.\n\nRequires wallet signature authentication via the Authorization header.\n\n**Format:** `Authorization: Bearer <base64(address:timestamp)>.<base64(signature)>`\n\n**Example using @fastxyz/sdk:**\n\n```ts\nconst timestamp = Math.floor(Date.now() / 1000).toString();\nconst message = `${wallet.address}:${timestamp}`;\nconst { signature } = await wallet.sign({ message });\n\nconst payload = Buffer.from(message).toString('base64');\nconst sig = Buffer.from(signature, 'hex').toString('base64');\nconst token = `${payload}.${sig}`;\n\nconst response = await fetch(`${baseUrl}/orders/${orderId}`, {\n  headers: { 'Authorization': `Bearer ${token}` },\n});\n```\n\nReplace `baseUrl` with the server URL where you discovered this API.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","example":"Bearer ZmFzdDFhYmMuLi46MTcxMjM0NTY3OA==.c2lnbmF0dXJl","description":"Bearer token: base64(address:timestamp).base64(signature)"},"required":false,"description":"Bearer token: base64(address:timestamp).base64(signature)","name":"authorization","in":"header"}],"responses":{"200":{"description":"Order details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderDetail"}}}},"401":{"description":"Missing or invalid wallet signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Order not found or not owned by authenticated wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders/{id}/view":{"get":{"tags":["Orders"],"summary":"View shareable order details page","description":"Returns a read-only HTML page for this order that can be opened in a browser or shared as a link. It shows products, shipping address, order status, tracking links, actual price breakdown, and refund information. Anyone with the link can view it, so treat the URL as a secret.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Shareable order details page","content":{"text/html":{"schema":{"type":"string","example":"<html>...</html>"}}}},"404":{"description":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/orders/{id}/cancel":{"post":{"tags":["Orders"],"summary":"Cancel a pending order","description":"Cancel a pending order that has not yet been submitted to the retailer. Only works while the order is in pending status (still in the processing queue). Returns a full refund including the order fee. Only the wallet that placed the order can cancel it.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Bearer token: base64(address:timestamp).base64(signature)"},"required":true,"description":"Bearer token: base64(address:timestamp).base64(signature)","name":"authorization","in":"header"}],"responses":{"200":{"description":"Order cancelled and refund initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderResponse"}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not authorized to cancel this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Order cannot be cancelled — already submitted or not pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook/fulfillment":{"post":{"tags":["Internal"],"summary":"Order fulfillment webhook","description":"Receives order status events from the fulfillment provider. Not intended for direct API consumers.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged"}}}},"/health":{"get":{"tags":["System"],"summary":"Health check","responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}}}}