A webhook is PocketPass telling your system something happened, instead of your system asking us over and over. You give us a URL, we POST to it whenever one of the events below occurs.
The events
| Event | Fires when |
|---|---|
customer.created | A golfer is added, whether by sync, signup page, or by hand |
customer.updated | A golfer's details change |
customer.deleted | A golfer is deleted |
wallet_pass.installed | A pass is added to Apple Wallet or saved to Google Wallet |
house_account.transaction.created | Money moves on a house account |
You choose which events each endpoint receives, so a receiver that only cares about installs isn't woken up by every detail change.
Setting one up
- Open Settings → Developers.
- Add an endpoint: the URL to POST to, and the events it should receive.
- Copy the signing secret shown for that endpoint and store it with your other secrets.
Your endpoint needs to be reachable over HTTPS and should reply 2xx quickly. Do the real work afterwards, not before you reply — a slow endpoint gets treated as a failed one.
Verifying a delivery is genuine
Your webhook URL is open to the internet, so anyone who learns it could POST to it. Every delivery carries a signature that proves it came from PocketPass and that nothing was changed in transit.
The header looks like:
Golfpass-Signature: t=1758592800,v1=<hex hmac>
To verify it:
- Read
t(a Unix timestamp) andv1(the signature) from the header. - Build the signed input: the timestamp, a literal full stop, then the exact raw request body —
t + "." + body. - Compute HMAC-SHA256 over that input using your endpoint's signing secret, and hex-encode it.
- Compare with
v1using a constant-time comparison, not==. - Reject anything where
tis more than about five minutes old, which stops an old delivery being replayed at you.
Sign the raw body exactly as received. If your framework parses the JSON and you re-serialise it before checking, key order or spacing can change and the signature will never match. Capture the raw body first.
When your endpoint is down
Deliveries are retried, so a short outage doesn't lose anything:
- Any reply that isn't
2xx, and any timeout past 10 seconds, counts as a failure. - PocketPass retries up to 5 times, waiting 1 minute, 5 minutes, 30 minutes, 2 hours, then 12 hours — roughly 14 hours of trying.
- After the fifth attempt the delivery is marked failed.
- An endpoint that fails five times in a row is disabled automatically, so one permanently broken receiver doesn't hold up the queue. Fix it, then re-enable it in Settings → Developers.
Designing a receiver that behaves
- Expect duplicates. A retry can arrive after your system already handled the first attempt. Make handling an event twice harmless, keyed on the event's id.
- Don't assume order.
customer.updatedcan arrive beforecustomer.createdif the first delivery was retried. Treat each event as a statement about the current state. - Reply first, work after. Acknowledge with
2xx, then process. Slow processing turns into retries and eventually a disabled endpoint.
Building this for the first time? Point the endpoint at a request-inspection service, trigger a change in the dashboard, and look at a real delivery before you write any verification code.