Agent skill

Loop Returns Webhooks Skill

Receive and verify Loop Returns webhooks (the Shopify returns platform, not loop.com freight). Use when setting up a Loop Returns webhook handler, debugging X-Loop-Signature verification, or handling return.created, return.updated, return.closed, label.created, restock.requested and other returns and exchange events.

Install this skill

npx skills add hookdeck/webhook-skills --skill loop-returns-webhooks


Loop Returns is a returns and exchanges platform for Shopify merchants. It sends a webhook for each topic (return, label, restock, ...) and trigger you subscribe to, signed with an X-Loop-Signature header. This skill is for Loop Returns (docs.loopreturns.com), not loop-webhooks, which is loop.com freight audit and uses Svix headers.

When to Use This Skill

  • How do I receive Loop Returns webhooks?
  • How do I verify the Loop Returns X-Loop-Signature header?
  • How do I handle return.created, return.updated, return.closed or label.created events?
  • Why is my Loop Returns webhook signature verification failing?
  • How do I get the Loop webhook secret from Developer Tools?

Verification (core)

X-Loop-Signature is the standard base64 HMAC-SHA256 of the raw request body, keyed with the webhook secret from the Loop Admin. There is no timestamp and no signature prefix. Verify the raw bytes before parsing JSON.

const crypto = require('crypto');

function verifyLoopSignature(rawBody, signature, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
  try {
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  } catch {
    return false; // Different lengths = invalid
  }
}

// Express: mount the route with express.raw({ type: '*/*' }) so every content type reaches
// the verifier as a Buffer, and read the header with req.get('x-loop-signature').

Loop publishes no verification SDK, so verification is manual in every language. See the examples for Next.js (request.text()) and FastAPI (await request.body()).

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

Common Event Types

The event name is the trigger field in the JSON body (there is no event header). topic says which payload shape you get.

TriggerTopicTriggered When
return.createdreturnA return is submitted in the customer returns portal
return.updatedreturnA return changes, including state changes (open, closed, cancelled, review, expired)
return.closedreturnA return's state becomes closed
return.processing.failedreturn processingQueued return processing failed to complete
label.createdlabelA shipping label is created
label.updatedlabelA label's shipment status changes (for example to in transit)
restock.requestedrestockAn item in a return is restocked in Shopify
giftcard.requestedgift cardA gift card is requested as part of an exchange
shipment.processedhappy returns shipmentLoop processed a Happy Returns shipment

For the full event reference, see Loop's webhook documentation.

Delivery Behavior

  • Respond with a 2xx within 10 seconds. Do slow work after responding.
  • Failed deliveries are retried five times (six attempts) with exponential backoff and jitter. 429, 500, 502, 503, 504 and network errors retry. 400, 401, 403, 404, 405, 410 and 422 do not retry, so return 400 for a bad signature only when you mean it.
  • Delivery is at least once: dedupe, for example on id + trigger + edited_at / label_updated_at.
  • Loop may add payload fields at any time, so don't reject unknown fields.

Environment Variables

LOOP_WEBHOOK_SECRET=your_loop_webhook_secret   # Loop Admin > Settings > Developer Tools

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 3, 2026

View on GitHub →