Webhooks
TryYourSize sends events to HTTPS endpoints you add in Studio → Developers → Webhooks. Endpoints belong to one store and one mode (test or live); up to 16 per store. Each endpoint subscribes to a list of event types, or to all (*). Its signing secret (whsec_…) is shown once.
Event types
Section titled “Event types”Payloads never carry shopper identity or measurements.
| Type | data |
When |
|---|---|---|
fit.recommended |
tryon_id, product_sku, recommended_size, no_size_fits |
Once per fit result, store and mode |
garment.published |
garment_version_id, fit_group_id, version, product_skus |
A garment version was published (sent to test and live endpoints) |
fit_check.completed |
fit_check_id, garment_version_id, status |
A Studio fit check finished or failed |
profile.disconnected |
connection_id, reason |
A shopper disconnected your store (live mode only) |
usage.threshold_reached |
Coming soon: your usage reached a threshold of your plan | |
webhook.test |
“Send test event” in Studio |
The envelope
Section titled “The envelope”{ "id": "evt_…", "object": "event", "type": "fit.recommended", "api_version": "2026-10", "created": 1793000000, "mode": "live", "store_id": "…", "data": { "tryon_id": "…", "product_sku": "CREW-TSHIRT", "recommended_size": "M", "no_size_fits": false }}Headers: TYS-Signature, TYS-Event-Id, TYS-Event-Type, TYS-Delivery-Id, TYS-Delivery-Attempt. The same event can arrive more than once: deduplicate on TYS-Event-Id (the envelope’s id).
Verify the signature
Section titled “Verify the signature”TYS-Signature: t=1793000000,v1=5f2c…v1 is the hex HMAC-SHA256 of "<t>.<raw request body>", keyed with the whole whsec_… secret. While you roll a secret, there are two v1 values (one per secret) for 24 hours: accept the request if any matches. Reject timestamps older than 5 minutes. Always verify the raw body, before any JSON parsing.
import express from "express";import TryYourSize from "@tryyoursize/node";
const tys = new TryYourSize(process.env.TYS_SECRET_KEY!);const app = express();
app.post("/webhooks/tryyoursize", express.raw({ type: "application/json" }), (req, res) => { let event; try { event = tys.webhooks.constructEvent(req.body, req.header("TYS-Signature")!, process.env.TYS_WEBHOOK_SECRET!); } catch { return res.sendStatus(400); } if (event.type === "garment.published") { /* … */ } res.sendStatus(200);});import osimport tryyoursizefrom flask import Flask, request
app = Flask(__name__)
@app.post("/webhooks/tryyoursize")def webhook(): try: event = tryyoursize.Webhook.construct_event( request.get_data(), request.headers["TYS-Signature"], os.environ["TYS_WEBHOOK_SECRET"] ) except Exception: return "", 400 if event["type"] == "garment.published": ... return "", 200import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody: Buffer, header: string, secret: string, toleranceSec = 300): boolean { const parts = header.split(",").map((p) => p.split("=")); const t = parts.find(([k]) => k === "t")?.[1]; const sigs = parts.filter(([k]) => k === "v1").map(([, v]) => v); if (!t || !sigs.length) return false; if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false; const expected = createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest(); return sigs.some((s) => { const got = Buffer.from(s, "hex"); return got.length === expected.length && timingSafeEqual(got, expected); });}import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool: items = [p.split("=", 1) for p in header.split(",") if "=" in p] t = next((v for k, v in items if k == "t"), None) sigs = [v for k, v in items if k == "v1"] if t is None or not sigs or abs(time.time() - int(t)) > tolerance: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return any(hmac.compare_digest(expected, s) for s in sigs)In any other language: split the header on ,, take t and every v1, compute HMAC-SHA256 over t + "." + raw body with the secret as the key, hex-encode it, compare in constant time against each v1, and check that t is within 5 minutes of now.
Responding and retries
Section titled “Responding and retries”- Answer any 2xx within 10 seconds to acknowledge. Do slow work after you answer.
- Redirects are not followed; the response body is ignored.
- On another status, a timeout or a network error, we retry: an attempt now, then after 1 min, 5 min, 30 min, 2 h, 5 h and 10 h, then every 12 h: 12 attempts over about 72 hours.
- Answer 410 Gone to stop the retries of that delivery.
- Disabling or deleting the endpoint stops pending deliveries.
Studio shows every delivery with its payload and attempts, and can resend one.
Rolling the secret
Section titled “Rolling the secret”In Studio, roll an endpoint’s secret: the previous secret keeps signing (as a second v1) for 24 hours, so you can deploy the new one without dropping events.
Was this page helpful?
Thank you. We read every answer.