Agent skill

Medusa Webhooks Skill

Receive and verify Medusa Cloud webhooks. Use when setting up a Medusa Cloud webhook endpoint, debugging X-Medusa-Signature verification, or handling build and deployment events like build.succeeded, build.failed, deployment.created, and deployment.succeeded.

Install this skill

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


These are Medusa Cloud webhooks: one endpoint per organization that is notified about builds and deployments. Self-hosted (open-source) Medusa has no built-in outbound webhooks, and Medusa application events such as order.placed are internal events handled by subscribers, not webhooks sent to your URL.

When to Use This Skill

  • How do I receive Medusa Cloud webhooks?
  • How do I verify the X-Medusa-Signature header?
  • How do I handle build.succeeded, build.failed, or deployment.succeeded events?
  • How do I notify a team or trigger a pipeline when a Medusa deployment finishes?
  • Why is my Medusa webhook signature verification failing?

Verification (core)

Medusa sends X-Medusa-Signature: t=<unix seconds>,v1=<hex>. v1 is the hex HMAC-SHA256 of "<t>.<raw body>", keyed with the endpoint's signing secret as a UTF-8 string. Always sign over the raw body.

The docs show an optional freshness check on t. Medusa re-signs each of its own retry attempts, but Hookdeck retries and replays resend the original timestamp, so a 5-minute window rejects late retries. The check is therefore opt-in here (MEDUSA_TIMESTAMP_TOLERANCE_SECONDS, default 0 = off). references/verification.md shows where the window is applied and how to drop or widen it.

const crypto = require('crypto');

// toleranceSeconds = 0 (default) skips the timestamp window; a positive value opts in
function verifyMedusaWebhook(rawBody, header, secret, toleranceSeconds = 0) {
  if (!header) return false;
  const parts = header.split(',').map((p) => p.trim().split('='));
  const t = parts.find(([k]) => k === 't')?.[1];
  const sigs = parts.filter(([k]) => k === 'v1').map(([, v]) => v);
  if (!t || !/^\d+$/.test(t) || sigs.length === 0) return false;
  if (toleranceSeconds > 0 && Math.abs(Date.now() / 1000 - Number(t)) > toleranceSeconds) return false; // opt-in replay window
  const expected = crypto.createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest('hex');
  return sigs.some((sig) => {
    try { return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); }
    catch { return false; } // length mismatch
  });
}

Medusa has no standalone verification SDK (the docs show a manual Node.js check), so verification is manual in every framework. This is not Standard Webhooks (no webhook-id / webhook-timestamp headers). In the Express example, read the body with express.raw({ type: '*/*' }) so any content type verifies.

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

Common Event Types

Every delivery is a POST with a JSON object { id, type, data, created_at } plus an X-Medusa-Event header matching type. Route on type:

EventTriggered WhenCommon Use Cases
build.createdA build is created for an environment (after a push to its branch)Track build starts
build.succeededA build completes successfullyPost to chat, trigger smoke tests
build.failedA build failsAlert the team
build.canceledA build is canceledUpdate status boards
deployment.createdA deployment is created for an environmentMark a release as in progress
deployment.succeededA deployment completes successfullyAnnounce release, run post-deploy checks
deployment.failedA deployment failsPage on-call
deployment.canceledA deployment is canceledUpdate status boards

Each payload's data carries organization, project, environment, and build or deployment objects. See references/overview.md.

Environment Variables

MEDUSA_WEBHOOK_SECRET=your_endpoint_signing_secret   # Signing Secret on the endpoint card in Cloud
MEDUSA_TIMESTAMP_TOLERANCE_SECONDS=0                 # Optional replay window; a positive value opts in

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 medusa --path /webhooks/medusa

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 3, 2026

View on GitHub →