Agent skill

Treet Webhooks Skill

Receive and verify Treet webhooks. Use when setting up a Treet webhook endpoint, verifying the X-Treet-Signature header (base64 HMAC-SHA256 of the raw body), debugging 401 signature failures, or handling Shipping Label events (label/created, label/updated).

Install this skill

npx skills add hookdeck/webhook-skills --skill treet-webhooks


Treet (treet.co) runs branded resale and trade-in marketplaces. Its webhooks are set up by the Treet team on request (topic, trigger and URL) and currently cover one topic, Shipping Label. Each request carries an X-Treet-Signature header: the base64 HMAC-SHA256 of the raw request body, keyed with a secret Treet issues to you.

When to Use This Skill

  • How do I receive Treet webhooks?
  • How do I verify the X-Treet-Signature header?
  • How do I handle label/created and label/updated events?
  • Why does my Treet webhook endpoint return 401?
  • Why are my Treet webhooks unsigned?

Verification (core)

const crypto = require('crypto');

function verifyTreetSignature(rawBody, signature, secret = process.env.TREET_WEBHOOK_SECRET) {
  if (!secret) return false; // never accept when unconfigured
  // Whole header value, exact form: 43 base64 characters plus "=" padding
  if (typeof signature !== 'string' || !/^[A-Za-z0-9+/]{43}=$/.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); // 44 bytes each
}
  • Header: X-Treet-Signature, bare standard base64 (padded), no prefix. Compare the whole value.
  • Signed content: the raw body bytes only. No timestamp, id or other header is signed, so there is no replay window to check.
  • Key: the secret as UTF-8 bytes, used as-is (not hex- or base64-decoded). Env var: TREET_WEBHOOK_SECRET.
  • Raw body: verify the exact bytes (express.raw({ type: '*/*' }), await request.arrayBuffer(), await request.body()). Never JSON.parse/JSON.stringify before verifying.
  • Exact comparison: the base64 is case-sensitive. Validate the format, then compare the strings in constant time. Decoding alone isn't enough: a value whose last character carries stray bits decodes to the same bytes but Hookdeck rejects it, so you must too.
  • Python: hmac.compare_digest on bytes, never str.
  • Missing or empty header, malformed value or mismatch: 401. Signed but malformed body: 400.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Signing Is Opt-In

Treet issues the secret key on request (support@treet.co). Without it, no signature header is sent. Hookdeck shows this note on its Treet source:

Signing is opt-in: ask Treet support for a secret key and paste it here. Without one, deliveries are unsigned and fail verification.

HTTP Methods

Treet's docs don't name the HTTP method. Hookdeck's Treet source pre-selects POST without managing the method (you can change it), so any other method the sender uses must also be allowed on the source. The examples accept GET, POST, PUT, PATCH and DELETE, verify the signature first for every method, and treat a missing body as empty bytes: a signed GET with no body is verified over zero bytes and acknowledged with 200.

Event Types

Dispatch on the body's trigger field. topic is the coarser topic (label) and is the same for both.

triggerFires when
label/createdA shipping label is created
label/updatedA shipping label changes (e.g. status becomes SHIPPED)

Log and acknowledge any other trigger. After verifying, treat every field as untrusted and check types before reading into nested values. Treet documents nothing about retries, timeouts or the expected response, so return 2xx once handled and make handlers idempotent.

Environment Variables

TREET_WEBHOOK_SECRET=your_treet_secret_key   # issued by Treet support

Local Development

npx hookdeck-cli listen 3000 treet --path /webhooks/treet

No account required. Use the printed URL when asking Treet to register your webhook.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 13, 2026

View on GitHub →