{"openapi":"3.1.0","info":{"title":"Zinc","summary":"Zinc lets you search, buy, and return items from top online retailers with a single API.","description":"Search, buy, and return items from top online retailers with a single API. Supports AI agent ordering via MPP (HTTP 402) — no account required. Supported retailers include 1-800-Flowers, Ace Hardware, Amazon, Amazon DE, Barnes & Noble, Best Buy, Chewy, Gap, IBS, Lowe's, Macys, Partstown, and 12 more. Ships to the US and 2 other countries (DE, IT).","version":"2026-08-21","x-logo":{"url":"https://mintlify.s3.us-west-1.amazonaws.com/zinc/logo/light.png"},"contact":{"name":"Zinc API Support","email":"support@zinc.com","url":"https://zinc.com/docs"},"x-guidance":"Zinc lets AI agents buy products from online retailers via a single API. Use POST /agent/orders to place an order — no Zinc account needed, payment is handled via MPP (HTTP 402 flow). Provide a product URL from a supported retailer, a shipping address, and max_price in cents. The API charges max_price + $1 API fee upfront and refunds the difference on completion. To find products first, the /agent/* data endpoints (search, products/search, products/offers, products/details) are MPP-paid at $0.01 per call; /agent/search returns orderable URLs to feed straight into /agent/orders. GET /retailers lists supported retailers for free (no payment or account). Authenticated equivalents (orders, products, managed-accounts) require a Bearer token (API key prefixed zn_); those orders are paid from a prefunded wallet — GET /wallet/me returns the spendable balance and per-order fee, so check it before POST /orders to avoid a 402. Orders are strict by default: if the gift option can't be applied or any item can't be bought, the whole order fails. When the user wants whatever can be shipped, send fulfillment: {\"best_effort\": [\"gift\", \"items\"]} and read fulfillment.concessions on the order to see what was relaxed. max_price is never relaxed. Docs: https://zinc.com/docs Supported retailers include 1-800-Flowers, Ace Hardware, Amazon, Amazon DE, Barnes & Noble, Best Buy, Chewy, Gap, IBS, Lowe's, Macys, Partstown, and 12 more, shipping to the US and 2 other countries (DE, IT).","x-supported-retailers":["1-800-Flowers","Ace Hardware","Amazon","Amazon DE","Barnes & Noble","Best Buy","Chewy","Gap","IBS","Lowe's","Macys","Partstown","Pokémon Center","Sephora","Target","The Home Depot","TikTok","Uniqlo","Walmart","Wayfair","Zinc","eBay","libraccio","zazzle"],"x-supported-countries":["US","DE","IT"]},"paths":{"/orders/bulk/validate":{"post":{"tags":["orders"],"summary":"Validate Bulk Upload","description":"Dry-run a CSV upload: validate every row and report estimated spend.\n\nNo orders are placed. Use this to show the confirmation preview before\ncalling POST /orders/bulk.","operationId":"validate_bulk_upload_orders_bulk_validate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUploadRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkValidateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/bulk":{"post":{"tags":["orders"],"summary":"Create Bulk Upload","description":"Create a bulk-upload batch and place its rows asynchronously.","operationId":"create_bulk_upload_orders_bulk_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUploadRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkBatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["orders"],"summary":"List Bulk Uploads","description":"List the current user's bulk-upload batches, newest first.","operationId":"list_bulk_uploads_orders_bulk_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkBatchListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/bulk/{batch_id}":{"get":{"tags":["orders"],"summary":"Get Bulk Upload","description":"Get a batch with per-row results and live order statuses.","operationId":"get_bulk_upload_orders_bulk__batch_id__get","parameters":[{"name":"batch_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Batch Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkBatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/bulk/{batch_id}/results.csv":{"get":{"tags":["orders"],"summary":"Download Bulk Results","description":"Download the batch results as a CSV (status + echoed custom columns).","operationId":"download_bulk_results_orders_bulk__batch_id__results_csv_get","parameters":[{"name":"batch_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Batch Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"CSV export: one row per uploaded row, with its order status and any echoed custom columns. Served as an attachment via Content-Disposition.","content":{"text/csv":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders":{"post":{"tags":["orders"],"summary":"Create Order","description":"Posts an order to a queue for processing","operationId":"create_order_orders_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["orders"],"summary":"List Orders","description":"Get a list of orders for the current user","operationId":"list_orders_orders_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Number of orders to return","default":50,"title":"Limit"},"description":"Number of orders to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of orders to skip","default":0,"title":"Offset"},"description":"Number of orders to skip"},{"name":"order_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by order ID (partial match)","title":"Order Id"},"description":"Filter by order ID (partial match)"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Partial match on order ID OR tracking number","title":"Search"},"description":"Partial match on order ID OR tracking number"},{"name":"merchant_order_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Filter by the retailer's own order number (e.g. an Amazon `113-…` ID), matched exactly against any of the order's order-placing jobs. Exact, not partial — dashes in the term are matched both as typed and stripped.","title":"Merchant Order Id"},"description":"Filter by the retailer's own order number (e.g. an Amazon `113-…` ID), matched exactly against any of the order's order-placing jobs. Exact, not partial — dashes in the term are matched both as typed and stripped."},{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by order status","title":"Status Filter"},"description":"Filter by order status"},{"name":"tracking_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to orders having at least one tracking number with this status","title":"Tracking Status"},"description":"Filter to orders having at least one tracking number with this status"},{"name":"has_tracking","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"If true, only orders with at least one tracking number; if false, only orders with none","title":"Has Tracking"},"description":"If true, only orders with at least one tracking number; if false, only orders with none"},{"name":"return_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by return-request status. `open` → orders with at least one open return. `closed` → orders with at least one approved or denied return. Omit for no filter.","title":"Return Status"},"description":"Filter by return-request status. `open` → orders with at least one open return. `closed` → orders with at least one approved or denied return. Omit for no filter."},{"name":"created_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only orders created at/after this instant (inclusive)","title":"Created After"},"description":"Only orders created at/after this instant (inclusive)"},{"name":"created_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only orders created before this instant (exclusive)","title":"Created Before"},"description":"Only orders created before this instant (exclusive)"},{"name":"metadata_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Top-level `metadata` key to match, e.g. `po_number`. Must be sent together with `metadata_value`. Nested paths are not supported.","title":"Metadata Key"},"description":"Top-level `metadata` key to match, e.g. `po_number`. Must be sent together with `metadata_value`. Nested paths are not supported."},{"name":"metadata_value","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Exact value `metadata_key` must equal. Matching is exact, not partial, and case-sensitive. Must be sent together with `metadata_key`.","title":"Metadata Value"},"description":"Exact value `metadata_key` must equal. Matching is exact, not partial, and case-sensitive. Must be sent together with `metadata_key`."},{"name":"user_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}],"description":"Filter to orders placed by one org teammate, matched as a case-insensitive substring of their email. Only ever narrows within the caller's organization; a solo user can only match their own address.","title":"User Email"},"description":"Filter to orders placed by one org teammate, matched as a case-insensitive substring of their email. Only ever narrows within the caller's organization; a solo user can only match their own address."},{"name":"include","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"description":"Optional expansions. `tracking_events` embeds the full carrier checkpoint timeline (and latest status) on each tracking number; omitted by default to keep list payloads small.","title":"Include"},"description":"Optional expansions. `tracking_events` embeds the full carrier checkpoint timeline (and latest status) on each tracking number; omitted by default to keep list payloads small."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/export":{"get":{"tags":["orders"],"summary":"Export Orders Csv","description":"Stream the current user's orders as a CSV file.\n\nTakes the same filters as ``GET /orders`` (via the shared\n``_visible_orders_filter``) so an export always contains exactly the rows\nthe caller was looking at — but no ``limit``/``offset``: the export covers\nthe whole filtered set, paged internally so memory stays flat.","operationId":"export_orders_csv_orders_export_get","parameters":[{"name":"order_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by order ID (partial match)","title":"Order Id"},"description":"Filter by order ID (partial match)"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Partial match on order ID OR tracking number","title":"Search"},"description":"Partial match on order ID OR tracking number"},{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by order status","title":"Status Filter"},"description":"Filter by order status"},{"name":"merchant_order_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Filter by the retailer's own order number (exact match)","title":"Merchant Order Id"},"description":"Filter by the retailer's own order number (exact match)"},{"name":"tracking_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to orders having at least one tracking number with this status","title":"Tracking Status"},"description":"Filter to orders having at least one tracking number with this status"},{"name":"has_tracking","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only orders with (true) or without (false) tracking","title":"Has Tracking"},"description":"Only orders with (true) or without (false) tracking"},{"name":"return_status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"`open` or `closed` return requests; omit for no filter","title":"Return Status"},"description":"`open` or `closed` return requests; omit for no filter"},{"name":"created_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only orders created at/after this instant (inclusive)","title":"Created After"},"description":"Only orders created at/after this instant (inclusive)"},{"name":"created_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only orders created before this instant (exclusive)","title":"Created Before"},"description":"Only orders created before this instant (exclusive)"},{"name":"metadata_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Top-level `metadata` key to match; send with `metadata_value`","title":"Metadata Key"},"description":"Top-level `metadata` key to match; send with `metadata_value`"},{"name":"metadata_value","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"description":"Exact value `metadata_key` must equal; send with `metadata_key`","title":"Metadata Value"},"description":"Exact value `metadata_key` must equal; send with `metadata_key`"},{"name":"user_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}],"description":"Filter to orders placed by one org teammate, matched as a case-insensitive substring of their email. Only ever narrows within the caller's organization; a solo user can only match their own address.","title":"User Email"},"description":"Filter to orders placed by one org teammate, matched as a case-insensitive substring of their email. Only ever narrows within the caller's organization; a solo user can only match their own address."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"CSV file of the filtered orders.","content":{"text/csv":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/test-products":{"get":{"tags":["orders"],"summary":"List Test Products","description":"Get list of test products for sandbox testing.\n\nReturns list of test product URLs that can be used with test API keys\nto trigger different test scenarios.","operationId":"list_test_products_orders_test_products_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Test Products Orders Test Products Get"}}}}}}},"/orders/{order_id}":{"get":{"tags":["orders"],"summary":"Get Order","description":"Retrieves an order by its ID","operationId":"get_order_orders__order_id__get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Order Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/timeline":{"get":{"tags":["orders"],"summary":"Get Order Timeline","description":"Customer-facing lifecycle timeline for an order.\n\nDerived on read from existing data — no dedicated storage. Merges the\nplacement outcome (OrderLog) with carrier tracking state (TrackingNumber /\nTrackingCheckpoint) into an ordered list of milestones. This is the order's\nstory to the customer, distinct from the admin-only job/automation log.","operationId":"get_order_timeline_orders__order_id__timeline_get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Order Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderTimelineResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/orders/{order_id}/cancel":{"post":{"tags":["orders"],"summary":"Cancel Order","description":"Cancel an order by its ID. Orders can only be cancelled if they are pending.","operationId":"cancel_order_orders__order_id__cancel_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Order Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/search":{"get":{"tags":["products"],"summary":"Search Products","description":"Search for products on a retailer.\n\n**Best Buy returns a partial page.** Best Buy server-renders only about 4 of\nthe ~24 products on a search page and loads the rest in the browser, so each\npage yields roughly 4 results rather than a full page. Ranking, pricing and\navailability are Best Buy's own; there are simply fewer items per page. Page\nthrough with `next_page` to collect more — `next_page` reflects whether Best\nBuy has further results, not how many came back in this response.\n\n**Shopify stores are their own retailer**: pass the store's domain as\n`retailer` (e.g. `retailer=yetch.studio`; any Shopify-powered storefront\nworks). Results are the store's own top matches (~10) and there is no\npagination, so `next_page` is always null and `page` must be omitted or 1.\n`product_id` is the store-scoped product handle to pass to the details\nendpoint with the same `retailer`.\n\n**Etsy search covers US shops, priced in USD.** Etsy sellers price in their\nown currency and a single page routinely mixes several, which makes `price`\nincomparable across a result set — so search is narrowed to US-located\nshops and any remaining non-USD listing is dropped. `currency_code` is set\non every result and is always `USD` here, and prices are never converted,\nso the number is what the seller charges. Because the currency check runs\nafter Etsy paginates, **a page can come back short while more results still\nexist** — page on with `next_page`. (Details is neither narrowed nor\nfiltered: it returns any listing, in its own currency.)\n\nEtsy results carry no `stars`/`num_reviews` — Etsy publishes a rating for\nthe *shop*, not the listing, and reporting a seller's rating as the\nproduct's would be misleading; `brand` carries the shop name, and the\ndetails endpoint reports the shop's rating explicitly. `product_id` is the\nnumeric listing id.","operationId":"search_products_products_search_get","parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Search term","title":"Query"},"description":"Search term"},{"name":"retailer","in":"query","required":true,"schema":{"type":"string","description":"Retailer identifier: amazon, walmart, bestbuy, etsy, or a Shopify store's domain (e.g. retailer=yetch.studio)","title":"Retailer"},"description":"Retailer identifier: amazon, walmart, bestbuy, etsy, or a Shopify store's domain (e.g. retailer=yetch.studio)"},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Page number for pagination","title":"Page"},"description":"Page number for pagination"},{"name":"free_shipping","in":"query","required":false,"schema":{"type":"boolean","description":"Only return items that ship for free (Walmart and Best Buy: ship price of 0). Currently a no-op for Amazon: the upstream search data under-reports Prime, so filtering on it would drop valid items — Amazon results are returned unfiltered. Filtering happens after pagination, so per-page counts vary; use `next_page` in the response to keep paging — an empty page with a non-null `next_page` is not the end of results. Rejected for Shopify stores: their search data carries no shipping information, so the filter cannot be honored.","default":false,"title":"Free Shipping"},"description":"Only return items that ship for free (Walmart and Best Buy: ship price of 0). Currently a no-op for Amazon: the upstream search data under-reports Prime, so filtering on it would drop valid items — Amazon results are returned unfiltered. Filtering happens after pagination, so per-page counts vary; use `next_page` in the response to keep paging — an empty page with a non-null `next_page` is not the end of results. Rejected for Shopify stores: their search data carries no shipping information, so the filter cannot be honored."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/{product_id}/offers":{"get":{"tags":["products"],"summary":"Get Product Offers","description":"Get offers for a product from a retailer.\n\nNot available for Shopify stores: a storefront lists one seller (itself),\nso per-variant price and availability live on the details endpoint instead.","operationId":"get_product_offers_products__product_id__offers_get","parameters":[{"name":"product_id","in":"path","required":true,"schema":{"type":"string","title":"Product Id"}},{"name":"retailer","in":"query","required":true,"schema":{"type":"string","description":"Retailer identifier: amazon, walmart, bestbuy, etsy, or a Shopify store's domain (e.g. retailer=yetch.studio)","title":"Retailer"},"description":"Retailer identifier: amazon, walmart, bestbuy, etsy, or a Shopify store's domain (e.g. retailer=yetch.studio)"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":31},{"type":"null"}],"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)","title":"Max Age"},"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)"},{"name":"newer_than","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it.","title":"Newer Than"},"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it."},{"name":"async","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Return immediately with status=processing","title":"Async"},"description":"Return immediately with status=processing"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Retailer payload, passed through unmodified. Fields vary by retailer, so only `status` is guaranteed: `completed` for a resolved response, `processing` when `async=true` and the fetch is still running, `failed` when the retailer returned an error (with `code` and `message`).","content":{"application/json":{"schema":{"title":"Response Get Product Offers Products  Product Id  Offers Get","type":"object","properties":{"status":{"type":"string","enum":["completed","processing","failed"]}},"required":["status"],"additionalProperties":true}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/products/{product_id}":{"get":{"tags":["products"],"summary":"Get Product Details","description":"Get details for a product from a retailer.\n\n**Best Buy is addressed by `bsin`**, not by the numeric SKU — the bsin is the\ntrailing id in a Best Buy product URL (`/product/{slug}/{bsin}`). Search\nresults return the SKU as `product_id` and also carry the bsin, so pass the\nbsin here. The response repeats the SKU as `sku` for cross-referencing.\n\nUnlike `/search`, a Best Buy detail response is complete: detail pages are\nfully server-rendered, so nothing is withheld for client-side loading.\n\n**Shopify is addressed by (store, handle)**: pass the store's domain as\n`retailer` (e.g. `retailer=yetch.studio`) and the product handle — the slug\nin `/products/{handle}`, returned as `product_id` by search — as the path\nparameter. The response includes per-variant price and availability.\n`async` is not supported for Shopify stores.\n\n**Etsy is addressed by the numeric listing id** (returned as `product_id` by\nsearch). `price` is in minor units of `currency_code`, not converted to USD.\n\nEtsy ratings are the **shop's**, reported as `shop_review_average` /\n`shop_review_count`, and both cover only the **past year** — an established\nshop with no recent sales reports 0, and an unrated shop reports a null\naverage rather than 0.0 stars. `stars` and `num_reviews` are deliberately\nnot set: they mean a product's rating everywhere else in this API, and a\nseller's rating is a different claim.\n\n`listing_type` is `physical`, `download` or `both` — a download has nothing\nto ship. `available` accounts for the shop being on vacation as well as\nstock, so it can be false on an in-stock active listing; `shop_is_vacation`\nsays which it was. `variants` is populated only when Etsy exposes a\nlisting's inventory matrix — check `has_variations` to tell \"no variants\"\nfrom \"variants not visible\". `taxonomy_id` is Etsy's raw category id; there\nis no category name yet. `async` is not supported for Etsy.","operationId":"get_product_details_products__product_id__get","parameters":[{"name":"product_id","in":"path","required":true,"schema":{"type":"string","title":"Product Id"}},{"name":"retailer","in":"query","required":true,"schema":{"type":"string","description":"Retailer identifier: amazon, walmart, bestbuy, etsy, or a Shopify store's domain (e.g. retailer=yetch.studio)","title":"Retailer"},"description":"Retailer identifier: amazon, walmart, bestbuy, etsy, or a Shopify store's domain (e.g. retailer=yetch.studio)"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":31},{"type":"null"}],"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)","title":"Max Age"},"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)"},{"name":"newer_than","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it.","title":"Newer Than"},"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it."},{"name":"async","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Return immediately with status=processing","title":"Async"},"description":"Return immediately with status=processing"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Retailer payload, passed through unmodified. Fields vary by retailer, so only `status` is guaranteed: `completed` for a resolved response, `processing` when `async=true` and the fetch is still running, `failed` when the retailer returned an error (with `code` and `message`).","content":{"application/json":{"schema":{"title":"Response Get Product Details Products  Product Id  Get","type":"object","properties":{"status":{"type":"string","enum":["completed","processing","failed"]}},"required":["status"],"additionalProperties":true}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/search":{"get":{"tags":["search"],"summary":"Search","description":"Search for products across retailers; returns orderable zn_sku_ listings.","operationId":"search_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Search term","title":"Q"},"description":"Search term"},{"name":"min_price","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Cents. Drop results priced below this.","title":"Min Price"},"description":"Cents. Drop results priced below this."},{"name":"max_price","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Cents. Drop results priced above this. Pass the `max_price` you intend to send to POST /orders and every result returned fits it. Results with no known price are dropped when a clamp is set.","title":"Max Price"},"description":"Cents. Drop results priced above this. Pass the `max_price` you intend to send to POST /orders and every result returned fits it. Results with no known price are dropped when a clamp is set."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/managed-accounts":{"get":{"tags":["managed-accounts"],"summary":"List Retailer Credentials","description":"List all retailer credentials for the current user.\nIf is_global=True (admin only), list global credentials instead.","operationId":"list_retailer_credentials_managed_accounts_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Page size (default 50, max 500). Responses include `total` for paging past it.","default":50,"title":"Limit"},"description":"Page size (default 50, max 500). Responses include `total` for paging past it."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of credentials to skip","default":0,"title":"Offset"},"description":"Number of credentials to skip"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Partial match on email or retailer","title":"Search"},"description":"Partial match on email or retailer"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetailerCredentialsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["managed-accounts"],"summary":"Create Retailer Credentials","description":"Create new retailer credentials for the current user.\nIf is_global=True (admin only), creates global credentials owned by the system user.","operationId":"create_retailer_credentials_managed_accounts_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetailerCredentialsCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetailerCredentialsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/managed-accounts/{short_id}":{"put":{"tags":["managed-accounts"],"summary":"Update Retailer Credentials","description":"Update retailer credentials for the current user.\nAdmins can also update global credentials.","operationId":"update_retailer_credentials_managed_accounts__short_id__put","parameters":[{"name":"short_id","in":"path","required":true,"schema":{"type":"string","title":"Short Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetailerCredentialsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetailerCredentialsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["managed-accounts"],"summary":"Delete Retailer Credentials","description":"Delete retailer credentials for the current user.\nAdmins can also delete global credentials.","operationId":"delete_retailer_credentials_managed_accounts__short_id__delete","parameters":[{"name":"short_id","in":"path","required":true,"schema":{"type":"string","title":"Short Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/agent/orders":{"post":{"tags":["agent"],"summary":"Create Mpp Order","description":"Place an order via the Machine Payments Protocol (MPP).\n\nNo Zinc account required. Payment is made upfront via MPP.\nSupports multiple payment methods (e.g. Tempo, Stripe).\nIf no valid payment credential is provided, returns HTTP 402\nwith payment challenges for all configured methods.\n\nPayment is the gate, but only for a genuine discovery probe: a bodyless\nPOST (from a registry like mppscan) is parsed leniently and reaches the 402\nchallenge instead of a 422. A *present-but-invalid* body, by contrast —\nincluding malformed JSON and non-object JSON — is a real order attempt\nand is rejected with a 422 up front, before any payment\nchallenge is issued or honored. Otherwise an agent could settle an on-chain\npayment against the challenge and then be rejected on the retry, with no way\nto refund the settlement (the MPP layer cannot verify or reverse a payment\nwhose retry body no longer matches the challenge).","operationId":"create_mpp_order_agent_orders_post","parameters":[{"name":"method","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Restrict the 402 to a single payment method (e.g. 'stripe', 'tempo', or 'x402'). Omit to advertise every configured method. Use this when your client can satisfy only one rail — it avoids returning multiple WWW-Authenticate challenges, which many HTTP clients mishandle.","title":"Method"},"description":"Restrict the 402 to a single payment method (e.g. 'stripe', 'tempo', or 'x402'). Omit to advertise every configured method. Use this when your client can satisfy only one rail — it avoids returning multiple WWW-Authenticate challenges, which many HTTP clients mishandle."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}},"headers":{"X-Api-Key":{"description":"API key for the paying agent. Reads the order via GET /orders/{id}, cancels it while pending, and registers a webhook URL via PUT /webhooks/endpoint.","schema":{"type":"string"}},"Payment-Receipt":{"description":"MPP payment receipt confirming the charge","schema":{"type":"string"}}}},"402":{"description":"Payment required — returns challenge details for each configured payment method","headers":{"WWW-Authenticate":{"description":"One header per supported payment method with challenge parameters (per RFC 9110 §11.6.1)","schema":{"type":"string"}}}},"409":{"description":"Duplicate `idempotency_key` — this payer already placed an order under that key. Checked after the payment proof is verified but BEFORE it is settled, so a client that lost the original 201 to a timeout can retry with the same key (and a fresh signed payment) without being charged twice. The existing order's id is in `error.details.order_id`; fetch it with the X-Api-Key header on this response via GET /orders/{id}. Same error shape as POST /orders.","headers":{"X-Api-Key":{"description":"API key for the payer's account, to look up the existing order","schema":{"type":"string"}}}},"422":{"description":"Invalid order body — rejected before any payment challenge is issued or honored, so a payment can never be taken for an order that would be rejected. Send a valid OrderCreate body, or no body at all to receive a discovery 402."}},"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderCreate"}}}},"x-payment-info":{"price":{"mode":"dynamic","currency":"USD","min":"1.00","max":"10000.00"},"protocols":[{"mpp":{"method":"","intent":"charge","currency":""}}]},"security":[]}},"/agent/search":{"post":{"tags":["agent"],"summary":"Agent Search","description":"**Beta** — response shape may change. Cross-retailer product search for agents. Returns orderable listings whose\n`url` can be passed straight to POST /agent/orders.","operationId":"agent_search","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","description":"Search term","title":"Q"},"description":"Search term"},{"name":"min_price","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Cents. Drop results priced below this.","title":"Min Price"},"description":"Cents. Drop results priced below this."},{"name":"max_price","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Cents. Drop results priced above this. Pass the `max_price` you intend to send to POST /orders and every result returned fits it. Results with no known price are dropped when a clamp is set.","title":"Max Price"},"description":"Cents. Drop results priced above this. Pass the `max_price` you intend to send to POST /orders and every result returned fits it. Results with no known price are dropped when a clamp is set."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"402":{"description":"Payment required — returns an MPP challenge (one WWW-Authenticate header per payable method) and, when configured, an x402 challenge in the PAYMENT-REQUIRED header. Pay with whichever rail you support.","headers":{"WWW-Authenticate":{"description":"One header per payable MPP method with challenge parameters (per RFC 9110 §11.6.1)","schema":{"type":"string"}},"PAYMENT-REQUIRED":{"description":"x402 v2 payment requirements (base64-encoded JSON)","schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.01"},"protocols":[{"mpp":{"method":"","intent":"charge","currency":""}},{"x402":{"scheme":"exact","network":"eip155:8453"}}]},"security":[],"x-beta":true}},"/agent/products/search":{"post":{"tags":["agent"],"summary":"Agent Product Search","description":"Per-retailer product search for agents (amazon | walmart).","operationId":"agent_product_search","parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string","description":"Search term","title":"Query"},"description":"Search term"},{"name":"retailer","in":"query","required":true,"schema":{"type":"string","enum":["amazon","walmart"]},"description":"Retailer: amazon or walmart"},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Page number for pagination","title":"Page"},"description":"Page number for pagination"},{"name":"free_shipping","in":"query","required":false,"schema":{"type":"boolean","description":"Only return items that ship for free (Walmart: ship price of 0). Currently a no-op for Amazon: the upstream search data under-reports Prime, so Amazon results are returned unfiltered. Applied per-page after pagination; use `next_page` in the response to keep paging — an empty page with a non-null `next_page` is not the end of results.","default":false,"title":"Free Shipping"},"description":"Only return items that ship for free (Walmart: ship price of 0). Currently a no-op for Amazon: the upstream search data under-reports Prime, so Amazon results are returned unfiltered. Applied per-page after pagination; use `next_page` in the response to keep paging — an empty page with a non-null `next_page` is not the end of results."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductSearchResponse"}}}},"402":{"description":"Payment required — returns an MPP challenge (one WWW-Authenticate header per payable method) and, when configured, an x402 challenge in the PAYMENT-REQUIRED header. Pay with whichever rail you support.","headers":{"WWW-Authenticate":{"description":"One header per payable MPP method with challenge parameters (per RFC 9110 §11.6.1)","schema":{"type":"string"}},"PAYMENT-REQUIRED":{"description":"x402 v2 payment requirements (base64-encoded JSON)","schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.01"},"protocols":[{"mpp":{"method":"","intent":"charge","currency":""}},{"x402":{"scheme":"exact","network":"eip155:8453"}}]},"security":[]}},"/agent/products/offers":{"post":{"tags":["agent"],"summary":"Agent Product Offers","description":"Offers/pricing for a specific product on a retailer.","operationId":"agent_product_offers","parameters":[{"name":"product_id","in":"query","required":true,"schema":{"type":"string","description":"Product identifier (e.g. ASIN)","title":"Product Id"},"description":"Product identifier (e.g. ASIN)"},{"name":"retailer","in":"query","required":true,"schema":{"type":"string","enum":["amazon","walmart"]},"description":"Retailer: amazon or walmart"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":31},{"type":"null"}],"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)","title":"Max Age"},"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)"},{"name":"newer_than","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it.","title":"Newer Than"},"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it."},{"name":"async","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Return immediately with status=processing","title":"Async"},"description":"Return immediately with status=processing"}],"responses":{"200":{"description":"Retailer payload, passed through unmodified. Fields vary by retailer, so only `status` is guaranteed: `completed` for a resolved response, `processing` when `async=true` and the fetch is still running, `failed` when the retailer returned an error (with `code` and `message`).","content":{"application/json":{"schema":{"title":"Response Agent Product Offers","type":"object","properties":{"status":{"type":"string","enum":["completed","processing","failed"]}},"required":["status"],"additionalProperties":true}}}},"402":{"description":"Payment required — returns an MPP challenge (one WWW-Authenticate header per payable method) and, when configured, an x402 challenge in the PAYMENT-REQUIRED header. Pay with whichever rail you support.","headers":{"WWW-Authenticate":{"description":"One header per payable MPP method with challenge parameters (per RFC 9110 §11.6.1)","schema":{"type":"string"}},"PAYMENT-REQUIRED":{"description":"x402 v2 payment requirements (base64-encoded JSON)","schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.01"},"protocols":[{"mpp":{"method":"","intent":"charge","currency":""}},{"x402":{"scheme":"exact","network":"eip155:8453"}}]},"security":[]}},"/agent/products/details":{"post":{"tags":["agent"],"summary":"Agent Product Details","description":"Full product details for a specific product on a retailer.","operationId":"agent_product_details","parameters":[{"name":"product_id","in":"query","required":true,"schema":{"type":"string","description":"Product identifier (e.g. ASIN)","title":"Product Id"},"description":"Product identifier (e.g. ASIN)"},{"name":"retailer","in":"query","required":true,"schema":{"type":"string","enum":["amazon","walmart"]},"description":"Retailer: amazon or walmart"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":31},{"type":"null"}],"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)","title":"Max Age"},"description":"Max response age in seconds, at least 31 (mutually exclusive with newer_than)"},{"name":"newer_than","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it.","title":"Newer Than"},"description":"Minimum retrieval timestamp, as a unix time (mutually exclusive with max_age). Windows shorter than 31s are widened to it."},{"name":"async","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Return immediately with status=processing","title":"Async"},"description":"Return immediately with status=processing"}],"responses":{"200":{"description":"Retailer payload, passed through unmodified. Fields vary by retailer, so only `status` is guaranteed: `completed` for a resolved response, `processing` when `async=true` and the fetch is still running, `failed` when the retailer returned an error (with `code` and `message`).","content":{"application/json":{"schema":{"title":"Response Agent Product Details","type":"object","properties":{"status":{"type":"string","enum":["completed","processing","failed"]}},"required":["status"],"additionalProperties":true}}}},"402":{"description":"Payment required — returns an MPP challenge (one WWW-Authenticate header per payable method) and, when configured, an x402 challenge in the PAYMENT-REQUIRED header. Pay with whichever rail you support.","headers":{"WWW-Authenticate":{"description":"One header per payable MPP method with challenge parameters (per RFC 9110 §11.6.1)","schema":{"type":"string"}},"PAYMENT-REQUIRED":{"description":"x402 v2 payment requirements (base64-encoded JSON)","schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.01"},"protocols":[{"mpp":{"method":"","intent":"charge","currency":""}},{"x402":{"scheme":"exact","network":"eip155:8453"}}]},"security":[]}},"/returns":{"post":{"tags":["returns"],"summary":"Create Return Request","operationId":"create_return_request_returns_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnRequestCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnRequestResponse"}}}},"409":{"description":"An open return request already exists for this order, or a requested item was already part of a prior return request (any status) and is no longer returnable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["returns"],"summary":"List Return Requests","operationId":"list_return_requests_returns_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Page size (default 50, max 500). Responses include `total` for paging past it.","default":50,"title":"Limit"},"description":"Page size (default 50, max 500). Responses include `total` for paging past it."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of returns to skip","default":0,"title":"Offset"},"description":"Number of returns to skip"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Partial match on order ID","title":"Search"},"description":"Partial match on order ID"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by return status","title":"Status"},"description":"Filter by return status"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnRequestListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/returns/{return_request_id}":{"get":{"tags":["returns"],"summary":"Get Return Request","operationId":"get_return_request_returns__return_request_id__get","parameters":[{"name":"return_request_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Return Request Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnRequestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/retailers":{"get":{"tags":["retailers"],"summary":"List Retailers","description":"List the retailers Zinc supports — the public \"what do you support?\" catalog.\n\nNo authentication required. One flat object per retailer brand: identifier,\ndomain, countries shipped to, and the free-shipping policy. International\nmarketplaces (e.g. amazon.com / amazon.de) are grouped under one brand with\nthe country listed in `supported_countries`. Optionally filter by name.","operationId":"list_retailers_retailers_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Number of retailers to return","default":100,"title":"Limit"},"description":"Number of retailers to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of retailers to skip","default":0,"title":"Offset"},"description":"Number of retailers to skip"},{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by name (case-insensitive partial match)","title":"Name"},"description":"Filter by name (case-insensitive partial match)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRetailerListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/usage":{"get":{"tags":["usage"],"summary":"Get My Usage","description":"The caller's own data-API usage over a trailing window, per endpoint.","operationId":"get_my_usage_usage_get","parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Trailing window in days (max 90).","default":30,"title":"Window Days"},"description":"Trailing window in days (max 90)."},{"name":"recent","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":0,"description":"How many recent calls to return.","default":50,"title":"Recent"},"description":"How many recent calls to return."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/wallet/me":{"get":{"tags":["wallet"],"summary":"Get Wallet","description":"Get your wallet balance.\n\nAll amounts are integer cents. `balance` is the ledger balance;\n`spendable_balance` is what `POST /orders` actually checks against (the two\ndiffer only for Zinc Connect accounts with in-flight holds). An order needs\n`max_price + order_fee_cents` spendable, so compare against that before\nplacing one instead of discovering a shortfall as a 402. Bulk-deal customers\n(`billed_by_invoice: true`) are invoiced monthly and skip the balance check.\n\nUnder a `zn_test_` key (or `X-Test-Mode`) this reads the sandbox wallet,\nwhich sandbox orders never draw down. Funds are added from the dashboard.","operationId":"get_wallet_wallet_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stats/delivery-map":{"get":{"tags":["stats"],"summary":"Get Delivery Map","description":"Recent delivered orders as anonymized, city-level map points.\n\nPublic and unauthenticated — feeds the marketing site's globe. Points are\nZIP-centroid coordinates rounded to two decimals with a curated category\nemoji; deduplicated and capped. Cached for about an hour.","operationId":"get_delivery_map_stats_delivery_map_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryMapResponse"}}}}}}},"/stats/lifetime":{"get":{"tags":["stats"],"summary":"Get Lifetime Stats","description":"Zinc's all-time successful-order count and GMV.\n\nPublic and unauthenticated. Top-level numbers are v2 (this service);\n``v1`` is the worker-computed legacy snapshot (seed until the first\ncompute lands); ``combined`` sums both. Cached for about a day.","operationId":"get_lifetime_stats_stats_lifetime_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LifetimeStatsResponse"}}}}}}},"/track/{order_id}":{"get":{"tags":["tracking"],"summary":"Get Public Tracking","description":"Public tracking view for a single order, keyed by its UUID.","operationId":"get_public_tracking_track__order_id__get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTrackingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sandbox/keys":{"post":{"tags":["sandbox"],"summary":"Mint a sandbox API key (no account needed)","description":"Mint a provisional sandbox user + test API key. No account needed.","operationId":"create_sandbox_key_sandbox_keys_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SandboxKeyCreate"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Sandbox key minted. The body is self-guiding: send `api_key` as a Bearer token, POST `example_order` to /orders as-is, and follow `next`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxKeyResponse"}}}},"429":{"description":"Daily mint limit reached — sandbox keys are limited to a few per IP per day (plus a global cap). Reuse a key you already minted (valid until 7 days after its last use) or retry after `Retry-After` elapses.","headers":{"Retry-After":{"description":"Seconds until the daily window resets (UTC midnight)","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp of the window reset","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Temporarily unable to mint — the sandbox or its rate limiter is unavailable (the limiter fails closed by design). Safe to retry after `Retry-After`.","headers":{"Retry-After":{"description":"Suggested backoff in seconds","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/sandbox/claim":{"post":{"tags":["sandbox"],"summary":"Claim a sandbox into your account","description":"Fold a provisional sandbox into the authenticated account.\n\nAlways a merge: Stytch's callback creates a real user row on first login,\nso a caller reaching this endpoint already has an account. The agent's key\nis reassigned rather than revoked, so whatever it has hardcoded keeps\nworking — that is the point of claiming rather than starting over.","operationId":"claim_sandbox_sandbox_claim_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxClaimRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxClaimResponse"}}}},"404":{"description":"Unknown, already-used, or swept claim token. Claim tokens are single-use and die with the sandbox they belong to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sandbox/status":{"get":{"tags":["sandbox"],"summary":"Has this sandbox been claimed?","description":"Whether the sandbox this key belongs to has been claimed yet.\n\nAn agent hands its claim URL to a human and then has no way to learn what\nhappened — a device-code grant would tell it for free (issue #825). Until\nwe have one, polling this closes the loop.\n\nStill provisional means nobody has claimed it. Past that, \"not provisional\"\nalone would be a lie — every ordinary account would read as claimed — so\nthe answer comes from the claim event written on the account, which is\nalso the only durable evidence a claim happened once the provisional row\nis deleted.","operationId":"get_sandbox_status_sandbox_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sandbox/quickstart":{"get":{"tags":["sandbox"],"summary":"Agent quickstart guide (markdown)","description":"The agent quickstart, served as plain markdown.","operationId":"get_quickstart_sandbox_quickstart_get","responses":{"200":{"description":"The agent quickstart as plain markdown: mint a key, place a sandbox order, watch the lifecycle, rehearse each failure mode, go live.","content":{"text/markdown; charset=utf-8":{"schema":{"type":"string"}}}}},"security":[]}},"/device/code":{"post":{"tags":["device"],"summary":"Start a sign-in the account owner can approve from any device","description":"Mint a device code. No account needed.\n\nSend your `zn_test_` sandbox key as the bearer and the sandbox comes along:\nwhen the owner approves, its orders and key move onto their account and\nyour sandbox key keeps working, alongside the live key you receive.","operationId":"create_device_code_device_code_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/DeviceCodeCreate"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceCodeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/device/codes/{user_code}":{"get":{"tags":["device"],"summary":"What an approval page shows for a user code","description":"Public on purpose: it holds only what the agent said about itself, and\nthe approval page needs it before the human has signed in.","operationId":"describe_device_code_device_codes__user_code__get","parameters":[{"name":"user_code","in":"path","required":true,"schema":{"type":"string","title":"User Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceCodeInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/device/approve":{"post":{"tags":["device"],"summary":"Approve or deny a device code (signed-in browser only)","description":"The human's decision. A machine credential must never make it: an API\nkey approving a device code would be a key minting a key.","operationId":"decide_device_code_device_approve_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApproveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApproveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/device/token":{"post":{"tags":["device"],"summary":"Poll for the key (returns it exactly once)","operationId":"redeem_device_code_device_token_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyExchangeResponse"}}}},"400":{"description":"`authorization_pending` (keep polling), `slow_down` (poll less often), or `expired_token` (start over)."},"403":{"description":"`access_denied` — the owner declined; stop."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/endpoint":{"get":{"tags":["webhooks"],"summary":"Get Webhook Endpoint","description":"The URL Zinc delivers this account's webhooks to, and the HMAC secret\nthat signs them. Both are null until ``PUT /webhooks/endpoint`` is called.","operationId":"get_webhook_endpoint_webhooks_endpoint_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["webhooks"],"summary":"Set Webhook Endpoint","description":"Register (or replace) the webhook URL for this account.\n\nEvery order and return event Zinc emits for the account is POSTed to this\nURL. A signing secret is generated on first registration and returned so\nthe caller can verify the ``X-Webhook-Signature`` header; replacing the URL\nkeeps the existing secret, so a URL move never invalidates verification.","operationId":"set_webhook_endpoint_webhooks_endpoint_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["webhooks"],"summary":"Clear Webhook Endpoint","description":"Stop webhook delivery for this account by clearing the URL.\n\nThe signing secret is kept, so re-registering a URL later resumes\ndeliveries signed with the same secret the caller already verifies against.","operationId":"clear_webhook_endpoint_webhooks_endpoint_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"tags":["health"],"summary":"Get Public Health","description":"Customer-facing platform health.\n\nNo auth. Cached in-process for 4 minutes — at the marketing site's 5-min\ncron cadence, the DB is touched at most ~once per tick.","operationId":"get_public_health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicHealthResponse"}}}}}}}},"components":{"schemas":{"Address":{"properties":{"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"address_line1":{"type":"string","title":"Address Line1"},"address_line2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line2"},"city":{"type":"string","title":"City"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"postal_code":{"type":"string","title":"Postal Code","description":"Postal code. For a US address this must be a ZIP: 5 digits, optionally +4 (e.g. '98632' or '27517-8761')."},"phone_number":{"type":"string","title":"Phone Number"},"country":{"type":"string","title":"Country","description":"Country as an ISO 3166-1 alpha-2 code (e.g. 'US', 'CA', 'GB', 'DE'). Case-insensitive. Longer spellings such as 'USA' or 'United States' are rejected — the error names the code to use.","default":"US"}},"type":"object","required":["first_name","last_name","address_line1","city","postal_code","phone_number"],"title":"Address","description":"Shipping address model.\n\nSupports international addresses. The `state` field is optional for countries\nthat don't use states/provinces. The `country` field uses ISO 3166-1 alpha-2\ncountry codes (e.g., \"US\", \"CA\", \"GB\", \"DE\"), which are required rather than\ninferred — a longer spelling like \"USA\" is rejected with the code to use, so\neverything downstream can rely on seeing alpha-2."},"ApiKeyExchangeResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"key":{"type":"string","title":"Key"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"is_active":{"type":"boolean","title":"Is Active"},"is_test":{"type":"boolean","title":"Is Test"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"wallet_balance_cents":{"type":"integer","title":"Wallet Balance Cents","description":"Spendable wallet balance of the account the key belongs to."},"sandbox":{"anyOf":[{"$ref":"#/components/schemas/SandboxClaimSummary"},{"type":"null"}],"description":"What the sandbox claim moved, when `sandbox_key` was sent."},"starter_credit":{"anyOf":[{"$ref":"#/components/schemas/StarterCredit"},{"type":"null"}],"description":"Wallet credit granted alongside this key, if the account earned one, with a few items it is known to cover."},"api_key":{"type":"string","title":"Api Key","readOnly":true}},"type":"object","required":["id","key","name","is_active","is_test","created_at","wallet_balance_cents","api_key"],"title":"ApiKeyExchangeResponse","description":"The issued key, plus what the agent needs to act on it right away."},"BulkBatchListResponse":{"properties":{"batches":{"items":{"$ref":"#/components/schemas/BulkBatchResponse"},"type":"array","title":"Batches"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["total"],"title":"BulkBatchListResponse","description":"A user's recent bulk-upload batches (newest first)."},"BulkBatchResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"},"status":{"$ref":"#/components/schemas/BulkBatchStatus"},"row_count":{"type":"integer","title":"Row Count"},"placed_count":{"type":"integer","title":"Placed Count"},"failed_count":{"type":"integer","title":"Failed Count"},"notify_on_complete":{"type":"boolean","title":"Notify On Complete","default":false},"notified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Notified At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"rows":{"items":{"$ref":"#/components/schemas/BulkRowResult"},"type":"array","title":"Rows"}},"type":"object","required":["id","status","row_count","placed_count","failed_count","created_at","updated_at"],"title":"BulkBatchResponse","description":"A bulk-upload batch and (on the detail endpoint) its per-row results."},"BulkBatchStatus":{"type":"string","enum":["pending","processing","completed","failed"],"title":"BulkBatchStatus","description":"Lifecycle of a CSV bulk-upload batch."},"BulkRowResult":{"properties":{"index":{"type":"integer","title":"Index"},"status":{"type":"string","title":"Status","description":"placed | failed | pending"},"order_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order Id"},"order_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"recipient":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recipient"},"product_urls":{"items":{"type":"string"},"type":"array","title":"Product Urls"},"passthrough":{"additionalProperties":true,"type":"object","title":"Passthrough"}},"type":"object","required":["index","status"],"title":"BulkRowResult","description":"Per-row outcome once a batch has been (or is being) processed."},"BulkRowValidation":{"properties":{"index":{"type":"integer","title":"Index","description":"Zero-based row index in the uploaded file."},"ok":{"type":"boolean","title":"Ok"},"errors":{"items":{"type":"string"},"type":"array","title":"Errors"},"product_urls":{"items":{"type":"string"},"type":"array","title":"Product Urls"},"quantities":{"items":{"type":"integer"},"type":"array","title":"Quantities"},"recipient":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recipient"},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination"},"max_price":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Price"},"retailer_credentials_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer Credentials Id"},"passthrough":{"additionalProperties":true,"type":"object","title":"Passthrough"}},"type":"object","required":["index","ok"],"title":"BulkRowValidation","description":"Per-row result of a dry-run validation."},"BulkUploadRequest":{"properties":{"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename","description":"Original uploaded filename, for display."},"rows":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Rows","description":"Parsed CSV rows, each a mapping of column header to cell value."},"notify_on_complete":{"type":"boolean","title":"Notify On Complete","description":"Email the account holder once every order in this batch has reached a terminal state (placed, failed, or cancelled).","default":false}},"type":"object","title":"BulkUploadRequest","description":"Raw CSV rows (header -> cell) posted from the dashboard."},"BulkValidateResponse":{"properties":{"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"},"total_rows":{"type":"integer","title":"Total Rows"},"valid_rows":{"type":"integer","title":"Valid Rows"},"invalid_rows":{"type":"integer","title":"Invalid Rows"},"estimated_total_cents":{"type":"integer","title":"Estimated Total Cents"},"wallet_balance_cents":{"type":"integer","title":"Wallet Balance Cents"},"sufficient_funds":{"type":"boolean","title":"Sufficient Funds"},"billed_by_invoice":{"type":"boolean","title":"Billed By Invoice","default":false},"over_credit_limit":{"type":"boolean","title":"Over Credit Limit","default":false},"rows":{"items":{"$ref":"#/components/schemas/BulkRowValidation"},"type":"array","title":"Rows"}},"type":"object","required":["total_rows","valid_rows","invalid_rows","estimated_total_cents","wallet_balance_cents","sufficient_funds"],"title":"BulkValidateResponse","description":"Dry-run summary returned before any orders are placed."},"Components":{"properties":{"db":{"type":"string","enum":["up","warning","down"],"title":"Db"},"queue":{"type":"string","enum":["up","warning","down"],"title":"Queue"},"ordering":{"type":"string","enum":["up","warning","down"],"title":"Ordering"}},"type":"object","required":["db","queue","ordering"],"title":"Components"},"CustomerNotificationStatus":{"properties":{"email":{"type":"string","title":"Email"},"delivered":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Delivered"}},"type":"object","required":["email"],"title":"CustomerNotificationStatus","description":"End-customer email-notification status for an order.\n\nPresent only when the order opted into the notifications add-on. ``delivered``\nis the deliverability signal: True once a notification reached the shopper\n(an unsubscribe click also counts — it proves receipt), False if one was\nattempted but bounced/failed/suppressed, and null while nothing has been\nsent yet (e.g. not shipped) or a send is still in flight."},"CustomerNotifications":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"End-customer email address to send order updates to."}},"type":"object","required":["email"],"title":"CustomerNotifications","description":"Opt-in to email the END customer as this order progresses.\n\nProviding this block opts the order into the notifications add-on: a\nper-order surcharge is applied and the public tracking page becomes\nreachable for the order. The feature is gated server-side by\n``customer_emails_enabled``; while that's off the block has no effect."},"DeliveryMapPoint":{"properties":{"lat":{"type":"number","title":"Lat"},"long":{"type":"number","title":"Long"},"emoji":{"type":"string","title":"Emoji"}},"type":"object","required":["lat","long","emoji"],"title":"DeliveryMapPoint","description":"One delivered-order marker for the marketing site's globe.\n\nCoordinates are a ZIP centroid rounded to two decimals — never a street\naddress, but the rounding does not hide which ZIP (see\n``lib.routers.stats``). Deliberately carries no order id, name, address,\nor per-point timestamp."},"DeliveryMapResponse":{"properties":{"points":{"items":{"$ref":"#/components/schemas/DeliveryMapPoint"},"type":"array","title":"Points"},"window_days":{"type":"integer","title":"Window Days"},"generated_at":{"type":"string","format":"date-time","title":"Generated At"}},"type":"object","required":["points","window_days","generated_at"],"title":"DeliveryMapResponse","description":"Recent delivered orders as deduplicated, anonymized map points."},"DeviceApproveRequest":{"properties":{"user_code":{"type":"string","maxLength":16,"minLength":4,"title":"User Code"},"granted":{"type":"boolean","title":"Granted"}},"type":"object","required":["user_code","granted"],"title":"DeviceApproveRequest"},"DeviceApproveResponse":{"properties":{"status":{"type":"string","title":"Status"},"name":{"type":"string","title":"Name"},"message":{"type":"string","title":"Message"}},"type":"object","required":["status","name","message"],"title":"DeviceApproveResponse"},"DeviceCodeCreate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Name","description":"What to call yourself on the approval screen and as the key's label, e.g. 'Claude Code' or 'acme-restock-agent'."}},"type":"object","title":"DeviceCodeCreate"},"DeviceCodeInfo":{"properties":{"user_code":{"type":"string","title":"User Code"},"name":{"type":"string","title":"Name"},"source":{"type":"string","title":"Source","description":"Runtime guessed from the requesting User-Agent (claude-code, codex, curl, …)."},"sandbox_attached":{"type":"boolean","title":"Sandbox Attached","description":"Approval will also claim the agent's sandbox into the approving account."},"starter_credit_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Starter Credit Cents","description":"Wallet credit approval would add, when the attached sandbox has placed an order and the account has not had one; null otherwise."},"status":{"type":"string","title":"Status","description":"pending | approved | denied"}},"type":"object","required":["user_code","name","source","sandbox_attached","status"],"title":"DeviceCodeInfo","description":"What the approval page shows before the human decides. Public: it holds\nonly what the agent chose to say about itself."},"DeviceCodeResponse":{"properties":{"device_code":{"type":"string","title":"Device Code","description":"Your half of the handshake. Keep it; send it when polling. Never show it to the human."},"user_code":{"type":"string","title":"User Code","description":"The short code the human sees on the approval page."},"verification_uri":{"type":"string","title":"Verification Uri","description":"Where the human approves; they will be asked for `user_code`."},"verification_uri_complete":{"type":"string","title":"Verification Uri Complete","description":"Same page with the code filled in. Show this one."},"expires_in":{"type":"integer","title":"Expires In","description":"Seconds until this code stops working."},"interval":{"type":"integer","title":"Interval","description":"Minimum seconds between polls of POST /device/token."},"human_message":{"type":"string","title":"Human Message","description":"One sentence to show your operator, ready to paste."},"next":{"type":"string","title":"Next","description":"What to do now."}},"type":"object","required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval","human_message","next"],"title":"DeviceCodeResponse"},"DeviceTokenRequest":{"properties":{"device_code":{"type":"string","maxLength":128,"minLength":16,"title":"Device Code"}},"type":"object","required":["device_code"],"title":"DeviceTokenRequest"},"ErrorCode":{"type":"string","enum":["unauthorized","forbidden","invalid_token","token_expired","not_found","already_exists","validation_error","insufficient_funds","authorization_pending","slow_down","expired_token","access_denied","payment_failed","payment_method_required","invalid_payment_method","invalid_shipping_address","url_unreachable","non_us_retailer","unsupported_retailer","unsupported_country","retailer_country_not_supported","guest_checkout_not_supported","invalid_variant","out_of_stock","shipping_unavailable","order_not_cancellable","return_not_allowed","external_service_error","stripe_error","stytch_error","internal_error","bad_request","rate_limit_exceeded","service_unavailable"],"title":"ErrorCode","description":"Standardized error codes for the API."},"ErrorDetail":{"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","title":"Message"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"}},"type":"object","required":["code","message"],"title":"ErrorDetail","description":"Standard error response body."},"ErrorDetails":{"properties":{"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"},"address_validation_reasons":{"items":{"type":"string"},"type":"array","title":"Address Validation Reasons","default":[]},"field_errors":{"items":{"$ref":"#/components/schemas/FieldError"},"type":"array","title":"Field Errors","default":[]}},"additionalProperties":true,"type":"object","required":["code","message"],"title":"ErrorDetails","description":"Structured detail for a failed job.\n\nAdditive sibling to the flat `error`/`error_type` keys (kept for\nback-compat). The structured home for enrichment beyond the category +\nmessage — e.g. why a shipping address was rejected — so we don't keep\ngrowing loose top-level keys on the job output."},"ErrorResponse":{"properties":{"error":{"$ref":"#/components/schemas/ErrorDetail"}},"type":"object","required":["error"],"title":"ErrorResponse","description":"Wrapper for error responses."},"FieldError":{"properties":{"field":{"type":"string","title":"Field"},"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message","default":""},"received":{"title":"Received"},"expected":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected"}},"additionalProperties":true,"type":"object","required":["field","code"],"title":"FieldError","description":"A single field-level validation failure from a worker.\n\nMirrors Sonic's `FieldError` (src/agent/types.py): when a job fails schema\nvalidation the worker reports one of these per offending field, so consumers\ncan show \"state should be 'NY', got 'New York'\" instead of a bare count."},"FulfillmentConcern":{"type":"string","enum":["gift","items","quantity"],"title":"FulfillmentConcern","description":"A strict order rule the customer may list in ``fulfillment.best_effort``.\nAbsent = strict (today's behaviour); listed = relax it and ship anyway."},"FulfillmentConcession":{"properties":{"concern":{"type":"string","enum":["gift","items","quantity"],"title":"Concern"},"code":{"type":"string","title":"Code","description":"Worker error code that would have failed a strict order."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"item_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Item Id","description":"Order item this concession is about, if any."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"requested_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Requested Quantity"},"fulfilled_quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fulfilled Quantity"}},"additionalProperties":true,"type":"object","required":["concern","code"],"title":"FulfillmentConcession","description":"One rule that was actually relaxed while placing the order."},"FulfillmentPreferences":{"properties":{"best_effort":{"items":{"$ref":"#/components/schemas/FulfillmentConcern"},"type":"array","title":"Best Effort","description":"Rules to relax instead of failing the order. `gift`: if the retailer offers no free gift option, place a normal order (requires `is_gift: true`). `items`: if an item can't be bought (not found, out of stock, variant or condition unavailable, add-to-cart failed), skip it and ship the rest — its status becomes `skipped`; an empty cart still fails. `quantity`: if the retailer caps an item below the requested quantity, buy the cap. Everything not listed stays strict. `max_price` is never relaxed. Whatever was relaxed is reported in `fulfillment.concessions`."}},"additionalProperties":false,"type":"object","title":"FulfillmentPreferences","description":"Which strict order rules may be relaxed. The order contract is strict by\ndefault; this object only ever loosens it.\n\nOne list, ``best_effort``, naming the concerns to relax. Absent (or empty)\nis today's behaviour. There is deliberately no way to *say* \"strict\" — the\nexisting fields already mean that (``is_gift: true`` is a required gift; a\nproducts list is all-or-nothing), and a second spelling of the same intent\nis a second thing to get wrong. Unknown concerns and unknown keys are\nrejected rather than ignored, so a typo can never silently mean \"strict\".\n\nExtension point: a future softer mode is a sibling list (``prefer``,\n``nearest``), a future threshold a scalar (``min_items``) — never a new\ntop-level order field."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LifetimeStatsResponse":{"properties":{"orders_successful":{"type":"integer","title":"Orders Successful"},"gmv_cents":{"type":"integer","title":"Gmv Cents"},"v2_as_of":{"type":"string","format":"date-time","title":"V2 As Of"},"v1":{"$ref":"#/components/schemas/LifetimeV1Stats"},"combined":{"$ref":"#/components/schemas/LifetimeTotals"}},"type":"object","required":["orders_successful","gmv_cents","v2_as_of","v1","combined"],"title":"LifetimeStatsResponse","description":"Zinc's all-time order and GMV totals.\n\nTop-level ``orders_successful`` / ``gmv_cents`` are the v2 (this service's)\nnumbers, live as of ``v2_as_of``; ``v1`` is the seeded legacy snapshot;\n``combined`` is the convenience sum of both."},"LifetimeTotals":{"properties":{"orders_successful":{"type":"integer","title":"Orders Successful"},"gmv_cents":{"type":"integer","title":"Gmv Cents"}},"type":"object","required":["orders_successful","gmv_cents"],"title":"LifetimeTotals","description":"Combined v1 + v2 all-time totals."},"LifetimeV1Stats":{"properties":{"orders_successful":{"type":"integer","title":"Orders Successful"},"gmv_cents":{"type":"integer","title":"Gmv Cents"},"as_of":{"type":"string","format":"date","title":"As Of"},"source":{"type":"string","enum":["seeded","computed"],"title":"Source","default":"seeded"}},"type":"object","required":["orders_successful","gmv_cents","as_of"],"title":"LifetimeV1Stats","description":"All-time totals from the legacy v1 API (api.zinc.io).\n\n``source: \"computed\"`` — read from the snapshot the weekly worker job\nstores (see ``lib.tasks.v1_lifetime``); ``source: \"seeded\"`` — the\nin-code cold-start fallback constant."},"MarginSpec":{"properties":{"type":{"type":"string","enum":["flat","percent"],"title":"Type"},"value":{"type":"number","minimum":0.0,"title":"Value","description":"Cents when type='flat'; a percentage when type='percent' (e.g. 15 == 15%)."}},"type":"object","required":["type","value"],"title":"MarginSpec","description":"Customer's markup on the order cost (computed before fees)."},"OrderCartItem":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"product_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Id","description":"Retailer product identifier (Amazon ASIN, Walmart item number, SKU) when one is visible on the page. Often absent — do not rely on it to join rows back to the requested products."},"quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quantity"},"unit_price":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Unit Price","description":"Price per unit."},"line_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Line Total","description":"Total for this row, normally `quantity * unit_price`. May differ when the retailer applies a per-line promotion."}},"additionalProperties":true,"type":"object","title":"OrderCartItem","description":"A single product row from the retailer's cart or order summary.\n\nLenient and open (``extra=\"allow\"``) like its siblings — it mirrors\nfulfillment output that may be partial. All amounts are in cents."},"OrderConnectInfo":{"properties":{"state":{"type":"string","title":"State","description":"secured | captured | released | refunded | …"},"secured_amount":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Secured Amount"},"order_cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Order Cost","description":"Actual order cost (goods) captured when the order is placed."},"customer_margin":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Customer Margin"},"zinc_fee":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Zinc Fee"},"stripe_fee":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Stripe Fee"},"final_charge":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Final Charge"},"transfer_amount":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Transfer Amount"},"payment_intent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Intent Id"},"connected_account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connected Account Id"},"simulated":{"type":"boolean","title":"Simulated","default":false}},"type":"object","required":["state"],"title":"OrderConnectInfo","description":"Stripe Connect charge + state for an order paid via Connect. Present only\nwhen the order has a Connect profile; ``null`` for prepaid-wallet orders. All\namounts in cents.\n\n``state`` moves ``secured`` → ``captured`` (or ``released``/``refunded`` on a\nterminal outcome). The post-capture fields (``order_cost`` and below) are\npopulated once Zinc places the order with the retailer and the actual total\nis captured."},"OrderCreate":{"properties":{"products":{"items":{"$ref":"#/components/schemas/OrderProduct"},"type":"array","title":"Products"},"shipping_address":{"$ref":"#/components/schemas/Address"},"max_price":{"type":"integer","title":"Max Price","description":"Maximum price (in cents) allowed for an order before it is finalized."},"idempotency_key":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"title":"Idempotency Key","description":"Optional idempotency key to prevent duplicate orders. If not provided, one will be generated."},"retailer_credentials_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer Credentials Id","description":"Optional short ID (e.g., 'zn_acct_XXXXXXXX') of specific retailer credentials to use for this order. If not provided, credentials will be selected automatically."},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata","description":"Optional metadata to attach to the order. Can contain arbitrary key-value pairs."},"po_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Po Number","description":"Optional purchase order number for the order."},"handling_days_max":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Handling Days Max","description":"Optional ceiling on a seller's shipping and handling days. Omit or send null for no limit."},"is_gift":{"type":"boolean","title":"Is Gift","description":"Mark the order as a gift, suppressing prices on the packing slip. If the retailer's checkout offers no free gift option, the order FAILS with `gift_option_unavailable` rather than being placed as a normal order — a gift that arrives with prices visible to the recipient is treated as worse than no order.","default":false},"gift_message":{"anyOf":[{"type":"string","maxLength":240},{"type":"null"}],"title":"Gift Message","description":"Optional note for the recipient, entered into the retailer's gift-message field at checkout. Requires `is_gift` to be true. Max 240 characters. Delivered where the retailer's checkout offers a gift message; the order is still placed without it where one isn't available."},"payment":{"anyOf":[{"$ref":"#/components/schemas/OrderPayment"},{"type":"null"}],"description":"Optional payment block. Omit for prepaid-wallet billing (default)."},"customer_notifications":{"anyOf":[{"$ref":"#/components/schemas/CustomerNotifications"},{"type":"null"}],"description":"Opt in to emailing the end customer order updates (and unlock the public tracking page for this order). Adds a per-order surcharge. Omit for no customer notifications (default)."},"fulfillment":{"anyOf":[{"$ref":"#/components/schemas/FulfillmentPreferences"},{"type":"null"}],"description":"Loosen the order's strict-by-default rules. Omit for today's behaviour: any rule that can't be met fails the order. List the rules to relax in `best_effort` (`gift`, `items`, `quantity`) to have the order placed anyway; whatever was relaxed is reported back in `fulfillment.concessions` on the order. `max_price` is never relaxed."}},"type":"object","required":["products","shipping_address","max_price"],"title":"OrderCreate","description":"Request model for creating a new order."},"OrderFulfillment":{"properties":{"best_effort":{"items":{"$ref":"#/components/schemas/FulfillmentConcern"},"type":"array","title":"Best Effort","description":"Rules to relax instead of failing the order. `gift`: if the retailer offers no free gift option, place a normal order (requires `is_gift: true`). `items`: if an item can't be bought (not found, out of stock, variant or condition unavailable, add-to-cart failed), skip it and ship the rest — its status becomes `skipped`; an empty cart still fails. `quantity`: if the retailer caps an item below the requested quantity, buy the cap. Everything not listed stays strict. `max_price` is never relaxed. Whatever was relaxed is reported in `fulfillment.concessions`."},"concessions":{"items":{"$ref":"#/components/schemas/FulfillmentConcession"},"type":"array","title":"Concessions","default":[]}},"type":"object","title":"OrderFulfillment","description":"What was asked (``best_effort``, empty for a strict order) plus what was\nactually relaxed (``concessions``, empty when the order was fulfilled\nexactly as requested — which is every strict order)."},"OrderItemResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"url":{"type":"string","title":"Url"},"quantity":{"type":"integer","title":"Quantity"},"variant":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProductVariant"},"type":"array"},{"type":"null"}],"title":"Variant"},"condition_in":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProductCondition"},"type":"array"},{"type":"null"}],"title":"Condition In"},"condition_not_in":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProductCondition"},"type":"array"},{"type":"null"}],"title":"Condition Not In"},"status":{"$ref":"#/components/schemas/OrderItemStatus"},"cancellation_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cancellation Reason"},"error_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Type"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","url","quantity","status","created_at","updated_at"],"title":"OrderItemResponse","description":"Response model for order item data.\n\nField names mirror the OrderItem model, so callers build it with\n``OrderItemResponse.model_validate(item)`` rather than enumerating fields."},"OrderItemStatus":{"type":"string","enum":["pending","processing","ordered","shipped","delivered","cancelled","failed","skipped"],"title":"OrderItemStatus"},"OrderJobResult":{"properties":{"success":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Success"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"error_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Type"},"error_details":{"anyOf":[{"$ref":"#/components/schemas/ErrorDetails"},{"type":"null"}]},"price_components":{"anyOf":[{"$ref":"#/components/schemas/OrderPriceComponents"},{"type":"null"}]},"estimated_delivery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Estimated Delivery"},"merchant_order_ids":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Merchant Order Ids"}},"additionalProperties":true,"type":"object","title":"OrderJobResult","description":"Fulfillment result surfaced on a completed or failed order. Null while the\norder is still processing.\n\nLenient and open (``extra=\"allow\"``) so it documents the common fields —\nsuccess, price breakdown, errors, merchant order ids, delivery estimate —\nwithout constraining the full processing payload, which carries additional\ninternal detail."},"OrderListResponse":{"properties":{"orders":{"items":{"$ref":"#/components/schemas/OrderResponse"},"type":"array","title":"Orders"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["orders","total","limit","offset"],"title":"OrderListResponse","description":"List of orders for a particular user_id"},"OrderMilestone":{"properties":{"type":{"type":"string","enum":["order_created","order_placed","tracking_acquired","shipped","delivered","undeliverable","order_failed","cancelled"],"title":"Type"},"label":{"type":"string","title":"Label"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"source":{"type":"string","title":"Source"},"detail":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Detail"}},"type":"object","required":["type","label","occurred_at","source"],"title":"OrderMilestone","description":"A single point on the customer-facing order timeline."},"OrderPayment":{"properties":{"mode":{"type":"string","enum":["wallet","card","connect"],"title":"Mode","default":"wallet"},"payment_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Method","description":"Card mode: one of the caller's saved Stripe payment methods (pm_…) to hold on; omit to use the default. Connect mode: the end-customer's vaulted payment-method id on the connected account."},"customer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer","description":"Connect mode: the end-customer's Stripe Customer id (cus_…) on the connected account. Required to charge a *reusable* saved card off-session; omit only for a one-time, unattached payment method."},"margin":{"anyOf":[{"$ref":"#/components/schemas/MarginSpec"},{"type":"null"}],"description":"Connect mode: the customer's margin on the order."}},"type":"object","title":"OrderPayment","description":"Optional payment block. Absent or mode='wallet' ⇒ unchanged prepaid-wallet\nbehavior. mode='card' charges the caller's own saved card for this one order:\na hold for max_price + the API fee now, captured for the actual total when the\norder is placed, released if it never is — the wallet is not involved.\nmode='connect' charges the end-customer's vaulted card in real time via\nStripe Connect (see ConnectService)."},"OrderPaymentInfo":{"properties":{"mode":{"type":"string","const":"card","title":"Mode","default":"card"},"state":{"type":"string","title":"State","description":"authorized | captured | released | refunded | …"},"authorized_cents":{"type":"integer","title":"Authorized Cents"},"fee_cents":{"type":"integer","title":"Fee Cents"},"stripe_fee_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Stripe Fee Cents","description":"Card-processing fee grossed into the amount above, the same way a wallet top-up and a Connect charge gross it in."},"captured_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Captured Cents"},"payment_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Method"},"payment_intent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Intent Id"},"simulated":{"type":"boolean","title":"Simulated","default":false}},"type":"object","required":["state","authorized_cents","fee_cents"],"title":"OrderPaymentInfo","description":"How a card-funded order is paid, and where its Stripe hold stands.\nPresent only for ``payment.mode == \"card\"`` orders; ``null`` for wallet and\nConnect orders (Connect has its own ``connect`` block). All amounts in cents.\n\n``state`` moves ``authorized`` → ``captured`` when the retailer order is\nplaced (``captured_cents`` = actual total + fee), or → ``released`` when the\norder fails or is cancelled first. The card is only ever charged\n``captured_cents``; the difference to ``authorized_cents`` is released."},"OrderPriceComponents":{"properties":{"subtotal":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Subtotal"},"tax":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tax"},"shipping":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Shipping"},"discount":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Discount","description":"Promotion/free-shipping discount applied by the retailer, as a positive magnitude subtracted from the total. A nonzero `shipping` paired with an equal `discount` means shipping was itemized but waived — the total correctly excludes it."},"fees":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fees","description":"Retailer fees not folded into subtotal/shipping/tax (gift wrap, import/regulatory charges), as a positive magnitude added to the total. Surfaced so a total exceeding the shown parts reads as an itemized fee rather than an unexplained charge."},"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total","description":"Order total in `currency` (subtotal + tax + shipping + fees − discount)."},"converted_payment_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Converted Payment Total","description":"`total` converted to the currency actually charged."},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"payment_currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Currency"},"cart_items":{"anyOf":[{"items":{"$ref":"#/components/schemas/OrderCartItem"},"type":"array"},{"type":"null"}],"title":"Cart Items","description":"Per-product rows making up `subtotal`. Distinct from `line_items`, which carries order-level adjustments (discounts, fees) and shares no fields with these — a discount is never a cart row. Null when the retailer's checkout page did not expose readable line items, so handle both `null` and `[]`. Individual fields may also be null on a row we could only partly read. Best-effort: `sum(line_total)` normally equals `subtotal`, but that is not guaranteed and must not be used as a billing input — the charge is always driven by `total`."},"line_items":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Line Items","description":"Order-level adjustments, each `{description, amount, category}`. Null — not `[]` — when the producer didn't supply them, which is every BizAPI-placed order today, so null-check before iterating."}},"additionalProperties":true,"type":"object","title":"OrderPriceComponents","description":"Retailer price breakdown for an order, all amounts in cents.\n\nLenient by design — every field is optional and unknown keys are preserved\n(``extra=\"allow\"``) — because it mirrors fulfillment output that may be\npartial (e.g. a failed order) or carry extra detail. Documents the common\nfields without revalidating the raw payload."},"OrderProduct":{"properties":{"url":{"type":"string","title":"Url","examples":["https://www.amazon.com/dp/B07JGBW826"]},"quantity":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Quantity","default":1},"variant":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProductVariant"},"type":"array"},{"type":"null"}],"title":"Variant","description":"Product variants to select (e.g., color, size)"},"condition_in":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProductCondition"},"type":"array"},{"type":"null"}],"title":"Condition In","description":"Condition allowlist. The agent only buys an offer whose condition is in this list. Each value must be a canonical condition. Omit or send null/[] for no constraint."},"condition_not_in":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProductCondition"},"type":"array"},{"type":"null"}],"title":"Condition Not In","description":"Condition denylist. The agent will not buy an offer whose condition is in this list. Each value must be a canonical condition. Omit or send null/[] for no constraint."}},"type":"object","required":["url"],"title":"OrderProduct","description":"Product item for an order.\n\nFor example:\n````{\n    url: \"https://www.amazon.com/dp/B07JGBW826\",\n    quantity: 1\n}```"},"OrderResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"status":{"$ref":"#/components/schemas/OrderStatus"},"max_price":{"type":"integer","title":"Max Price"},"attempts":{"type":"integer","title":"Attempts"},"items":{"items":{"$ref":"#/components/schemas/OrderItemResponse"},"type":"array","title":"Items"},"shipping_address":{"additionalProperties":true,"type":"object","title":"Shipping Address"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","default":{}},"po_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Po Number"},"handling_days_max":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Handling Days Max"},"is_gift":{"type":"boolean","title":"Is Gift","default":false},"gift_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gift Message"},"retailer_credentials_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer Credentials Id"},"retailer_credentials_uuid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer Credentials Uuid"},"job_result":{"anyOf":[{"$ref":"#/components/schemas/OrderJobResult"},{"type":"null"}],"description":"Fulfillment result and price breakdown for a completed or failed order; null while processing."},"merchant_order_ids":{"items":{"type":"string"},"type":"array","title":"Merchant Order Ids","description":"The retailer's own order number(s) for this order (e.g. an Amazon `113-…` ID), as recorded when it was placed. Empty while processing, or if the order never reached the retailer.","default":[]},"tracking_numbers":{"items":{"$ref":"#/components/schemas/TrackingNumberResponse"},"type":"array","title":"Tracking Numbers","default":[]},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"user_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"User Id"},"returns":{"items":{"$ref":"#/components/schemas/ReturnRequestSummary"},"type":"array","title":"Returns","default":[]},"connect":{"anyOf":[{"$ref":"#/components/schemas/OrderConnectInfo"},{"type":"null"}],"description":"Stripe Connect charge details when this order was paid via Connect; null for prepaid-wallet orders."},"payment":{"anyOf":[{"$ref":"#/components/schemas/OrderPaymentInfo"},{"type":"null"}],"description":"Card hold details when this order was paid with payment.mode='card'; null for wallet and Connect orders."},"customer_notifications":{"anyOf":[{"$ref":"#/components/schemas/CustomerNotificationStatus"},{"type":"null"}],"description":"End-customer email-notification status when the order opted into the notifications add-on; null when it didn't."},"fulfillment":{"$ref":"#/components/schemas/OrderFulfillment","description":"`best_effort`: the rules this order allowed to be relaxed (empty = strict). `concessions`: every rule that actually was, with the worker's code and the item it concerned. Empty concessions means the order was fulfilled exactly as requested."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","status","max_price","attempts","items","shipping_address","retailer_credentials_id","created_at","updated_at"],"title":"OrderResponse","description":"Response model for order data."},"OrderStatus":{"type":"string","enum":["pending","in_progress","order_placed","order_failed","cancelled","cancelled_by_retailer"],"title":"OrderStatus"},"OrderTimelineResponse":{"properties":{"order_id":{"type":"string","format":"uuid","title":"Order Id"},"current_status":{"$ref":"#/components/schemas/OrderStatus"},"milestones":{"items":{"$ref":"#/components/schemas/OrderMilestone"},"type":"array","title":"Milestones"}},"type":"object","required":["order_id","current_status","milestones"],"title":"OrderTimelineResponse","description":"Customer-facing lifecycle timeline for an order.\n\nDerived on read by merging placement logs (OrderLog) with carrier tracking\nstate (TrackingNumber / TrackingCheckpoint). No dedicated storage — works\nretroactively on every historical order."},"ProductCondition":{"type":"string","enum":["New","Refurbished","UsedLikeNew","UsedVeryGood","UsedGood","UsedAcceptable"],"title":"ProductCondition","description":"Canonical product-condition strings for condition filtering.\n\nMatching against ``condition_in`` / ``condition_not_in`` is exact and\ncase-sensitive against these *values*. The vocabulary matches the Zinc v1\nAPI's ``seller_selection_criteria`` condition strings. The Sonic worker's\nCANONICAL_CONDITIONS (``src/agent/schemas.py``) must use the same strings,\nsince they ride through the job payload unchanged.\n\nThe member *name* is the corresponding Amazon BizAPI offer condition code\n(see ``CONDITION_MAP`` in ``bizapi_service``), so the canonical→BizAPI\nmapping is derived from the enum rather than maintained as a separate table."},"ProductSearchResponse":{"properties":{"status":{"type":"string","title":"Status"},"results":{"items":{"$ref":"#/components/schemas/ProductSearchResult"},"type":"array","title":"Results","default":[]},"next_page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Next Page","description":"Only set on free_shipping searches: the page to request to keep paging. An empty `results` with a non-null next_page is NOT the end of results — request next_page to continue. None means results are exhausted (or the search was unfiltered)."}},"type":"object","required":["status"],"title":"ProductSearchResponse","description":"Response from the product search endpoint."},"ProductSearchResult":{"properties":{"product_id":{"type":"string","title":"Product Id"},"title":{"type":"string","title":"Title"},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image"},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"},"price":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price"},"stars":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stars"},"num_reviews":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Num Reviews"},"num_offers_estimate":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Num Offers Estimate"},"num_sales":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Num Sales"},"product_details":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Product Details"},"fresh":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Fresh"},"prime":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Prime"},"pantry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Pantry"},"addon":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Addon"},"ship_price":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ship Price"},"upc":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upc"},"mpn":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mpn"},"available":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Available"},"available_online":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Available Online"},"available_instore":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Available Instore"},"freight_shipping":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Freight Shipping"},"preorder":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Preorder"},"first_party_seller":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"First Party Seller"},"buyapi_hint":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Buyapi Hint"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"variant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant Id"},"currency_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency Code"}},"type":"object","required":["product_id","title"],"title":"ProductSearchResult","description":"Normalized product search result across retailers.\n\nCommon fields are required or have defaults; retailer-specific fields are optional."},"ProductVariant":{"properties":{"label":{"type":"string","minLength":1,"title":"Label","description":"Variant label (e.g., 'Color', 'Size')"},"value":{"type":"string","minLength":1,"title":"Value","description":"Variant value (e.g., 'Black', 'Large')"}},"type":"object","required":["label","value"],"title":"ProductVariant","description":"Product variant schema for selecting product options (color, size, etc.)."},"PublicHealthResponse":{"properties":{"status":{"type":"string","enum":["up","warning","down"],"title":"Status"},"components":{"$ref":"#/components/schemas/Components"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["status","components","updated_at"],"title":"PublicHealthResponse"},"PublicRetailer":{"properties":{"retailer":{"type":"string","title":"Retailer","description":"Stable retailer identifier / slug, e.g. 'amazon'."},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Human-friendly name, e.g. 'Amazon'."},"base_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Url","description":"Retailer domain, e.g. 'amazon.com'."},"no_account_needed":{"type":"boolean","title":"No Account Needed","description":"Whether Zinc can place the order for you without your own account (guest checkout or Zinc-managed credentials).","default":false},"use_your_account":{"type":"boolean","title":"Use Your Account","description":"Whether you can order through your own retailer account.","default":false},"supported_countries":{"items":{"type":"string"},"type":"array","title":"Supported Countries","description":"ISO 3166-1 alpha-2 countries this retailer has declared it ships to. Empty = no declared restriction."},"free_shipping":{"type":"boolean","title":"Free Shipping","description":"Whether free shipping is offered (unconditionally or above free_shipping_threshold_cents).","default":false},"free_shipping_threshold_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Free Shipping Threshold Cents","description":"Order subtotal (cents) at/above which shipping is free; null means no threshold."}},"type":"object","required":["retailer"],"title":"PublicRetailer","description":"A supported retailer in the public catalog — one flat object per brand.\n\nThis is the customer-facing \"what do you support?\" shape: a retailer\nidentifier, where to buy from, the countries we ship to, the free-shipping\npolicy, and how orders are fulfilled (Zinc-managed vs. bring-your-own\naccount). Internal guest-checkout/credential details remain omitted."},"PublicRetailerListResponse":{"properties":{"retailers":{"items":{"$ref":"#/components/schemas/PublicRetailer"},"type":"array","title":"Retailers"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["retailers","total"],"title":"PublicRetailerListResponse","description":"Public supported-retailer catalog."},"PublicTrackingDestination":{"properties":{"address_line1":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line1"},"address_line2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line2"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},"type":"object","title":"PublicTrackingDestination","description":"Where an order is headed, for the public tracking page.\n\nDeliberately omits the recipient's name and phone number — everything else\non the shipping address (street, city, state, ZIP, country) is shown."},"PublicTrackingItem":{"properties":{"url":{"type":"string","title":"Url"},"quantity":{"type":"integer","title":"Quantity"},"variant":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Variant"},"status":{"$ref":"#/components/schemas/OrderItemStatus"}},"type":"object","required":["url","quantity","status"],"title":"PublicTrackingItem","description":"An ordered item shown on the public tracking page."},"PublicTrackingResponse":{"properties":{"order_id":{"type":"string","format":"uuid","title":"Order Id"},"current_status":{"$ref":"#/components/schemas/OrderStatus"},"estimated_delivery_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Estimated Delivery Date","description":"Best-known order-level delivery estimate (destination-local calendar date); per-package estimates are on each shipment."},"destination":{"$ref":"#/components/schemas/PublicTrackingDestination"},"shipments":{"items":{"$ref":"#/components/schemas/PublicTrackingShipment"},"type":"array","title":"Shipments"},"items":{"items":{"$ref":"#/components/schemas/PublicTrackingItem"},"type":"array","title":"Items"}},"type":"object","required":["order_id","current_status","destination","shipments","items"],"title":"PublicTrackingResponse","description":"Public, unauthenticated tracking view for an order.\n\nKeyed by the order's random UUID (used as an unguessable share token). Shows\nthe status, destination, per-package carrier checkpoints, and the ordered\nitems — everything except the recipient's name and phone."},"PublicTrackingShipment":{"properties":{"carrier":{"type":"string","title":"Carrier"},"tracking_number":{"type":"string","title":"Tracking Number"},"status":{"anyOf":[{"$ref":"#/components/schemas/TrackingStatus"},{"type":"null"}]},"estimated_delivery_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Estimated Delivery Date"},"checkpoints":{"items":{"$ref":"#/components/schemas/TrackingCheckpointResponse"},"type":"array","title":"Checkpoints"}},"type":"object","required":["carrier","tracking_number"],"title":"PublicTrackingShipment","description":"One package (tracking number) on the public tracking page.\n\nIntentionally minimal: carrier + tracking number (the page links to the\ncarrier's own tracking site), status, ETA, and the scan history. The\n17track URL and Zinc alias are deliberately NOT exposed here."},"RecentCall":{"properties":{"endpoint":{"type":"string","title":"Endpoint"},"at":{"type":"string","title":"At"},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"},"result_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Result Count"},"retailer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer"},"product_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Id"}},"type":"object","required":["endpoint","at"],"title":"RecentCall","description":"A single metered data-API call, for the \"recent activity\" table."},"RetailerCredentialsCreate":{"properties":{"email":{"type":"string","title":"Email"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"},"retailer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer","description":"Retailer name (e.g., 'amazon'). If null, applies as default credentials."},"totp_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Totp Secret","description":"TOTP secret key for 2FA (will be encrypted at rest)."},"retailer_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Retailer Config"}},"type":"object","required":["email"],"title":"RetailerCredentialsCreate","description":"Request model for creating retailer credentials."},"RetailerCredentialsListResponse":{"properties":{"credentials":{"items":{"$ref":"#/components/schemas/RetailerCredentialsResponse"},"type":"array","title":"Credentials"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["credentials","total"],"title":"RetailerCredentialsListResponse","description":"Response model for list of retailer credentials."},"RetailerCredentialsResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"short_id":{"type":"string","title":"Short Id"},"email":{"type":"string","title":"Email"},"retailer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer"},"has_totp":{"type":"boolean","title":"Has Totp","description":"Whether TOTP 2FA is configured for this account.","default":false},"has_forwarding":{"type":"boolean","title":"Has Forwarding","description":"Whether email forwarding has been verified for this account.","default":false},"is_paused":{"type":"boolean","title":"Is Paused","description":"Whether this credential is paused (jobs will not be picked up).","default":false},"locked_by_job_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Locked By Job Id","description":"The job ID currently using this credential, if any."},"cooldown_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cooldown Minutes","description":"Minimum minutes between uses. None = no cooldown."},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At","description":"When this credential was last released after a job completed."},"retailer_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Retailer Config"},"forwarding_email":{"type":"string","title":"Forwarding Email","description":"Email address to forward retailer emails to for verification and 2FA code extraction."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","short_id","email","retailer","forwarding_email","created_at","updated_at"],"title":"RetailerCredentialsResponse","description":"Response model for retailer credentials."},"RetailerCredentialsUpdate":{"properties":{"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"},"retailer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retailer"},"totp_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Totp Secret"},"retailer_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Retailer Config"}},"type":"object","title":"RetailerCredentialsUpdate","description":"Request model for updating retailer credentials."},"ReturnRequestCreate":{"properties":{"order_id":{"type":"string","format":"uuid","title":"Order Id"},"items":{"items":{"$ref":"#/components/schemas/ReturnRequestItem"},"type":"array","minItems":1,"title":"Items","description":"Per-product lines being returned"},"reason":{"$ref":"#/components/schemas/ReturnRequestReason"},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes"}},"type":"object","required":["order_id","items","reason"],"title":"ReturnRequestCreate"},"ReturnRequestItem":{"properties":{"order_item_id":{"type":"string","format":"uuid","title":"Order Item Id"},"quantity":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Quantity"}},"type":"object","required":["order_item_id","quantity"],"title":"ReturnRequestItem","description":"A single product line on a return — which order item, how many."},"ReturnRequestListResponse":{"properties":{"return_requests":{"items":{"$ref":"#/components/schemas/ReturnRequestResponse"},"type":"array","title":"Return Requests"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["return_requests","total"],"title":"ReturnRequestListResponse"},"ReturnRequestReason":{"type":"string","enum":["damaged","not_delivered","empty_box","wrong_item","defective","not_as_described","wrong_size","no_longer_needed","forced_cancellation","other"],"title":"ReturnRequestReason","description":"Universal return reason codes (Shopify + Amazon prepaid alignment)."},"ReturnRequestResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"order_id":{"type":"string","format":"uuid","title":"Order Id"},"status":{"$ref":"#/components/schemas/ReturnRequestStatus"},"reason":{"$ref":"#/components/schemas/ReturnRequestReason"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"resolution_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Notes"},"items":{"items":{"$ref":"#/components/schemas/ReturnRequestItem"},"type":"array","title":"Items","default":[]},"label_urls":{"items":{"type":"string"},"type":"array","title":"Label Urls","default":[]},"merchant_return_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Merchant Return Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","order_id","status","reason","notes","created_at","updated_at"],"title":"ReturnRequestResponse"},"ReturnRequestStatus":{"type":"string","enum":["open","approved","denied","credited"],"title":"ReturnRequestStatus"},"ReturnRequestSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"status":{"$ref":"#/components/schemas/ReturnRequestStatus"},"reason":{"$ref":"#/components/schemas/ReturnRequestReason"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"resolution_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Notes"},"items":{"items":{"$ref":"#/components/schemas/ReturnRequestItem"},"type":"array","title":"Items","default":[]},"label_urls":{"items":{"type":"string"},"type":"array","title":"Label Urls","default":[]},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","status","reason","created_at","updated_at"],"title":"ReturnRequestSummary"},"SandboxClaimRequest":{"properties":{"token":{"type":"string","maxLength":128,"minLength":1,"title":"Token"}},"type":"object","required":["token"],"title":"SandboxClaimRequest","description":"The raw claim token from a claim URL."},"SandboxClaimResponse":{"properties":{"claimed":{"type":"boolean","title":"Claimed"},"orders_moved":{"type":"integer","title":"Orders Moved"},"keys_moved":{"type":"integer","title":"Keys Moved"},"message":{"type":"string","title":"Message"}},"type":"object","required":["claimed","orders_moved","keys_moved","message"],"title":"SandboxClaimResponse","description":"What a completed claim moved onto the account."},"SandboxClaimSummary":{"properties":{"orders_moved":{"type":"integer","title":"Orders Moved"},"keys_moved":{"type":"integer","title":"Keys Moved"},"note":{"type":"string","title":"Note","default":"Your sandbox order history and its key now belong to this account. The sandbox key keeps working for test mode; use the live key for real orders."}},"type":"object","required":["orders_moved","keys_moved"],"title":"SandboxClaimSummary","description":"What claiming the agent's sandbox moved onto the approving account."},"SandboxKeyCreate":{"properties":{"email":{"anyOf":[{"type":"string","maxLength":254},{"type":"null"}],"title":"Email","description":"(optional) Your operator's email address — we'll send them a link to claim this sandbox and its order history when they want a live account. Never used for anything else, and never required."},"name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Name","description":"(optional) What this key is for, e.g. 'acme-shopping-agent dev testing'. Becomes the key's label."}},"type":"object","title":"SandboxKeyCreate","description":"Optional context for a sandbox key mint.\n\nAn empty body (or none at all) is valid — nothing here gates the mint."},"SandboxKeyResponse":{"properties":{"api_key":{"type":"string","title":"Api Key","description":"Sandbox API key (zn_test_...). Send as `Authorization: Bearer <api_key>` — test keys route to the sandbox automatically, no extra headers needed."},"key":{"type":"string","title":"Key","description":"Same value as `api_key`; every key response carries both names."},"expires_policy":{"type":"string","title":"Expires Policy"},"quickstart_url":{"type":"string","title":"Quickstart Url"},"example_order":{"additionalProperties":true,"type":"object","title":"Example Order","description":"A complete valid body for POST /orders — try it as-is."},"claim_url":{"type":"string","title":"Claim Url","description":"Give this to the human you work for. Signing in there folds this sandbox — its order history and this key, which keeps working — into their Zinc account. Poll GET /sandbox/status to find out when they have."},"next":{"items":{"type":"string"},"type":"array","title":"Next","description":"What to do now, in order."}},"type":"object","required":["api_key","key","expires_policy","quickstart_url","example_order","claim_url","next"],"title":"SandboxKeyResponse","description":"A freshly minted sandbox key, with the next steps inline.\n\nThe response doubles as documentation: agents read the bytes they get\nback, not a docs site, so every field they need to proceed is here."},"SandboxStatusResponse":{"properties":{"claimed":{"type":"boolean","title":"Claimed","description":"True once a human has folded this sandbox into an account — at which point this key is a real account's key and keeps working."},"claimed_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Claimed By Email","description":"The claiming account's email, once claimed."},"hint":{"type":"string","title":"Hint","description":"What to do next given this status."}},"type":"object","required":["claimed","hint"],"title":"SandboxStatusResponse","description":"Whether this sandbox has been claimed yet.\n\nLets an agent learn the outcome of a claim it can't see — the thing a\ndevice-code grant would give it for free (see issue #825)."},"SearchResponse":{"properties":{"status":{"type":"string","title":"Status"},"query":{"type":"string","title":"Query"},"results":{"items":{"$ref":"#/components/schemas/Sku"},"type":"array","title":"Results","default":[]},"excluded_by_price":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Excluded By Price","description":"How many orderable results the min_price/max_price clamp removed; null when no clamp was set."},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint","description":"Set when the result list is empty for a reason the caller can act on."}},"type":"object","required":["status","query"],"title":"SearchResponse","description":"Response from the cross-retailer search endpoint."},"Sku":{"properties":{"url":{"type":"string","title":"Url"},"retailer":{"type":"string","title":"Retailer"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image"},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"},"price":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price"},"stars":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stars"},"num_reviews":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Num Reviews"},"available":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Available"}},"type":"object","required":["url","retailer"],"title":"Sku","description":"A cross-retailer buyable listing. Pass `url` to /orders to buy it."},"StarterCredit":{"properties":{"cents":{"type":"integer","title":"Cents","description":"Starter credit added to the wallet by this approval; 0 when none was."},"reason":{"type":"string","title":"Reason","description":"`granted`; `already_granted` (the account received its one starter credit earlier); `no_sandbox_order` (no proven sandbox was attached)."},"wallet_balance_cents":{"type":"integer","title":"Wallet Balance Cents","description":"Spendable wallet balance right now."},"max_price_cents":{"type":"integer","title":"Max Price Cents","description":"Largest `max_price` an order can carry on the wallet alone (balance minus the per-order fee)."},"message":{"type":"string","title":"Message","description":"Plain-language version of the above, for the operator."},"picks":{"items":{"$ref":"#/components/schemas/StarterPick"},"type":"array","title":"Picks","description":"Suggested first purchases (a bag of coffee)."}},"type":"object","required":["cents","reason","wallet_balance_cents","max_price_cents","message","picks"],"title":"StarterCredit","description":"The wallet this key spends from, in the terms an agent needs to place\nits first order. Always present on a key-issuing response, whether or not\na starter credit landed this time: an agent must never have to infer its\nbudget from a null."},"StarterPick":{"properties":{"title":{"type":"string","title":"Title"},"url":{"type":"string","title":"Url"},"retailer":{"type":"string","title":"Retailer"},"price_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price Cents","description":"Retailer list price when the pick was last verified; null when not yet checked."}},"type":"object","required":["title","url","retailer"],"title":"StarterPick","description":"One thing the starter credit is known to cover, with an orderable URL."},"TrackingCheckpointResponse":{"properties":{"checkpoint_time":{"type":"string","format":"date-time","title":"Checkpoint Time","description":"When the carrier recorded this scan event."},"status":{"$ref":"#/components/schemas/TrackingStatus","description":"Carrier-derived shipment state at this checkpoint."},"message":{"type":"string","title":"Message","description":"Carrier-provided description of the scan."},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"zip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Zip"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location","description":"Free-form location string when city/state are absent."}},"type":"object","required":["checkpoint_time","status","message"],"title":"TrackingCheckpointResponse","description":"A single carrier-reported scan event for a tracking number."},"TrackingNumberResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"carrier":{"type":"string","title":"Carrier"},"tracking_number":{"type":"string","title":"Tracking Number"},"zinc_tracking_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Zinc Tracking Number","description":"Zinc-issued tracking number under pseudo-carrier ZNLOGIC (format ZPY…YQ). Carrier-independent alias for this shipment; assigned once and never changes, even if the carrier tracking is corrected."},"zinc_tracking_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Zinc Tracking Url","description":"Public tracking URL for the Zinc tracking number (17track.net)."},"status":{"anyOf":[{"$ref":"#/components/schemas/TrackingStatus"},{"type":"null"}],"description":"Carrier-derived shipment state (pending, in_transit, delivered)."},"estimated_delivery_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Estimated Delivery Date","description":"Carrier-reported delivery estimate for this shipment, as a destination-local calendar date. Refreshed on every carrier poll; null when the carrier hasn't reported one."},"checkpoints":{"items":{"$ref":"#/components/schemas/TrackingCheckpointResponse"},"type":"array","title":"Checkpoints","description":"Carrier scan events, most recent first. Empty unless the checkpoint timeline was requested."},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","carrier","tracking_number","created_at"],"title":"TrackingNumberResponse","description":"Response model for tracking number data.\n\n``status`` is the authoritative carrier-derived shipment state and is always\npresent. ``checkpoints`` is the per-scan timeline; it's embedded on the\nsingle-order read (``GET /orders/{id}``) and, on the list endpoint, only\nwhen ``include=tracking_events`` is requested."},"TrackingStatus":{"type":"string","enum":["pending","in_transit","delivered","undeliverable"],"title":"TrackingStatus","description":"Carrier-derived shipment state for a TrackingNumber."},"UsageMetric":{"properties":{"total":{"type":"integer","title":"Total"},"prev_total":{"type":"integer","title":"Prev Total"},"series":{"items":{"type":"integer"},"type":"array","title":"Series"}},"type":"object","required":["total","prev_total","series"],"title":"UsageMetric"},"UserUsageResponse":{"properties":{"window_days":{"type":"integer","title":"Window Days"},"since":{"type":"string","title":"Since"},"metrics":{"additionalProperties":{"$ref":"#/components/schemas/UsageMetric"},"type":"object","title":"Metrics"},"recent":{"items":{"$ref":"#/components/schemas/RecentCall"},"type":"array","title":"Recent"}},"type":"object","required":["window_days","since","metrics","recent"],"title":"UserUsageResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WalletResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"integer","title":"User Id"},"balance":{"type":"integer","title":"Balance"},"wallet_type":{"$ref":"#/components/schemas/WalletType"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"spendable_balance":{"type":"integer","title":"Spendable Balance","description":"Balance available to spend on orders, in cents. Excludes in-flight Connect hold reservations, which transiently inflate `balance`. This is the figure the order balance check compares against.","default":0},"order_fee_cents":{"type":"integer","title":"Order Fee Cents","description":"Zinc's API fee per order, in cents. An order needs `max_price + order_fee_cents` available (plus the notifications add-on when the order opts into customer emails).","default":0},"billed_by_invoice":{"type":"boolean","title":"Billed By Invoice","description":"True for bulk-deal customers, who are invoiced monthly. Their orders skip the wallet balance check entirely, so a low balance does not block them.","default":false}},"type":"object","required":["id","user_id","balance","wallet_type","created_at","updated_at"],"title":"WalletResponse","description":"Response model for wallet data.\n\nBeyond the raw balance this carries the two other numbers a client needs to\nanswer \"can I place this order?\" without reimplementing the gate in\n``POST /orders``: the *spendable* balance that gate actually compares\nagainst, and the per-order API fee it adds on top of ``max_price``. Both are\nserver-derived on purpose — the fee is env-tunable and the two balances\ndiffer for Connect accounts, so a client computing either one locally would\ndrift."},"WalletType":{"type":"string","enum":["api_consumption","purchases"],"title":"WalletType"},"WebhookEndpointResponse":{"properties":{"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"},"webhook_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Secret"}},"type":"object","required":["webhook_url","webhook_secret"],"title":"WebhookEndpointResponse","description":"The caller's webhook endpoint: where events go and the HMAC secret that\nsigns them. Both are null until registered."},"WebhookEndpointUpdate":{"properties":{"url":{"type":"string","title":"Url","description":"Absolute http(s) URL that receives order and return events.","examples":["https://example.com/zinc/webhook"]}},"type":"object","required":["url"],"title":"WebhookEndpointUpdate","description":"Body for ``PUT /webhooks/endpoint``: the single URL Zinc delivers to."}},"securitySchemes":{"BearerAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"Zinc API key (Bearer zn_...)"}}},"servers":[{"url":"https://api.zinc.com","description":"Production"}],"security":[{"BearerAuth":[]}]}