Fit API reference
The ShirtMath fit API is two browser calls: post a publishable key to /b2b/widget/session for a short-lived signed session, then post a product and the shopper's reference garment to /b2b/widget/match for a recommended size.
Last verified: 26 August 2026
The ShirtMath fit API is two browser calls and one server-side family. You post a publishable key to /b2b/widget/session to get a short-lived signed session, then post a product reference and a shopper's reference garment to /b2b/widget/match to get a recommended size, an explanation, and a candidate list. Everything else lives under the workspace endpoints, scoped to your business id.
This page documents the endpoints that exist on the running service today. Where a field list, an error code or a numeric limit is not published here, it is because it is settled at integration time and this page will not guess at it. ShirtMath is pre-launch; treat every shape below as documented but young.
What is the ShirtMath fit API?
It is a garment-to-garment matching service. Your product catalogue carries flat measurements. The shopper supplies a reference garment they already own and like. The API compares the two sets of measurements and returns the size on your product that lands closest, with the reasoning attached.
It does not scan bodies and it does not estimate a body from a photograph. The comparison is garment against garment, which is why the answer can be explained in numbers rather than asserted. If you have not imported a catalogue yet, start at connect your catalogue; the matching endpoints have nothing to match against until products exist.
How does authentication work?
There are two credential types and they are not interchangeable.
| Credential | Where it belongs | Header |
|---|---|---|
| Publishable key | Browser, storefront markup | X-ShirtMath-Key |
| Session token | Browser, returned by the session call | X-ShirtMath-Session |
| Secret key | Server only, shown once at creation | Documented at integration time |
A secret key is displayed once, at the moment it is created, and is not retrievable afterwards. If it is lost, rotate it. Rotation and revocation are separate operations on purpose: rotation issues a replacement so you can cut over, revocation kills a key outright.
- Create a key — POST to /b2b/workspace/{business_id}/keys. The secret half is shown once in that response.
- Verify a domain — register the storefront origin, then confirm it through /b2b/workspace/{business_id}/domains/verify.
- Open a session — POST /b2b/widget/session with the publishable key in X-ShirtMath-Key. A domain in verified state is granted CORS; anything else is not.
- Match — POST /b2b/widget/match with the session token in X-ShirtMath-Session.
- Rotate or revoke — /b2b/workspace/{business_id}/keys/{key_id}/rotate and /revoke when a key leaks or a contractor leaves.
How does domain verification restrict origins?
The session endpoint grants cross-origin access only to a domain your workspace holds in verified state. A publishable key on its own is not enough. That is the point of the pair: the key is public by design, so the origin restriction is what stops a copied key from working on someone else's site.
Domains are managed at /b2b/workspace/{business_id}/domains and moved to verified through /b2b/workspace/{business_id}/domains/verify. The verification method and its record format are handed over during onboarding rather than published here.
What does POST /b2b/widget/session return?
A short-lived signed session, intended to be held in the page and used for the match calls that follow. It is short-lived deliberately, so a token scraped from a page stops working quickly. The exact lifetime is set per tenant and confirmed at integration time.
What does POST /b2b/widget/match do?
It takes the product the shopper is looking at and the garment they already own, and returns a size on your product. The request body carries two members.
| Request member | Meaning |
|---|---|
| product_ref | Your own product id, as imported into the catalogue |
| reference | The shopper's reference garment; the object's field list is fixed at integration time |
The response carries five members that the widget renders directly.
| Response member | What it holds |
|---|---|
| status | Whether an answer was produced, or the honest-failure value below |
| authority | Which fit authority answered; values seen in the client include SANDBOX and PHASE4 |
| current_product | Carries recommended_size for the product the shopper is on |
| explanation | The reasoning behind the recommendation, for display |
| candidates | The other sizes or products considered, as a list |
Read authority before you trust a recommendation in a test environment. A SANDBOX answer is not a production answer, and a widget that renders the two identically will mislead whoever is doing the acceptance pass.
What happens when ShirtMath cannot answer?
The status member may come back as FIT_AUTHORITY_UNAVAILABLE. That is not an error to retry around and not a signal to fall back on a guess. It means the service does not hold enough measurement data to make a defensible comparison for that product, and the widget shows an honest "not enough information" instead.
The usual cause is missing measurements on your side rather than a fault on ours. Check /b2b/workspace/{business_id}/coverage, which reports measurement coverage across the imported catalogue, and fill the gaps at source.
Which workspace endpoints exist?
All of these are scoped to a business id. A credential issued to one workspace reaches that workspace's records and no other.
| Endpoint | Purpose |
|---|---|
| /b2b/api/products | The tenant's own imported products |
| /b2b/workspace/{business_id} | The workspace record |
| /b2b/workspace/{business_id}/catalog | The imported catalogue |
| /b2b/workspace/{business_id}/coverage | Measurement coverage across that catalogue |
| /b2b/workspace/{business_id}/keys | List and create keys |
| /b2b/workspace/{business_id}/keys/{key_id}/rotate | Issue a replacement for one key |
| /b2b/workspace/{business_id}/keys/{key_id}/revoke | Kill one key |
| /b2b/workspace/{business_id}/domains | Registered storefront domains |
| /b2b/workspace/{business_id}/domains/verify | Move a domain to verified state |
| /b2b/workspace/{business_id}/install | Install details for the storefront |
| /b2b/workspace/{business_id}/integration | Integration configuration |
| /b2b/workspace/{business_id}/audit | Audit record for the workspace |
What does the browser widget do?
The client identifies itself as b2b-widget/0.1.0 and mounts into a host element on your product page. It opens a session, calls match when the shopper supplies a reference garment, and renders the recommended size with its explanation. The version string is worth logging on your side: a widget at 0.1.0 is early software and you will want to know which build produced a given answer.
What are the rate and permission boundaries?
Three boundaries are visible in the service as it runs today. First, origin: CORS is granted only to a verified domain. Second, scope: workspace endpoints are addressed by business id, so a key does not reach another tenant's catalogue. Third, credential separation: the browser only ever holds a publishable key and a short-lived session, never a secret key.
Numeric request limits, retry guidance and the error code table are set per tenant and confirmed during onboarding. They are not published on this page because publishing a number that later changes is worse than publishing nothing.
Why does garment measurement make the answer explainable?
Because the comparison is between two numbers rather than between two letters. Letters are not stable across cuts. In ShirtMath's held blank specifications, a ladies' Gildan G500L Medium publishes a 17.5" chest while the unisex Mediums publish 20.0" to 20.5" — a 3 inch gap on the same letter, and the same 3 inch gap at Large.
That gap is why an explanation member exists in the response at all. A shopper told "Large" learns nothing; a shopper told which measurement drove the answer can disagree with it intelligently. The same drift across brands is covered in the guide to vanity sizing, and the comparison logic itself is set out in the methodology.
What is not in this API?
Body scanning is not, in any form. Shoe identification and shoe fit from a photograph are not. Sunglasses measurement from a photograph is not. The live capability is a shirt laid flat with a scale card, measured from a photograph, matched against published garment measurements — roughly half of scanner readings land within an inch of a tape, sleeve opening and bicep are the least reliable, and anything the reader cannot resolve confidently is left blank rather than filled in.
Common questions
Do I need a secret key in the browser?
What happens if ShirtMath cannot answer?
Can I call the widget endpoints from any domain?
Where are the full field lists documented?
Where to go next
See the match logic from the shopper's side first. Photograph a shirt laid flat with a scale card and watch what the comparison actually says before you wire the widget into a product page.
Scan a shirt