> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://docs.bloy.io/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# 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`
* 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')) });
});
```