---
title: Sell what you don't stock
description: Build a storefront with no inventory and no merchant deals — how the money works, and what Agnic will and will not do for you.
---

# Sell what you don't stock

You can build a store today that sells things you have never touched, from merchants you have never spoken to.

Your customer buys from you. You buy from the merchant, with your own card, through Agnic. The parcel goes to your customer's door. You never hold stock, never negotiate a wholesale deal, and never ask the merchant to integrate with anything.

This page is about the part that is easy to get wrong: the money.

---

## Who is the merchant of record

You are.

Your customer's contract is with you. You collect from them on your own Stripe account, you set the price, you keep the margin, and you handle the refund if it goes wrong. Agnic is not in that transaction and never sees it.

On the other side, Agnic completes a checkout at the real merchant using **your business card**, which you vault once. The merchant's contract is with you too — as far as they are concerned, a customer bought something and asked for it to be delivered elsewhere. That happens all day.

Agnic never holds your money. There is no balance, no float, no top-up, and there will not be one. That is a deliberate line: the moment a platform holds funds on your behalf it is a money transmitter, and that is a different company. What Agnic sells is the checkout, not the custody.

---

## The order of operations that keeps you solvent

The sequence matters, and the naive version loses money.

**Collect from your customer first.** Not after. If you place the merchant order first and the customer's card then declines, you own an item you did not want, at a price you did not choose.

**Authorize, then capture when the order is placed.** Take an authorization when your customer checks out, place the Agnic order, and capture only once you have a `succeeded` back. If the order fails, void the authorization and your customer is never charged. Stripe holds an authorization for seven days, which is far longer than any order here takes.

**Refund from the failure, not from a support ticket.** Every terminal order carries `retryable` and `retry_action`. When `retryable` is `false` and the action is `none`, the order is dead and the money should go back automatically. When `retryable` is `null`, stop: money may have moved, and that is the one case a person should look at before you refund or retry.

**The tax is yours.** You are selling to your customer; whatever you owe on that sale is between you and your tax authority. The tax the merchant charges you is a cost of goods, and it is included in the amount Agnic is authorized to spend.

**The markup is yours.** Agnic charges you a flat fee per successful checkout, never a percentage of the sale. What you charge on top is your business.

---

## Setting up, once

Two things, both hosted, both one-time.

**Vault your business card.** Send the user to `https://app.agnic.ai/partner/cards/new`. The card details go straight to the vault; your servers never see a card number and neither does Agnic's application code. What comes back is an alias.

**Sign a delegation.** `https://app.agnic.ai/partner/delegations/new`. This is a passkey signature setting the most that may ever be spent, per transaction and per day. Set it high enough for your real volume — a delegation that is too tight turns every busy afternoon into a step-up approval, and a step-up needs a human with the passkey.

Then mint an API key and you are ready. Everything after that is the [checkout tools](/docs/agentic-commerce/checkout-api).

---

## Delivering to your customer

This is the feature the whole model rests on, and it is one field.

```json
{
  "ship_to": {
    "name": "Dana Okonkwo",
    "street_address": "Unit 18, Dickens Yard",
    "address_locality": "Ealing",
    "postal_code": "W5 2TG",
    "address_country": "GB"
  }
}
```

Pass it to `preview_order`, and pass exactly the same object to `place_order` — through the tools it is part of what the confirmation token is bound to, so a difference means nothing is charged. The shipping address on the merchant's checkout becomes your customer's; the card and its billing address stay yours.

The merchant's confirmation and dispatch emails come to **you**, not to your customer. That is what you want: you are the one who has to answer "where is my order?", and you cannot answer it from an inbox you do not control.

Shipping is priced in your customer's market, not yours. A Canadian card buying from a British shop for a British recipient gets British shipping options and a British total.

Ship-to works on Shopify-rail merchants. Elsewhere you get `ship_to_unsupported` before anything is charged, and `find_merchants` tells you the rail before you commit to a merchant.

