September 11, 2026 — the $2,000 plan is renamed Scale, and publishing no longer requires a plan
The $2,000-a-month plan is now called Scale. Separately, a storefront no longer needs a plan to go live: publishing has been open to every store since September 1, 2026.Changed
- Enterprise is now Scale. Same plan, same $2,000 a month per store, same card rate (3.4% + 30¢ on domestic cards). Only the name changed. Earlier entries in this changelog that mention the Enterprise plan mean this plan.
- Publishing no longer requires a plan (since September 1, 2026). This reverses Build free, pay to publish from v1.13. Once an account’s email address is confirmed, its store’s default theme goes live on its PlatformDTC address, and every later publish goes through without a plan check.
- Prices and billing. Basic $20, Pro $200 and Scale $2,000 a month per store, billed monthly. The $1 first month still applies once per account, to the first plan you start. There is still no free plan and no free trial.
- Subscriptions on the $2,000 plan. They are not moved, repriced or re-billed. The plan’s
identifier is still
enterprise; only its displayed name changed. - The one-time $100,000 AI credit is still granted when a new account confirms its email.
- Storefronts that are already live stay live.
Orders is one screen — Disputes and Drafts are tabs of the order list, and every tab shows its count the same way
Disputes and Drafts are no longer separate screens. They are tabs in the same row as All Orders, Open, Fulfilled, Cancelled, Alert and Warning, with the same toolbar and table.The row is grouped: order status (All Orders, Open, Fulfilled, Cancelled) | fraud signals (Alert,
Warning) | Drafts and Disputes.Changed
- Disputes is a filter of the order list. It shows the orders a cardholder has disputed, in the same table as every other tab. Each row carries a dispute badge — for example Chargeback · Under review — and hovering it shows the reason and when evidence is due. Open a row for the full dispute.
- Respond to disputes automatically now sits in the toolbar while the Disputes tab is open.
- Drafts use the order list’s toolbar and table. Search drafts by number (with or without the
#) or by customer email, filter them by Open, Invoice sent or Completed under Filters, and create a draft from the toolbar. - Every tab shows its count in the same style, including Alert, Warning, Disputes and Drafts.
- Exporting from the Alert, Warning or Disputes tab now exports exactly the orders on that tab. It used to export every order.
Every action on the order page now works — returns, printing, duplicate, notes, tags and messaging
The order page’s actions are all live. Controls that looked clickable but did nothing now either do the job or are gone.New
- Return on fulfilled orders — pick the items, quantity, reason and restock option; quantities are capped at what is not already on a return. Manage the return from Returns.
- Print → Order invoice and Print → Packing slip, printed straight from the browser.
- Duplicate creates a draft order with the same items, customer and ship-to.
- Notes and Tags are editable from the order page.
- Message customer opens a support email to the buyer, addressed and titled with the order number.
- An item’s name now opens that product in your catalog.
- The customer’s name opens their customer profile.
- Refund at the top of the page opens the refund form, and appears only while money is left to refund.
- Cancel order asks you to confirm, with an optional reason, instead of cancelling on the first click.
- The status beside the order number is the real payment status. A refunded order no longer reads “Unpaid”.
- The order page and its printed documents show your store’s order number (for example #603), the same number as the orders list and return names, instead of a 36-character id.
- The billing address is shown only when checkout recorded one. “Same as shipping address” appears only when the two match.
- Edit and Mark as delivered, which had nothing behind them.
Dispute, payout and account fees at cost — itemised in Finance and deducted from your balance
PlatformDTC pays the payment processor’s dispute, payout and account fees for your store. From September 11, 2026 they are recovered at cost, itemised in your dashboard, and deducted from your PlatformDTC Payments balance.These fees were always charged — to PlatformDTC, on your store’s behalf — but they never appeared
anywhere you could see them. They now have their own statement, every line at the processor’s own
price with no margin, and they are taken from the balance your sales build rather than added to an
invoice a month later.Changed
- Dispute fees, at cost. $15 for every dispute received, which the card network charges whatever the outcome, and a further $15 when a dispute is contested — credited back to you if you win. This replaces the $20 chargeback fee.
- Payout and account fees, at cost. 0.25% + 25¢ of each payout sent to your bank, and $2 for each month in which your account receives a payout.
- Collected from your balance, not your invoice. These fees, and any international or currency-conversion cost that could not be taken inside a payment (the v1.16 shortfall), are deducted from your PlatformDTC Payments balance. A deduction never takes your balance below zero; anything it cannot cover yet is retried when funds arrive.
- Platform fees on Finance → Payouts. An itemised statement of every amount — what it was for, the period, the amount, and whether it is owed, being collected, collected or credited — and every deduction made from your balance.
- Costs PlatformDTC paid for your store before these terms took effect are recoverable at cost: international and currency-conversion costs not taken in a payment’s fee before 09:00 UTC on September 11, 2026, payments charged below your current plan’s rate, and dispute, payout and account fees. Each is itemised on Finance → Payouts, and nothing for a past period is deducted until at least 14 days after we email you notice.
- Your card rate — 3.8% + 30¢ on Basic, 3.5% + 30¢ on Pro, 3.4% + 30¢ on Enterprise — and the at-cost international (+1.5%) and currency-conversion (+1%) pass-through from v1.16.
- No margin on any pass-through, and still no separate transaction fee.
- A rate card agreed in writing for your store still applies.
International and currency-conversion costs are now taken in each payment's fee
From 09:00 UTC on September 11, 2026, the card costs that depend on where a card was issued are priced into the payment itself.Your plan’s card rate has always been the rate for a domestic card. A card issued in another country,
or an order charged in a currency other than your account’s, costs more to process — and until now
that difference was not visible on the payment it belonged to. It is now taken in that payment’s
transaction fee, at the moment of the charge, at cost.Changed
- International cards: +1.5%, at cost. When a card was issued outside your payments account’s country, 1.5% of the payment is added to that payment’s fee. Cards issued in Puerto Rico and other US territories count as US cards.
- Currency conversion: +1%, at cost. When an order is charged in a currency other than your account’s default currency, 1% of the payment is added to its fee.
- Renewals and post-purchase offers are priced off the saved card, the same way.
- Klarna and Afterpay carry their own processing rate. 5.99% + 30¢ for Klarna and 6% + 30¢ for Afterpay, plus your plan’s commission — 6.89% / 6.59% / 6.49% + 30¢ for Klarna and 6.9% / 6.6% / 6.5% + 30¢ for Afterpay on Basic / Pro / Enterprise.
- What could not be priced at the charge goes on your next invoice. If a card’s issuing country was not available when the payment was made, the amount not collected appears as an itemised line on your next PlatformDTC invoice, with the number of payments, the volume and the rate.
- Your domestic card rate. 3.8% + 30¢ on Basic, 3.5% + 30¢ on Pro, 3.4% + 30¢ on Enterprise.
- No margin on either pass-through, and still no separate transaction fee.
- Nothing charged before 09:00 UTC on September 11, 2026 is repriced or billed.
- A rate card agreed in writing for your store still applies.
Support shows the photos your customers send, and lets you send them back
Attachments arrived in the support inbox — in both directions.Customers have always been able to attach things to the emails they send you. Until today the inbox
threw all of it away: the text of the message came through, and the photo of the crushed parcel, the
screenshot of the order confirmation and the PDF invoice did not. A thread would say “photo
attached” and there would be no photo, so answering it meant leaving the dashboard and opening the
mailbox in another client. Replies had the same hole in the other direction — there was no way to
send a shipping label or a replacement receipt from the reply box.New
- Files a customer sends now appear in the thread. Pictures are shown at full width, and every file — image, PDF, spreadsheet, anything — is listed under the message with its name and size, one click to download. Click a picture to open it full-screen.
- Attach files to a reply. A paperclip under the reply box, or drag files onto it, or paste a screenshot straight in. Images you attach are embedded in the email your customer receives, so they see the picture in the message rather than having to open an attachment.
- Attach files when you write first, too. The same on New email.
- A reply can be nothing but a file. “Here is your label” with the label attached and no words is a real answer, and Send now allows it.
- Up to 20 files per message, 25 MB each — the same ceiling email itself has.
- Nothing about how mail reaches you or leaves you. Same mailbox, same address, same threading.
- Your customers’ files stay private. They are not on a public link. Only someone signed in to your store can open them, and every attachment is checked against its own fingerprint before it is handed over.
Your dashboard now says when your store has no way to pay you
A store with no payment account is told so, on every page, until it has one.A store is created without a payment account, and until today nothing in the product said that out
loud. The storefront looked finished, orders could arrive, and the one setup step between having a
store and being paid for it was a settings page nobody had a reason to open. Payments activation is
now a first-class notice: it appears in the notification bell, and — while your store still has no
way to be paid — as a banner across the dashboard.Changed
- A payments notice on every dashboard page. It carries the reason it is there and a single button to Settings → PlatformDTC Payments. Once payments are set up it disappears on its own; there is nothing to acknowledge.
- What it says depends on your store. If orders are already arriving and there is no payment account, the notice says how many, and it cannot be dismissed — money is being taken with nowhere to send it, and that is not something to hide behind a close button. If you have not sold yet, it is the ordinary setup nudge and closes for the day. If you run your own card processor, it is an offer rather than a warning, and it stays in the bell rather than taking a banner.
- One thing at a time on a new account. While your confirmation email is still outstanding — the thing holding your storefront offline — the payments nudge waits its turn rather than stacking a second banner on the first.
- Dismissing it anywhere dismisses it everywhere. The banner and the bell are the same notice, so closing one no longer leaves the other showing a badge for the same sentence.
- Payment notices no longer name our processor. Every message about the PlatformDTC Payments rail now calls it PlatformDTC Payments, matching the order timeline and the payments settings page. Your own connected Stripe account is still called Stripe Card, because that is what it is.
- Nothing about how you set payments up. Same page, same verification, same account.
- Stores that already have payments set up see nothing new. The notice only exists while there is no payment account, and it is per store, not per account.
Public beta — sign-up is open to anyone
Anyone can now create an account and build a real store.Until today the way in was a conversation. Sign-up is open: you register, and at the end of it there
is a real store on a subdomain with a default theme already seeded and ready to edit — no sales
call, no invite code, no waitlist, nothing to wait for. Building it costs nothing; the storefront
starts serving shoppers when you start a plan. Nothing about existing stores changes; this is a
door being added, not a door being moved.Public beta is a statement about pace, not about quality. These are not sandboxes — a published
store is a real store taking real payments. What it means is that behaviour we think is wrong gets
fixed rather than preserved for compatibility, and that when a change is the kind a merchant would
notice, it lands here first.Changed
- Sign-up is self-serve. Register at platformdtc.com/register and the store exists when you finish, seeded and ready to edit.
- Build free, pay to publish. The theme editor, catalog, checkout settings and the rest of the dashboard are open on the real product from the moment you register — not a demo of it. Serving shoppers is what a plan buys: until one starts, the storefront is not published.
- Plans are priced per store, and the first month is $1. Basic $20/mo, Pro $200/mo, Enterprise $2,000/mo; each plan covers one store, so a second store is a second plan. The $1 first month applies once per account, to the first plan you start. There is no Free plan. Card processing is billed separately at 3.8% + 30¢ on Basic, 3.5% + 30¢ on Pro and 3.4% + 30¢ on Enterprise.
- New accounts get a one-time grant of $100,000 in AI credits. The agent surface is the part worth trying, and it is metered — the grant is there so trying it is not a budget decision.
- A page explaining what beta means for you. Public beta sets out what is covered, what may change under you, how to report something broken, and where to check whether it is us or you.
- Storefronts that are already live stay live. Pay-to-publish is a rule for stores that have not been published yet. Nothing that was already serving shoppers went dark, and nothing needs to be re-published to stay up.
- Merchants already on a plan keep the price they are on. The catalogue above applies to plans started from today. Retired plans are deactivated, not deleted: a subscription still on one keeps its own price and its own card rate.
- Nothing else changed for existing stores. Same dashboard, same API, same keys, same scopes. No migration, no re-authentication, no plan change.
- Talk to sales is still the front door. If you are migrating a catalog or moving live subscriptions, that path is unchanged and is still the one we would point you at. Self-serve is an addition to it.
- The Gateway contract is unchanged. Scopes, idempotency, jobs, approvals and webhooks behave exactly as documented. Opening sign-up did not loosen any gate.
Checkout now runs on platformdtc.com
Your checkout has moved off a separate address and onto the main one.Shoppers used to be handed from your store to
checkout.platformdtc.com — a hostname that matched
neither your brand nor anything they had clicked. That hop cost a fresh DNS lookup and TLS
handshake on the slowest possible moment in the funnel, and it showed buyers a domain name nobody
chose. Checkout now runs at platformdtc.com/checkout/… instead, on the same address as everything
else.Stores on their own custom domain are unaffected and remain the better setup: checkout there runs on
yourbrand.com, where the shopper never leaves your brand at all.Changed- Checkout runs at
platformdtc.com/checkout/<session>. One origin, one certificate, one connection — the checkout page now reuses the connection the shopper already has. - Buy-now permalinks read
platformdtc.com/cart/<variant>:<qty>. The permalink keeps the same shape it has always had; only the domain changed. A permalink is the one checkout link a person reads and pastes, so its shape does not move when your store does. - Express wallets registered on the new address. Apple Pay, Google Pay, Link, Amazon Pay and Klarna are registered for the new host before any store is moved onto it. They only render on a registered hostname, and an unregistered one fails silently — so the move is gated on the registration, not on a checklist.
- Every link you have already sent still resolves. Permalinks running in live ad campaigns, recovery emails already in inboxes and links inside released mobile app builds keep working. They are forwarded to the new address with their query string intact, so payment returns and campaign attribution are unaffected.
- Your shopper account area has not moved. Sign-in, orders, saved products, followed stores and the subscription portal stay exactly where they were.
- Nothing about your checkout changed but the address. Same branding, same blocks, same payment providers, same settings.
Removing a domain now frees its email too
A domain you remove is now genuinely free — including the email on it.Removing a custom domain used to release only half of it. If you had set email up
on that domain, the mail side kept holding the name: one domain belongs to one
account, permanently, because two accounts sharing a mail domain would be two
accounts able to read each other’s mail. So the domain looked disconnected, and
setting it up on another store answered “that domain is already served by
another organisation” — with nothing you could do about it from your own
dashboard.Added
- Remove email from a domain. In Settings → Domains → Email from your
domain, each configured domain now has a remove action. It asks first, and it
says what goes: a sending domain stops sending as your address and falls back to
[email protected]; a full-mailbox domain loses its mailboxes and the mail in them. - Removing the domain removes its email too. Disconnecting a custom domain now releases the matching mail setup automatically, so the name is immediately usable on another store or another account.
- A confirmation before a domain is removed. Removing a domain was one unconfirmed click. It now asks, and when the domain carries your email it says so in the dialog, before anything is torn down.
- Your other addresses are untouched. Email lives on the root domain, so if you
have both
brand.comandshop.brand.comconnected, removing one of them leaves the email alone — it is only released when the last hostname on that domain goes. - Nothing is released while it might still be receiving. If the mail service cannot be reached, the removal reports a failure and changes nothing rather than freeing a name whose mailboxes are still live.
Every parcel tracked, and told to you when it stops moving
Your store now watches all 3,393 parcels it has shipped, not a sample of them —
and emails you when a batch of them stops moving.Until now only the most recent shipments were registered with the carrier
aggregator, because registration is metered and the backfill ran a seven-day
window. That meant most tracking numbers had no scan data behind them: a shopper
pasting theirs into your tracking page was told the order could not be found,
which is the worst possible answer to give someone who is already anxious about
a parcel.Added
- Full tracking coverage. Every fulfilled order carrying a tracking number is now registered and syncing, so your tracking page answers for all of them.
- Delivery alerts. One email per issue per day — never one per parcel — when enough of your shipments cross a line: the carrier has flagged them (held, returning, failed delivery), they have been silent for over a week, they are past the delivery date you promised, or they were marked shipped and never scanned at all. Each email says what the number means, names a few of the parcels, and links straight to them.
- “Preparing to ship” now means what it says. Carriers report a printed label the same way they report a real shipment, and we were passing that through as Shipped — on 266 parcels, 109 of which had been sitting that way for over a week. Those now read “Preparing to ship” for your shopper, and get their own line on the delivery dashboard so you can see which supplier printed labels and dispatched nothing. Transit times were also being measured from the moment the label was printed rather than the first real carrier scan; they are now measured properly, which is why your average may have moved.
- A reason for every hidden scan. The tracking page has always suppressed origin-country and non-English carrier lines so your brand story does not end on a foreign logistics portal. Now each hidden scan records which rule hid it, so the policy can be audited instead of trusted.
What Meta claims, and what the data can stand behind
A new report at Analytics → Advertising. It puts Meta’s reported ROAS beside a
number built only from orders we can prove came from a Facebook ad click, and
explains the difference.Ad platforms report their own contribution, and every platform takes full credit
for every conversion it touched. Meta’s default counts a purchase up to seven days
after a click and one day after a mere view, and fills the gaps it cannot
observe with modelled estimates. On one live store over thirty days, Meta claimed
more conversion value than the store took in revenue — including subscription
renewals no advertisement drove.Added
- The claim beside the floor. Meta’s ROAS and a click-only ROAS over the same spend. The second is deliberately conservative: it counts only orders carrying a real Facebook click identifier, so the true figure sits between the two.
- Where the gap comes from, as arithmetic against your own books — how many more conversions Meta claims than you had orders, and what share of your total revenue its claim represents.
- What the evidence supports, tier by tier, with unattributed revenue shown at full size as its own line. We never spread unknown revenue across channels; that is what makes a platform number look better than it is.
- A per-campaign table, Meta’s claim next to ours. Campaigns we cannot measure
show a dash and an
unmatchedbadge — not a zero. “We could not measure this” and “this sold nothing” are different statements, and only one of them is true. - Coverage, stated plainly. Every figure carries the share of orders and spend it rests on.
- Campaign-level figures depend on an ad’s destination naming the product it sells. Ads landing on a product page, or on a product-named domain, are matched automatically; the rest are reported as unmatched rather than guessed at.
- Subscription renewals inherit the attribution of the order that acquired them. Renewals whose acquisition predates click-id capture stay unattributed, and the report says so instead of showing a zero.
Disputes answer themselves
Chargebacks can now be contested automatically. Turn it on at Orders →
Disputes, on the switch above the table.A card dispute gives you roughly seven days to respond, and a deadline that passes
in silence is scored as a loss — the money, the goods, and a mark against the
dispute rate that card networks cap. Until now PlatformDTC could show you a
chargeback but not answer one; answering meant logging into your payment provider
before a deadline nobody was watching.Added
- Automatic evidence submission, per store, off by default. When on, we build the response from the order’s own records — customer name and email, the IP the order was placed from, billing and shipping address, carrier and every tracking number on the order (including each leg of a split shipment), the line items, and your published refund policy — and submit it to the cardholder’s bank.
- Timed to the deadline, not to the filing. Evidence goes about 48 hours before the deadline, because it can only be sent once: a delivery scan that lands on day five belongs in the response.
- Reason-aware. A non-receipt dispute leads with delivery proof, a duplicate-charge dispute explains the difference between the two charges, and a cancelled-subscription dispute answers the cancellation claim.
- An
Evidencecolumn on the disputes table — submitted (with the timestamp), queued, retrying, not contested, or deadline passed. - An alert when we answer for you, listing exactly which evidence fields were sent, and a matching entry on the order’s own timeline.
- It never contests a refunded order. If you already refunded, the money is back with the cardholder; those are marked not contested and left alone.
- It never fabricates. A fact the order cannot substantiate is left out rather than guessed — an issuer weighs a contradicted claim against you.
- It does not cover PayPal disputes, which have their own evidence process.
Traffic & Behaviour report
Analytics has a sixth report, and it is the first one that is not about orders.
Open it at Analytics → Reports → Traffic & Behaviour.Every report until now answered a question about a purchase. This one answers the
question underneath it: of everyone who arrived, how many got anywhere, and what
stopped the rest. The on-site pixel has been recording that since it shipped —
scroll depth, engaged time, rage and dead clicks, form abandonment, script errors,
Core Web Vitals — and until now nothing read it back.Added
- Traffic — sessions, visitors, pageviews, pages per session, bounce rate and returning-visit share.
- Engagement — average engaged time (which accrues only while the tab is visible and the visitor is actually doing something, so a tab left open all afternoon counts for nothing), scroll depth, how many readers reached the bottom of the page, and what share of sessions reached a checkout step.
- Friction — rage clicks, dead clicks, the share of sessions that hit a script error, frustrated sessions, and form abandonment. This is the part of the report that tells you where orders are being lost rather than that they were.
- Site Speed — Largest Contentful Paint, Interaction to Next Paint, Cumulative Layout Shift and Time to First Byte, each at the 75th percentile, measured on your real visitors’ devices rather than in a lab.
- Same date filter and same previous-period comparison as every other report, so the numbers here line up with the ones next to them.
- A metric where a rise is bad now says so, and the dashboard colours it accordingly. Refunds, Returns, Disputes, Unsubscribes, Cancellations and Churn were all painted green when they grew. They are not.
Primary domain and redirects
A store now has one primary domain, and every other domain either redirects to it
or serves the storefront on its own address. See Domains.Until now every domain attached to a store served the same storefront. A merchant
with four domains had four copies of their store competing with each other in
search, and no way to say which one was real.Added
traffic_modeon every domain —primary,redirectorserve. Redirects are301by default, preserve the path and query, and are answered at the edge before any request reaches the storefront.GET /domains,PATCH /domains/{id},PATCH /domains/{id}/set-primaryon the Agent Gateway, so an agent can read and change a store’s routing understore:read/store:publish.redirect_otherson set-primary — fold every other attached domain into the new primary in one call, for a store that has drifted into several copies of itself.- A canonical URL on the storefront. The compiled storefront now emits
<link rel="canonical">andog:urlbuilt from the primary domain. Neither existed before: social previews and search engines were pointed at the platform subdomain even for merchants on their own domain. - The domains screen in Settings shows what each domain actually does — “Primary”, “Redirects to acme.com”, “Serving separately” — and flags when several domains are serving duplicate copies of the store.
- Newly attached domains default to redirecting to the primary, matching what merchants expect. Domains attached before this release keep serving and are not touched — nothing started redirecting on its own. Change them when you want to.
- A store’s first verified domain is promoted to primary automatically, so links and emails stop pointing at the platform subdomain the moment a domain goes live.
wwwand its apex are always treated as one address: promoting either one makes the other redirect to it.
- Meta CAPI
event_source_urlwas resolved from a database table that does not exist, silently falling back to a legacy field. Purchase and subscription-renewal events sent from the server could therefore report the wrong store address, which degrades Meta’s event matching. Every URL the platform generates now comes from one resolver.
Public status page
status.platformdtc.com is live. Live operational state
for the surfaces we probe, a rolling uptime record, and the full incident history — with
email, Slack, Teams, webhook, RSS and Atom subscriptions. See
Platform Status.Added
- Eight components, grouped by who feels the failure: Storefronts and Checkout (buyer-facing), Merchant Dashboard and Core API (merchant), and Notifications, Background Jobs & Fulfillment, Analytics & Reporting and Documentation (platform).
- A JSON API implementing the Atlassian Statuspage v2 contract —
/api/v2/summary.json,status.json,components.json,incidents.json,incidents/unresolved.json. Unauthenticated, CORS-open, cached 30s. Any existing Statuspage widget or uptime aggregator works against it with no adapter. - Response times, not just up/down. Every component publishes its p50, p95 and the tail ratio between them, alongside the exact thresholds we alert on. If you think a surface is slow you can cite the same number we do.
GET /api/v2/uptime.json— 90-day daily uptime history. A PlatformDTC extension; Statuspage keeps this private.- Signed webhooks using the same
X-PlatformDTC-SignatureHMAC-SHA256 scheme as platform webhooks, so existing verification code works unchanged.
- The page has no origin server. It runs entirely on Cloudflare’s edge and shares no infrastructure with the systems it reports on, and its checks run from outside our network. A status page hosted on the thing it monitors goes dark exactly when you need it, so this one is verified the hard way: by cutting off every monitored service and confirming the page still loads and correctly reports a major outage.
- Alerting keys on the tail, not the average. An internal investigation found individual API requests reaching 9.9s while the median held at a healthy 0.31s — every average-based dashboard was green for weeks. Thresholds are per-component because healthy baselines span two orders of magnitude across these surfaces, from ~12ms on cached checkout to ~1s on the server-rendered dashboard.
- Feeds and webhooks emit one entry per incident update, not per incident, so subscribers hear about progress and resolution rather than only the opening.
- Email is double opt-in. An unconfirmed address is never sent anything.
- Individual stores are not modelled. Problems affecting a small number of specific stores may not appear — this reports platform-wide state. If your store is affected while the page is green, contact support rather than assuming it is you.
Import a product from a Shopify link
Import a product from its Shopify URL. Paste a link
from any public Shopify storefront and get a real product in your store — title,
sanitized description, images, options, and every variant with its own price, compare-at
price and SKU. In the dashboard: My Products → Import Products → From a Shopify link.Added
POST /products/import/shopify/preview— reads the source store and returns exactly what would be created, writing nothing. Use it to show a confirmation step.POST /products/import/shopify— creates a product per URL, up to 20 per call. One bad link never aborts the batch (207 Multi-Statuson a mixed result).- Re-import is idempotent. A link whose source product the store already holds comes
back as
skippedwith the existingproduct_idinstead of creating a duplicate. - Both endpoints take the
write_productsscope. Preview needs it too, since it performs outbound fetches on your behalf.
- Images are re-hosted on your own CDN, not hotlinked from the source store, so an imported product does not lose its photos when that store edits them.
- Descriptions are sanitized — scripts, iframes and event handlers are stripped from
the source
body_htmlbefore it is stored. - Money is copied verbatim in the source store’s currency; no conversion is applied.
- Inventory is not imported. Public storefronts expose whether a variant is in stock but never the count, so imported variants start untracked rather than with a made-up number.
- Imports default to draft status so nothing reaches your storefront before you have reviewed the pricing.
- Some storefronts block automated reads entirely. Those links report that plainly rather than failing silently or importing a partial product.
Products on the agent surface
Products is now a first-class agent API — the last core commerce
object to reach the agent surface.
/api/v1/products accepts X-Agent-Key: sq_agt_*
alongside the dashboard’s user JWT, with the same audit log and Idempotency-Key semantics
as the other resource APIs.Added- Full product surface — list (paged, searchable, sortable), get, create, partial update,
soft delete, facets, duplicate, images, variants, bulk status/tags/delete, and the dropship
catalog-link bind (
POST/DELETE, previously read-only). - Writable fulfillment routing —
supplier_idandfulfillment_rulesare now settable through the API, so destination-based routing finally has a population path. read_products/write_productsscopes, matching the Shopify-parity naming the other resource APIs use.
- Commerce scopes were unmintable.
read_inventory,write_inventory,read_discounts,write_discounts,read_gift_cards,write_gift_cards,read_metafields,write_metafields,read_returns,write_returns,read_draft_ordersandwrite_draft_orderswere enforced by the resource APIs but rejected at key creation, so nosq_agt_*key could hold them and those APIs were reachable only with a user JWT. All twelve are now grantable. Reissue any key that needs them — existing keys are unchanged. offers:read/offers:write/offers:approvewere unmintable too, for the same reason — and this was not theoretical: a key provisioned in production already carried all three and could never use them, because the scopes failed validation and the offers router was not mounted. All three are now grantable.offers:approveis deliberately not a spend scope — offer optimization gates its two dangerous calls in-band, at the call rather than the key.write_gift_cardsandwrite_discountsare now spend scopes. Both mint bearer value that is redeemable at checkout, so they belong behind the human-approval gate alongsideads:writeandmarketing:send.
catalog:read/catalog:writeare now aliases ofread_products/write_products. Already-issued keys keep working and either name authorizes either surface; no migration is required. New keys should use the*_productsnames.GET /whoamigained aneffective_scopesfield.scopescontinues to report the grant exactly as issued;effective_scopesshows it expanded through the aliases, so an agent debugging a403can see why acatalog:readkey reaches aread_productsroute.- The tool catalog is generated from the MCP toolpack instead of hand-maintained. It had drifted to listing 23 of 78 tools.
Commerce resource APIs
Six new resource APIs are live under
/api/v1, all on the scoped, idempotent,
audited Agent Gateway:- Inventory & Locations — multi-location stock; set/adjust/move across quantity states.
- Discounts — code & automatic discounts (DiscountNode model).
- Gift Cards & Store Credit — issue/redeem gift cards, per-customer store credit.
- Draft Orders — invoices/B2B/phone orders; calculate → invoice → complete.
- Metafields — typed custom data on any resource + definitions.
- Returns — RMA lifecycle + reverse logistics.
products/*, inventory_levels/update, discounts/*, gift_cards/*, draft_orders/*,
metafields/update, returns/*, refunds/disputes, GDPR compliance topics, bulk_operations/finish).Fulfillment API
Supplier / 3PL fulfillment is live —
/api/v1/fulfillment. A Shopify-parity
FulfillmentOrder API so third-party logistics and dropship suppliers can
pull assigned orders, accept/reject, push tracking, and sync stock.Added- FulfillmentOrder model — orders split per assigned location;
status×request_statusstate machine matching Shopify. - Supplier endpoints — register a fulfillment service (auto-provisions a location),
/assigned_fulfillment_orders, accept/reject + cancellation handshake, create/update/cancel fulfillments (partials + multi-tracking),/inventory_levels/set&/adjust. - Scope enforcement —
read/write_assigned_fulfillment_orders,write_fulfillments. - Webhooks —
fulfillment_orders/*,fulfillments/*,orders/fulfilled+ thefulfillment_order_notificationservice callback, HMAC-signed. - Auto-routing — paid-order line items routed to supplier locations by supplier id and auto-submitted so 3PLs are notified to ship immediately.
Agent Gateway
The Agent Gateway is live —
/api/v1/agent/v1. PlatformDTC is now agent-native.Added- 36 REST endpoints across store, catalog, ads, marketing, analytics, orders, jobs, and admin.
- 23
dtc_*MCP tools via themcp-platformdtcserver for OpenClaw agents. - Service-account keys (
sq_agt_*) with fine-grained scopes, per-key rate limits, rotation, and revocation. - Async job model with one poll surface (
/jobs,/jobs/{id},/jobs/{id}/wait). - Idempotency on all mutations.
- Human-approval gate for spend actions.
- Signed webhooks (HMAC-SHA256) for
job.*,deploy.*,order.created,roas.threshold,approval.requested. - Full audit log of every call (
/audit). - Interactive API playground + this documentation.
dtc_create_automation(multi-step workflow builder).- Endpoints:
/analytics/daily-summary,/campaigns/{id}/analytics,/orders/import, job SSE streaming (/jobs/{id}/stream).