Overview
Push loyalty events from BLOY to your systems in real time, instead of polling for changes.
Register an HTTPS endpoint against a topic, and BLOY sends it a signed POST the moment something happens — a customer earns points, reaches a new VIP tier, or claims a referral reward.
Getting started
API base URL:https://api.bloy.io/rest-api/v1
Plan requirements
Webhook API require the BLOY Unlimited plan.
What you can do
- Subscribe an endpoint to a topic, or to a whole namespace with
points/* - Verify every delivery with a per-subscription HMAC signature
- Rotate a signing secret without downtime, with a 24-hour grace period
- Inspect delivery history and replay any delivery that failed
Prerequisites
- A publicly reachable HTTPS endpoint accepting
POST— see Callback URL requirements - Your shop's public API key, from the BLOY app in your Shopify admin
- A handler that reads the raw request body (a JSON parser breaks signature verification)
- Somewhere to store the signing secret — it is returned only once
Quickstart
1. Register a subscription. Save the signingSecret from the response; it is never shown again.
curl -X POST https://api.bloy.io/rest-api/v1/webhooks/subscriptions \
-H "Authorization: Bearer YOUR_PUBLIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "topic": "points/earned", "callbackUrl": "https://example.com/hooks/bloy" }'
2. Verify and acknowledge. Check the signature against the raw body, return 2xx immediately, then do the work asynchronously.
const express = require('express');
const crypto = require('crypto');
const app = express();
// Raw body — a JSON parser would break the signature.
app.post('/hooks/bloy', express.raw({ type: 'application/json' }), (req, res) => {
const expected = crypto
.createHmac('sha256', process.env.BLOY_SIGNING_SECRET)
.update(req.body)
.digest('hex');
const a = Buffer.from(expected, 'utf8');
const b = Buffer.from(req.get('X-Bloy-Hmac-Sha256') || '', 'utf8');
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).send('invalid signature');
}
res.sendStatus(200); // acknowledge first
queue.push({ topic: req.get('X-Bloy-Topic'), event: JSON.parse(req.body.toString('utf8')) });
});
Updated on: 26/08/2026
Thank you!
