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-Signatureheader? - How do I handle
return.created,return.updated,return.closedorlabel.createdevents? - 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.
| Trigger | Topic | Triggered When |
|---|---|---|
return.created | return | A return is submitted in the customer returns portal |
return.updated | return | A return changes, including state changes (open, closed, cancelled, review, expired) |
return.closed | return | A return's state becomes closed |
return.processing.failed | return processing | Queued return processing failed to complete |
label.created | label | A shipping label is created |
label.updated | label | A label's shipment status changes (for example to in transit) |
restock.requested | restock | An item in a return is restocked in Shopify |
giftcard.requested | gift card | A gift card is requested as part of an exchange |
shipment.processed | happy returns shipment | Loop processed a Happy Returns shipment |
For the full event reference, see Loop's webhook documentation.
Delivery Behavior
- Respond with a
2xxwithin 10 seconds. Do slow work after responding. - Failed deliveries are retried five times (six attempts) with exponential backoff and jitter.
429,500,502,503,504and network errors retry.400,401,403,404,405,410and422do not retry, so return400for 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
- references/overview.md - Loop webhook concepts, topics, triggers, and payloads
- references/setup.md - Creating subscriptions, finding the secret
- references/verification.md - Signature verification details and gotchas