Agnic
Agentic Commerce

Testing

Test against a real shop running in test mode. Real cart, real checkout, real card rail, no money.

Testing

Testing is free. You place orders against a real Shopify shop that is running in test mode, using a test card, and no money moves.

Everything about the run is real except the settlement: a live cart, real shipping rates for your destination, the shop's actual hosted checkout, the real card rail with the vault swapping your alias mid-flight. The gateway is the only part that is simulated.


Why a development store would not work

Worth knowing, because it is the first thing everybody tries.

A Shopify development store can only take test payments, which sounds ideal. Two independent things make it useless.

Its Test payment gateway only accepts the literal card number 1. Agnic never types a card number — it types a vaulted alias shaped like a card, and the vault swaps in the real number mid-flight. You cannot vault 1, because it is not a card and will not survive the browser's own validation. So the one gateway that costs nothing is the one gateway this rail cannot use.

And a development store cannot drop its storefront password. Every path stops at the password page, including the cart link, and the checkout engine correctly classifies that as unreachable. Lifting the password means putting the store on a paid plan, at which point it is no longer a development store.

The way out is not a special store. It is an ordinary store with Shopify Payments switched to test mode, which accepts the normal test card numbers through the normal card fields.


The sandbox

untitled-fidget.shop — a real Shopify shop, already onboarded, running in Shopify Payments test mode.

Its merchant id is merchant_untitled_fidget_shop, on the Shopify rail, and it has two items priced at 1.00 CAD:

Itemsku
Hex Token Fidgetgid://shopify/ProductVariant/43945235349570
Paw Print Charmgid://shopify/ProductVariant/43945255567426

It ships to Canada and the US, and prices in the destination's currency. It does not ship to the UK: a UK ship_to answers unfulfillable with no delivery options. That is the shop's shipping settings, not your code — it is exactly the answer your product will get from any real shop that cannot reach an address, so handle it here.

Finding a SKU yourself

The sandbox shop is not in the products/search pool, and neither is any shop that has not been onboarded. Two ways to get SKUs for any Shopify merchant:

Quote with a product name. Put the name where the SKU goes. The quote fails with 422 and code: "unknown_sku", and its suggestions list the real SKUs, ranked, with prices.

curl -s -X POST https://api.agnic.ai/api/autofill/shopify/quote \
  -H "X-Agnic-Token: $TOKEN" -H "Content-Type: application/json" \
  -d '{ "merchant_id": "merchant_untitled_fidget_shop", "items": [{ "sku": "hex token fidget", "quantity": 1 }] }' \
  | jq '.suggestions'

Explore the shop. POST /api/autofill/explore with the shop URL returns status: "exploring" and an order_id. Poll GET /api/autofill/orders/{order_id} for a few seconds until status is explored; the shop's SKUs are in discovered_items.catalog. The explore response itself never carries them.

GET /api/autofill/merchants/{id} returns catalog: null for a Shopify-rail merchant, and that is correct: its products come from Shopify at quote time, not from that field.

Confirm test mode for yourself from the checkout: the installments block reads "Installments can't be used in test mode." That is Shopify saying it, not us.

Buy the one-dollar items. Nothing stops you buying the expensive ones, but a fifty-dollar simulated order teaches you nothing a one-dollar one does not.


Cards

Use a test card. Vault it once at https://app.agnic.ai/partner/cards/new — the number goes straight to the vault, and neither your servers nor Agnic's application code ever see it.

Visa4242 4242 4242 4242
Mastercard5555 5555 5555 4444
Expiryany future date
Security codeany three digits
Name on cardat least two words

These are Luhn-valid sixteen-digit numbers, so they vault and reveal exactly like a real card. Nothing about the rail is bypassed.

Do not vault a real card for this. There is no reason to, and test mode would decline it anyway.


Three things that will interrupt you

None of these are failures. All three cost people an afternoon before they were written down.

The security code expires roughly hourly

The vault holds a card's security code for about fifty minutes and never longer — it is prohibited from keeping it. After that, your next dispatch returns 202 with cvv_refresh_required.

Open the approval_url, re-enter any three digits, then dispatch the same saved request again with the approval_token. Nothing was charged and nothing is broken.

Your mandate must be in the store's currency

The policy engine will not convert between currencies, deliberately: converting at an unseen rate hides spending behind a stale number.

A mandate denominated in GBP against this shop's CAD order returns 202 with currency_mismatch on every dispatch, forever, until you issue one in CAD. The mandate form has a currency selector and it defaults to your profile's country, which is how most people end up with the wrong one.

Do not poll by re-dispatching

A cvv_refresh_required step-up mints a brand new token on every dispatch. It does not report the pending one as not-ready.

So a retry loop that waits by calling dispatch again will never terminate. It spins, and it fills the approvals table with dead tokens. Poll GET /api/approvals/{token} until its status is approved, then dispatch once.

The mandate step-up behaves differently — it does answer approval_not_ready. Write your loop for the harsher of the two.


What these orders are, and are not

No money moves. No parcel is shipped. The shop's own order records mark them as test orders.

They still carry test: false in the Agnic API. That field is about the merchant being a designated test merchant, which this one is not — it is an ordinary shop that happens to have its gateway in test mode. So do not branch on test to decide whether an order was practice. It will be false here.

There is no TEST STORE prefix on the preview summary here, for the same reason.

Because settlement is simulated, this is the one thing the sandbox does not prove: that a real card would have been accepted by a real gateway. Everything before that point is identical.


Shipping somewhere other than the cardholder

Pass ship_to and the order goes to that address instead of the cardholder's, with the billing block filled from the card's own address.

Your own billing address must be on your account first. The billing block on the checkout is filled from your profile, not from the request — there is no field for it in the dispatch body. If the profile is missing a name, street, city, postal code or country, that block is left blank, the shop refuses the form, and you get a 409 billing_profile_incomplete naming the fields. Set them at app.agnic.ai/profile or with PATCH /api/profile. It is the cardholder's address, not the recipient's.

Two things to know. The engine proves the destination is on the hosted checkout before it touches the card, and refuses outright if it cannot — a parcel to the wrong address is not something a retry fixes. And this path was materially broken on the Shopify rail until 13 September 2026, so if you tried it before then and gave up, try again.


Deliberately breaking things

The failure paths are where an agent product is actually judged, and most of them happen before the card is ever touched.

Set max_shipping_minor to 1 and watch the constraint blocker arrive at preview, with no token issued and no browser launched.

Change one character of a ship_to postcode between preview and place, and watch TOKEN_BODY_MISMATCH come back before anything is dispatched.

Order a variant that is out of stock and read what retryable: false looks like next to re_preview.

Ask for ship_to at a merchant that is not on the Shopify rail and read the refusal.

Declines are simulatable too, now that the gateway is in test mode — Shopify publishes the card numbers that force a decline or a gateway error. Use those rather than inventing a failure your code will never actually see.

Doing each of these once, on purpose, is worth more than reading about them.