---

## Not spending more than you meant to

Your customer paid you a fixed price. The merchant's shipping is not fixed until the cart is priced, and that gap is where an intermediary loses money quietly.

```json
{ "constraints": { "max_total_minor": 6000, "max_shipping_minor": 1200 } }
```

Checked at preview, and checked again against a freshly built cart before the card is used. If shipping comes back at more than you allowed, you get both figures back and nothing is charged.

Through the MCP tools the caps are also bound into the confirmation token, so a model cannot quietly raise them between quoting and placing. Over HTTP there is no token — the second check still happens and a breach is still refused pre-charge, but the caps are yours to send correctly both times.

Set `max_total_minor` to what you actually collected, minus the margin you need. It is the difference between finding out now and finding out in a reconciliation.

---

## Testing

Testing is free. You test against **`untitled-fidget.shop`**, a real Shopify shop running in Shopify Payments test mode, with two items at **1.00 CAD** — SKUs `gid://shopify/ProductVariant/43945235349570` and `gid://shopify/ProductVariant/43945255567426`, merchant `merchant_untitled_fidget_shop`. It ships to Canada and the US, not the UK. Real cart, real shipping rates, real checkout, real card rail — only the settlement is simulated, so no money moves and no parcel ships.

Use a test card (`4242 4242 4242 4242`), vaulted once at `https://app.agnic.ai/partner/cards/new`.

A Shopify **development** store would not work, and it is worth knowing why: its checkout only accepts the literal card number `1`, and Agnic never types a card number — it types a vaulted alias that the vault swaps for a real card mid-flight. You cannot vault `1`. An ordinary store with its gateway in test mode takes the normal test numbers through the normal fields.

One thing to plan around: these orders still carry `test: false`. That field marks a designated test *merchant*, which this shop is not. Do not branch your own code on it to decide whether an order was practice.

Three interruptions to expect — the security code expiring roughly hourly, a mandate in the wrong currency being refused outright, and a step-up loop that never terminates if you poll by re-dispatching. All three are explained in [Testing](/docs/agentic-commerce/testing).

Most failure paths cost nothing at all, because they happen before the card. [Testing](/docs/agentic-commerce/testing) lists the ones worth triggering on purpose.

---

## A worked example: the wishlist nobody can trace

Take a real business and follow it all the way down, because the interesting problems are not where you expect.

**The idea.** Creators and public figures get sent things by their audience. They also, reasonably, refuse to publish a home address. So you build a wishlist: the creator curates products, a fan picks one and pays, the parcel arrives. The creator's address is never shown to anyone.

Two hard problems sit inside that sentence, and only one of them is obvious.

---

### Problem one: you have no inventory, so what is on the list?

The obvious answers all fail, and it is worth seeing exactly where.

**Curate by hand.** The creator pastes links. This works on day one and rots immediately: prices move, variants sell out, and a wishlist showing a £20 item that now costs £26 turns every purchase into a support ticket. You have no way to know, because a link is not a feed.

**Scrape the retailers.** Now you own a scraper per retailer, and you still cannot buy — you can only display. Every price is a guess with a timestamp.

**Use affiliate feeds.** This is the one that looks right and is the most wrong. Affiliate networks solve listing beautifully: a real catalogue, real prices, no scraping. Then the fan clicks through to the retailer and *the retailer's checkout asks the fan for a shipping address*.

Stop there, because that is the whole business failing. Either the creator's address is on the wishlist for every fan to type in — which is the thing you exist to prevent — or the fan ships to themselves, which is not a gift. **Affiliate solves the catalogue and destroys the product.** The listing problem and the privacy problem look separate and are the same problem.

**What Agnic does about the catalogue.** Two calls, no merchant relationships.

