Prerequisites: Your API key (
zupy_pk_*). See Getting Started if you don’t have these yet.Integrating via OpenDelivery? Partners on the OpenDelivery standard
receive a different outbound surface: the OD
LoyaltyEventEnvelope with X-App-Id /
X-App-MerchantId / X-App-Signature headers (HMAC hex without the sha256= prefix, signed
with your OAuth2 client_secret). This page documents the native webhook format only — don’t
mix the two verification schemes. If you enable both surfaces, you will receive the same fact
twice (e.g. points.earned and loyalty.points.earned); pick one.How It Works
Zupy sends an HTTP POST to your configured webhook URL whenever a loyalty event occurs:- Event Occurs — Customer earns points, redeems a reward, coupon expires, etc.
- Sign — Event payload is signed with HMAC-SHA256 using your secret
- Send — HTTP POST to your
webhook_urlwith signed payload - Retry — If your server returns non-2xx, we retry with exponential backoff
Event Types
Setup
1
Configure your webhook URL
Use the webhook management API to set your endpoint:The response includes your
webhook_secret (masked). You’ll need this for signature verification.2
Select events to receive
Choose which events trigger notifications. Leave empty to receive all events.Available events:
customer.enrolledcustomer.tier_changedpoints.earnedpoints.spentreward.redeemedcoupon.usedcoupon.cancelledcoupon.expiringcoupon.expired
3
Test your integration
Send a test ping to verify connectivity:Response:
4
Verify signatures
Implement signature verification in your webhook handler (see below).
Payload Format
Every webhook POST contains a JSON payload with this structure:Headers
Signature Verification
Verify the payload authenticity using HMAC-SHA256. This prevents spoofed requests.Important: Use the raw request body bytes for verification — not the parsed JSON. The signature is computed over the exact bytes received.
Retry Behavior
If your server returns a non-2xx status code or times out, Zupy retries delivery:- Max delivery attempts: 4 (1 initial + 3 retries)
- Timeout: 10 seconds per attempt
- Backoff: Exponential base-4 (1s, 4s, 16s)
- After exhaustion: Event is marked as
exhaustedand not retried again
Idempotency
Use theX-Webhook-Id header to handle duplicate deliveries:
The same event may be delivered multiple times (due to retries). Always check
X-Webhook-Id before processing.Event Payloads
customer.enrolled
customer.enrolled
Triggered when a new customer joins a loyalty program.
points.earned
points.earned
Triggered when a customer’s points balance increases.
reward.redeemed
reward.redeemed
Triggered when a customer redeems a reward (coupon created).
coupon.used
coupon.used
Triggered when a coupon is validated or used at a store.
coupon.expiring
coupon.expiring
Triggered 3 days before a coupon expires.
customer.tier_changed
customer.tier_changed
Triggered when a customer moves loyalty tier.
points.spent
points.spent
Triggered when a customer’s points balance decreases.
coupon.cancelled
coupon.cancelled
Triggered when a coupon is cancelled.
coupon.expired
coupon.expired
Triggered when a coupon passes its expiration date.
Best Practices
Best Practices & FAQ
Best Practices & FAQ