Agent skill
Etsy Webhooks Skill
Receive and verify Etsy webhooks. Use when setting up Etsy webhook handlers, debugging Etsy signature verification (Standard Webhooks: webhook-id, webhook-timestamp, webhook-signature), or handling Etsy shop events like order.paid, order.shipped, order.canceled, listing.updated, or inventory.updated.
Install this skill
npx skills add hookdeck/webhook-skills --skill etsy-webhooks
When to Use This Skill
- How do I receive Etsy webhooks?
- How do I verify Etsy webhook signatures?
- How do I handle
order.paidororder.shippedevents from Etsy? - How do I react to
listing.updatedorinventory.out_of_stockfor my Etsy shop? - Why is my Etsy webhook signature verification failing?
How Etsy Webhooks Work
Etsy webhooks follow the Standard Webhooks spec. Every delivery is an HTTP POST with a JSON body and three headers:
| Header | Contents |
|---|---|
webhook-id | Unique message ID (stays the same across retries) |
webhook-timestamp | Unix timestamp (seconds) when the event was emitted |
webhook-signature | Space-separated list of v1,<base64 signature> entries |
The signature is HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{raw body}, keyed with the signing secret after you strip the whsec_ prefix and base64-decode the rest. The result is base64-encoded. Etsy recommends rejecting timestamps more than 300 seconds from your server time.
Payloads are thin: they carry event_type, shop_id, and a resource_url you call (with your OAuth token) to fetch the full receipt, listing, or product.
Verification (core)
const crypto = require('crypto');
function verifyEtsyWebhook(rawBody, headers, secret) {
const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const signatureHeader = headers['webhook-signature'];
if (!id || !timestamp || !signatureHeader) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; // replay protection
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = crypto.createHmac('sha256', key)
.update(`${id}.${timestamp}.${rawBody}`)
.digest('base64');
// Header may hold several "v1,<sig>" entries (e.g. during secret rotation)
return signatureHeader.split(' ').some((entry) => {
const [version, sig] = entry.split(',');
if (version !== 'v1' || !sig) return false;
const a = Buffer.from(sig);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
});
}
Etsy has no webhook SDK. The Node examples use the standardwebhooks package (new Webhook(secret).verify(rawBody, headers)), which implements exactly this algorithm with a 5-minute tolerance. The FastAPI example verifies manually.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
| Event | Triggered When |
|---|---|
order.paid | An order receives payment |
order.canceled | A seller cancels an order |
order.shipped | Shipping information is created for a receipt |
order.delivered | An order is marked as delivered |
listing.created | A listing is created |
listing.updated | Title, description, personalization, taxonomy, images, attributes, or state change |
listing.deleted | A listing is deleted |
inventory.created | A product (listing variant) is created |
inventory.updated | Price, readiness state, SKU, or state of a product changes |
inventory.deleted | A product is deleted |
inventory.out_of_stock | A product's quantity reaches 0 |
Note the spelling: order.canceled (one "l"). See references/overview.md for payload shapes.
Environment Variables
ETSY_WEBHOOK_SECRET=whsec_xxxxx # From Developer Portal → Manage your apps → Webhook portal → endpoint
Local Development
npx hookdeck-cli listen 3000 etsy --path /webhooks/etsy
No account required. Use the tunnel URL as the endpoint URL in the Etsy Webhook portal, then send a test event from the endpoint's Testing tab.
Resources
- references/overview.md - Etsy webhook events and payload structure
- references/setup.md - Configure endpoints in the Etsy Webhook portal, get the signing secret
- references/verification.md - Signature verification details and gotchas
- examples/ - Runnable Express, Next.js, and FastAPI examples with tests