`GET /api/autofill/products/search?q=…&country=…` searches a vetted pool of Shopify shops and returns rows carrying a `sku` you can quote immediately. `GET /api/autofill/products/lookup?url=…` turns a link the creator pasted into the same thing — and if it is a shop nobody has bought from yet, the response says so and `POST /api/autofill/explore` onboards it in seconds.

So the creator can curate freely, and everything they curate is buyable rather than merely displayable.

---

### The step almost everyone skips

Before an item goes on the list, **quote it to the creator's address**.

```json
{
  "merchant_id": "…",
  "items": [{ "sku": "…", "quantity": 1 }],
  "ship_to": { "…": "the creator's address" }
}
```

Not to check the price. To check the shop will deliver there at all.

A shop that cannot reach the creator's country returns `unfulfillable`, with no delivery options. If you discover that when a fan tries to pay, you have taken their money for something undeliverable and you are refunding a stranger who wanted to do something nice. Discovering it at listing time costs one API call and the item silently never appears.

This is the single highest-value line of code in the product and it is invisible in the finished thing.

---

### Problem two: the address, and who is allowed to see it

The creator's address lives in your database. Once. It is never on the wishlist, never in the fan's confirmation, never in the retailer's customer record as anything but a delivery line.

At the moment of purchase you pass it as `ship_to` on the quote and the dispatch. Three properties make this hold up, and each is deliberate.

**It is order data, not a profile edit.** Passing `ship_to` does not write an address anywhere. There is no accumulating record, and the next order goes to the cardholder unless you say otherwise.

**Every read path gives it back redacted.** Fetch the order and you get name, town, region, postcode, country — never the street line. Your own support tooling, your own logs and your own staff see a town, not a door. The full address exists on the dispute-evidence bundle and nowhere else. You do not have to build that discipline; you have to not work around it.

**The merchant's email goes to you, not the creator.** The cardholder is your business, so order confirmations, dispatch notices and marketing all land in your inbox. The creator does not end up on fifty retailer mailing lists because someone sent them a candle. Relay what matters and drop the rest.

The fan, meanwhile, sees what a fan should: a product, a price, a thank-you. They never see an address field, because they never touch a retailer's checkout.

---

### The money, in order

The fan is your customer. The retailer is yours.

1. Fan pays **you**, on your own Stripe. Authorize, do not capture.
2. You quote the real order with `ship_to` and a `constraints.max_total_minor` set to what you actually collected, minus your margin.
3. You dispatch. If the price moved past the cap, it is refused **before the card** and you void the authorization. The fan is never charged for a failure.
4. On `succeeded`, capture.

That order matters. Buy first and a declined fan card leaves you holding a candle you did not want at a price you did not choose.

---

### What will actually go wrong

**The price moved between listing and purchase.** Inevitable, and it is why the cap exists. A breach is a refusal carrying both figures, so you can tell the fan the item is now £26 and ask, rather than silently overspending or silently failing.

**It sold out.** The order comes back with `retryable: false` and `retry_action: "re_preview"`. Refund automatically; do not retry.

**Two fans buy the same item.** Two orders, two carts, two mandate checks. There is no batching and there should not be — one refusal must not take the other order down with it.

**Something is genuinely unknown.** `retryable: null` means money may have moved. Do not refund automatically and do not retry. That is the one state a person should look at.

---

### Three things not to do

Do not put the creator's address in a gift message, a note to the merchant, or anything the fan can read back.

Do not show the fan the order's `ship_to`, even redacted. They do not need the town.

Do not build your own address store keyed by fan. The address belongs to the creator, is passed per order, and is never a field on anything the fan owns.

---

## What this does not do yet

No webhooks. Poll `get_order_status`; it is the documented path.

No returns or cancellations through the API. Once an order is placed with the merchant, a return is between you and them, like any other purchase.

Ship-to on Shopify-rail merchants only.

The recipient's own email and phone are not passed to the merchant. Contact details stay the cardholder's, which is to say yours.

None of these are oversights. They are the things that were cut so that the part above works properly.
