Webhooks
Webhooks let your application receive real-time HTTP callbacks whenever important events occur in your Ploints account. Instead of polling the API, you register a URL and Ploints pushes events to you.
Setting Up Webhooks
You can register a webhook endpoint via the API or from the Ploints dashboard under Integrations → Webhooks.
Via API
curl -X POST https://ploints.space/api/v1/webhooks \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yoursite.com/webhooks/ploints",
"events": ["points.earned", "customer.created"]
}'The response includes a secret field. Store this securely -- you will need it to verify webhook signatures.
{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://yoursite.com/webhooks/ploints",
"events": ["points.earned", "customer.created"],
"secret": "whsec_k8x2..."
}
}Available Events
Subscribe to any combination of the following event types:
| Event | Description |
|---|---|
points.earned | A customer was awarded points |
points.redeemed | A customer redeemed points |
customer.created | A new customer was enrolled |
customer.tier_changed | A customer moved to a new loyalty tier |
badge.issued | A badge was issued to a customer |
reward.redeemed | A customer redeemed a reward |
campaign.conversion | A campaign conversion was tracked (e.g. referral, influencer link) |
Payload Format
Webhook payloads are sent as POST requests with a JSON body:
{
"id": "evt_a1b2c3",
"type": "points.earned",
"timestamp": "2025-06-15T10:30:00Z",
"data": {
"customerEmail": "jane@example.com",
"amount": 50,
"description": "Purchase #1234",
"newBalance": 350
}
}Signature Verification
Every webhook request includes a X-Ploints-Signature header containing an HMAC-SHA256 signature of the raw request body. Always verify this signature to ensure the request genuinely came from Ploints.
How it works
- Read the raw request body as a string (do not parse JSON first).
- Compute an HMAC-SHA256 hash of the raw body using your webhook secret as the key.
- Compare the computed hash to the value in the
X-Ploints-Signatureheader using a constant-time comparison. - If they match, the request is authentic. If not, reject it with a
401.
Node.js Example
import crypto from "node:crypto";
function verifyWebhookSignature(
rawBody: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Usage in an Express handler
app.post("/webhooks/ploints", (req, res) => {
const signature = req.headers["x-ploints-signature"] as string;
const rawBody = req.body; // raw string (use express.raw())
if (!verifyWebhookSignature(rawBody, signature, process.env.PLOINTS_WEBHOOK_SECRET!)) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = JSON.parse(rawBody);
switch (event.type) {
case "points.earned":
// Handle points earned
break;
case "customer.created":
// Handle new customer
break;
// ... handle other events
}
res.status(200).json({ received: true });
});Retry Policy
If your endpoint returns a non-2xx status code or times out (after 10 seconds), Ploints will retry the delivery with exponential backoff:
- 1st retry: 1 minute after the initial attempt
- 2nd retry: 5 minutes
- 3rd retry: 30 minutes
- 4th retry: 2 hours
- 5th retry: 12 hours (final attempt)
After 5 failed retries the event is marked as failed. You can view and manually resend failed deliveries from the dashboard under Integrations → Webhooks → Delivery Log.
Best Practices
- Always verify signatures -- never trust unverified payloads.
- Respond quickly -- return a
200immediately and process asynchronously if your handler is slow. - Handle duplicates -- use the
idfield to deduplicate in case of retries. - Use HTTPS -- webhook URLs must use HTTPS for security.
Want to manage webhooks programmatically? See the API Reference or use the TypeScript SDK.