Skip to main content
POST
Create Order
Create a new order for processing. Orders are queued and processed asynchronously.

Request Flow

  1. Submit Order - Send order details including products and shipping address
  2. Validation - We validate product URLs and shipping address
  3. Queued - Order is queued for processing
  4. Processing - Our system places the order with the retailer
  5. Completed - You receive confirmation with tracking details
Order flow diagram

Order processing flow

Product URLs

Provide direct product URLs. Zinc resolves the retailer from the URL itself, and will attempt checkout at most online stores — not only the ones in the retailer catalog. That catalog is the set Zinc verifies with daily test orders, not the limit of what it can buy from: a store missing from it is not unsupported. Each product must include:
  • url - Direct link to the product page
Make sure product URLs are accessible and lead directly to the product page, not search results or category pages.
Holding a URL you are unsure about? GET /retailers/check answers whether Zinc will order from that store and ship it to a given country, before you submit. It applies the same gates as this endpoint, so the two cannot disagree.
An order can contain at most 10 products. More than that is rejected with 422 / validation_error — Order cannot contain more than 10 items. The limit is on entries in the products array, not units: one entry with quantity: 50 still counts as one. See The Products Array.
Optionally, the product can include:
  • quantity - Number of items to order (integer, default 1)
  • variant - A list of label, value pairs indicating a variant of a product. For example, if you’re ordering a shirt. The shirt may come in different colors and different sizes. To indicate a red medium shirt, you would do:
    Make sure the strings used for both the label and value match up to what is present on the retailer website. For example, if a medium is indicated by the value M, use M for the value.
  • condition_in / condition_not_in - Condition allow/deny lists. Limit which offers are eligible by item condition — useful for buying only new items, or for accepting used items down to a floor. See Condition Filtering.

Products Zinc cannot buy

Some items can’t be purchased through Zinc no matter which account places the order. They fail with product_unavailable at checkout rather than being rejected when you submit, so filter them out of your catalog up front:
  • Digital goods — e-books, downloads, digital video
  • Gift cards, including e-gift cards
  • Customized or personalized items — anything configured on the product page
  • Retailer-assembled bundles
  • Groceries — Amazon Fresh and Whole Foods
  • Pharmacy items
  • Services — installation, at-home service, and similar add-ons
  • Magazine subscriptions
  • Restricted items — anything the retailer prohibits from automated purchase
An offer can also be unbuyable for the account placing the order: business-only offers can’t be bought on a consumer account, and some consumer-only items can’t be bought on a business account. Both surface as product_unavailable. See Retailer Accounts if you’re placing orders on your own account.

Shipping Address

All orders require a valid shipping address. Addresses are validated using Google’s Address Validation API. Required fields:
  • first_name and last_name
  • address_line1 (use address_line2 for apartment/suite, optional)
  • city
  • postal_code — for a US address this must be a ZIP: five digits, optionally +4 (98632 or 27517-8761)
  • phone_number
Optional fields:
  • state — omit for countries that don’t use states/provinces
  • country — ISO 3166-1 alpha-2 code (e.g. US, CA, GB, DE); defaults to US
International shipping is supported. Provide the destination country as an ISO 3166-1 alpha-2 code; state is optional where it doesn’t apply.

Which account places the order

By default Zinc places the order on its own accounts and you pay for the item out of your wallet. To have it placed on your own retailer account instead, pass that account’s short_id as retailer_credentials_id — orders on your own account are charged the API fee only. Zinc never selects one of your saved accounts for you, so send the field on every order that needs it. See Retailer Accounts.

Payment

By default, orders draw from your prepaid wallet — no payment object needed. To charge your end-customer’s card instead and keep a margin, send a payment object with mode: "connect". See the Stripe Connect guide for the full flow. In connect mode, the authorization hold is sized from max_price, and the card is charged the actual total when Zinc places the order with the retailer.

Connect mode

Fulfillment preferences

An order is strict by default: if any rule can’t be met — an item can’t be bought, the retailer caps the quantity, the gift option isn’t offered — the whole order fails and nothing ships. The optional fulfillment object relaxes that one concern at a time. Each key takes the mode to hold that rule at, and the only mode today is "best_effort". Anything you leave out stays strict, so an omitted fulfillment block and an empty one mean exactly the same strict order.
max_price is never relaxed. No fulfillment setting will let an order exceed the ceiling you set.
Two things hold no matter what you relax:
  • An empty cart still fails. If every item ends up skipped, the order fails with cart_empty rather than placing an order for nothing.
  • Unknown keys and unknown modes are rejected, not ignored. A typo like "itmes" or "best-effort" returns a 422, so a mistake can never quietly leave a rule strict when you meant to relax it.
Whatever was actually relaxed is reported back in fulfillment.concessions on the order, so a placed order always tells you what it did — see Fulfillment.
fulfillment is also accepted on POST /agent/orders, which shares the same order-creation schema.

Excluding merchants

fulfillment.excluded_merchants goes the other way: it makes an order stricter. List up to 20 merchants the order must never be bought from. Each entry is either a seller’s display name as shown on the retailer’s storefront, or an Amazon Business merchant id (amzn1.…). Names are matched ignoring case and spacing.
The list is added to any blocklist already on your account. It can bar more merchants but can never allow one your account already bars. The order echoes it back as fulfillment.excluded_merchants.
excluded_merchants currently applies to Amazon Business orders only and is ignored for other retailers.

