Labs — Shopify App
B2B Triage: Wholesale Registration & Approval for Shopify B2B
Wholesale applications become real Shopify companies.
B2B Triage is a Shopify app by Human After All that turns wholesale sign-ups into native Shopify B2B accounts. A buyer applies through a form on your storefront, a person reviews the application, and approval writes real Shopify objects — Company, location, contact, catalog assignment, payment terms, tax exemption. Not a tag. Not a note. The records Shopify's own B2B engine runs on.
Launching on the Shopify App Store · Works on every paid Shopify plan
Last updated:
01 — The object graph
What does B2B Triage create when you approve a buyer?
Approving one application writes six native Shopify objects: a Company, a company location, a company contact, a catalog assignment, payment terms, and a tax exemption. Most registration apps stop at a customer record — the buyer's answers pasted into the Notes field as a text blob, a tag or two, and a password. B2B Triage ends at the object graph Shopify's B2B engine actually reads. The Company is created with a stable external ID, derived when the buyer applies, so a double-clicked Approve button cannot silently create duplicates. The location is assigned to one of your existing catalogs; the app never creates catalogs, so yours stay yours. Payment terms are resolved from Shopify's own template to a typed integer. The tax exemption is the jurisdiction-correct value from Shopify's 75 — Canada alone has six kinds. Every step is recorded on the application's own timeline. When Shopify refuses one, you read Shopify's words — not an error code we invented.
One approval. Six native objects.
Company
Created with a stable external ID, derived when the buyer applies. Shopify does not enforce unique company names; without the ID, a double-clicked Approve silently creates two.
Company location
Including the second one Shopify auto-creates in the background — no terms, no exemption, no catalog. Buyers tend to pick that one and pay retail. The app detects and reconciles it.
Company contact
The buyer, attached to the location using the assignment mutation that works when the customer already exists — which, in practice, is always.
Catalog assignment
The buyer’s location is assigned to a catalog you already built, so many buyers share the same tiered catalogs. The app never creates a catalog — yours stay yours.
Payment terms
Net terms resolved from Shopify’s own template to a typed integer — never parsed out of a display string, which breaks on the first French-language store.
Tax exemption
The jurisdiction-correct value from Shopify’s 75 exemptions. Canada alone has six kinds. A blanket yes/no boolean is not the same thing.
02 — Your data
Why native Shopify objects instead of tags and notes?
Because a note is not data and a tag is not a number. The common pattern stores registration answers as a delimited plaintext blob inside the customer's Notes field — editable by any staff member, unreachable by any report, segment, or query. B2B Triage writes typed, namespaced metafields instead. net_terms_days = 30 is an integer, so it supports "greater than 30". The tag net30 is a string: it collides with net300 and supports nothing. Metafields reach ShopifyQL as a GROUP BY dimension, so you can genuinely report on your wholesale program. Tags have no analytics dimension at all. Namespaces separate cleanly too: everything the app writes lands under b2b_triage, while tags share one flat namespace with every app on your store — two apps writing "wholesale" collide in silence. We still write tags, as a compatibility bridge for tools that only read tags, such as Klaviyo's native Shopify sync. The metafields are the truth; the tags are a courtesy.
| Property | Why it matters |
|---|---|
| Reportable | Metafields reach ShopifyQL as a GROUP BY dimension. Tags have no analytics dimension at all — you cannot report on a tag. (Shopify does not offer admin-list filtering on customer metafields; that answer comes from analytics and the export, not the customer list.) |
| Typed | net_terms_days = 30 supports "greater than 30". The tag net30 string-collides with net300 and supports nothing. |
| Namespaced | Everything lands under b2b_triage. Tags are one flat global namespace shared with every app on your store — two apps writing "wholesale" collide in silence. |
| Exportable | CSV with Shopify GIDs as join keys, so the row matches your CRM instead of approximating it. Values are normalised at input. |
03 — Ownership
What happens to your companies if you uninstall?
They stay. Shopify records no app-ownership link on a Company, a company location, a contact, a catalog assignment, payment terms, or a tax exemption — so there is nothing for an uninstall to take away. Metafields are the one exception: they carry a reserved $app: namespace, and uninstalling an app genuinely severs those. Companies have no such field. The accounts B2B Triage approves are ordinary Shopify records. Your staff can edit them. Your other apps can read them. They behave exactly as if your team had created them by hand in the admin. Stated precisely, because the claim deserves it: what is proven is the mechanism, and the mechanism holds no matter who created the objects. Contrast that with wholesale apps that keep registration data and pricing in their own database — leave, and your program leaves with them. This is the design philosophy in one sentence: we own the interface, Shopify owns the engine.
04 — Tax IDs
What happens when the EU VAT registry goes down?
On 16 July 2026 we asked VIES, the EU's VAT registry, about IE6388047V — Google Ireland's real VAT number. For 21 minutes the registry called it invalid; Ireland's member-state service was down. Then, with nothing changed at Google, it came back valid.
That is the trap. VIES sometimes reports its own outages as "invalid": technically, an HTTP 200 with isValid: false, while the reason hides in a different field.
| Time (UTC) | isValid | userError | The naive reading |
|---|---|---|---|
| 05:14 | false | MS_UNAVAILABLE | "Invalid" — fraud-flagged |
| 05:35 | true | VALID | GOOGLE IRELAND LIMITED |
B2B Triage never reads isValid. It reads the error code as an allowlist: exactly one value means "not registered". Everything else — even codes the European Commission ships next year — means "we could not check right now", which gets a different word on screen.
The app never rejects an applicant because a registry blinked. No branch in our code turns a registry's bad day into a rejection — not a policy we promise, a branch that does not exist. "Never checked", "checked and found nothing" and "the check failed" are three different facts; a boolean would collapse them into a lie.
05 — Performance
Does B2B Triage slow down your storefront?
No, and the numbers are measured rather than promised. The registration form ships as a theme app block, not an app embed. It loads only on the pages where you place it; an embed executes on every page of your store, including the ones with no form. The block makes zero third-party requests — no tracker, no CDN font, no analytics pixel, nothing that phones anywhere except your own storefront. It measures under 13 KB gzipped, enforced by a build step that fails when it grows. The form works with JavaScript disabled entirely; any tax-registry lookup runs on our server, never in your buyer's browser. Input handling follows the same discipline. Postal codes normalise per country — h9b2g9 becomes H9B 2G9. Phone numbers store as E.164. And 244 countries get province and state dropdowns built from Shopify's own zone data, which is not ISO 3166-2.
06 — Labs
How does B2B Triage fit with B2B Protocol and our other B2B work?
B2B Triage is the first Labs product we are taking to the Shopify App Store — self-serve, installable, and deliberately scoped to one job: wholesale registration and approval on Shopify's native B2B. The rest of our Labs work is client-exclusive. B2B Protocol is the Shopify Plus B2B accelerator we deploy for manufacturers and distributors — ERP middleware, buyer segment routing, corporate spend control. Its POS module put B2B contract pricing on Shopify POS for the first time. The philosophy is identical across all of them: never build a parallel system beside Shopify. Create and assign the platform's own objects, so merchants keep everything if they ever leave. If your wholesale program needs more than an approval flow — ERP-connected pricing, multi-segment routing, or a full Shopify Plus implementation — that is our agency practice, and the contact form is the door.
Wholesale applications, triaged.
B2B Triage is launching on the Shopify App Store. The full product story lives on this page, and every claim on it is checked against the code, not recalled. If a question survives the FAQ, talk to us directly about your wholesale registration flow.