Optional Order Data

You can include additional data with your order for tracking and reference purposes. This data will be used by our system as input to any fields during checkout that match.
  • po_number - Your internal purchase order number for tracking and reconciliation
  • is_gift - Mark the order as a gift (boolean, default false), suppressing prices on the packing slip. See Gift orders for the failure behavior when a retailer offers no gift option.
  • gift_message - Optional note for the recipient, entered into the retailer’s gift-message field at checkout (string, max 240 characters). Requires is_gift to be true.
  • handling_days_max - Optional ceiling on a seller’s handling days (integer, minimum 1). Offers from sellers whose handling time exceeds this are skipped. Omit or send null for no limit.
If you only need data for internal reference, use the metadata field instead.

Gift orders

Setting is_gift: true suppresses prices on the packing slip. Because a gift that arrives with prices visible to the recipient is worse than no order at all, this is treated as a hard requirement rather than a preference:
If the retailer’s checkout offers no free gift option, the order fails with gift_option_unavailable instead of being placed as a normal order. Handle this error type if you offer gifting as an optional add-on — see Order Processing Errors.
If gifting is a nice-to-have rather than the point of the order, you can opt out of that failure instead of catching it: send fulfillment: { "gift": "best_effort" } and a retailer with no gift option gets a normal order rather than none. See Fulfillment preferences. gift_message is best-effort by contrast: it’s delivered where the retailer’s checkout offers a gift-message field, and the order is still placed without it where one isn’t available.
is_gift and gift_message are also accepted on POST /agent/orders, which shares the same order-creation schema.

Response

A successful order creation returns:
  • id - Unique order identifier (UUID)
  • status - Current order status (initially “pending”)
  • items - Array of order items with their details
  • shipping_address - Confirmed shipping address
  • created_at - Timestamp of order creation
Use the order id to retrieve order status and updates.

Authorizations

Authorization
string
header
required

Zinc API key (Bearer zn_...)

Headers

authorization
string | null

Body

application/json

Request body for POST /orders.

products
OrderProduct · object[]
required
max_price
integer
required

Maximum price (in cents) allowed for an order before it is finalized.

Required range: x >= 0
shipping_address
Address · object | null

Where to ship. Required for wallet and Connect orders.

idempotency_key
string | null

Optional idempotency key to prevent duplicate orders. If not provided, one will be generated.

Maximum string length: 36
retailer_credentials_id
string | null

Optional short ID (e.g. 'zn_acct_XXXXXXXX') of one of your own saved retailer accounts to place this order on. The item is then paid for by the payment method saved on that account, and your wallet is charged the API fee only — so the balance needed to place the order is the fee, not max_price + fee. Omit it and the order is placed on a Zinc account and funded from your wallet; Zinc never substitutes one of your saved accounts, so send this field on every order that has to run on yours.

metadata
Metadata · object | null

Optional metadata to attach to the order. Can contain arbitrary key-value pairs.

po_number
string | null

Optional purchase order number for the order.

handling_days_max
integer | null

Optional ceiling on a seller's shipping and handling days. Omit or send null for no limit.

Required range: x >= 1
is_gift
boolean
default:false

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.

gift_message
string | null

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.

Maximum string length: 240
payment
OrderPayment · object | null

Optional payment block. Omit for prepaid-wallet billing (default).

customer_notifications
CustomerNotifications · object | null

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
FulfillmentPreferences · object | null

Loosen the order's strict-by-default rules. Omit for today's behaviour: any rule that can't be met fails the order. Set a rule (gift, items, quantity) to best_effort to have the order placed anyway; anything left unset stays strict. Whatever was relaxed is reported back in fulfillment.concessions on the order. max_price is never relaxed. excluded_merchants tightens instead: merchants this order must not be bought from.

Response

Successful Response

Response model for order data.

id
string<uuid>
required
status
enum<string>
required
Available options:
pending,
in_progress,
order_placed,
order_failed,
cancelled,
cancelled_by_retailer
max_price
integer
required
attempts
integer
required
items
OrderItemResponse · object[]
required
shipping_address
Shipping Address · object
required
retailer_credentials_id
string | null
required
created_at
string<date-time>
required
updated_at
string<date-time>
required
metadata
Metadata · object
po_number
string | null
handling_days_max
integer | null
is_gift
boolean
default:false
gift_message
string | null
retailer_credentials_uuid
string | null
job_result
OrderJobResult · object | null

Fulfillment result and price breakdown for a completed or failed order; null while processing.

merchant_order_ids
string[]

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.

tracking_numbers
TrackingNumberResponse · object[]
created_by
string | null
user_id
integer | null
returns
ReturnRequestSummary · object[]
connect
OrderConnectInfo · object | null

Stripe Connect charge details when this order was paid via Connect; null for prepaid-wallet orders.

payment
OrderPaymentInfo · object | null

Card hold details when this order was paid by card outside the wallet and Connect; null for wallet and Connect orders.

customer_notifications
CustomerNotificationStatus · object | null

End-customer email-notification status when the order opted into the notifications add-on; null when it didn't.

fulfillment
OrderFulfillment · object

gift/items/quantity: the mode this order asked for on each rule (all null = strict). concessions: every rule that actually was relaxed, with the worker's code and the item it concerned. Empty concessions means the order was fulfilled exactly as requested.