Webhooks
Receive HTTPS POSTs when passes are created, updated, revoked or when messages are sent. Webhook payloads are HMAC-SHA256 signed.
Event types
| event | Fired when |
|---|---|
| pass.created | POST /passes returns 202. |
| pass.updated | PATCH /passes/{id} returns 202. |
| pass.revoked | DELETE /passes/{id} returns 202. |
| message.sent | POST /messages returns 202. |
| job.completed | (Reserved โ worker-side trigger pending.) |
| job.failed | (Reserved โ worker-side trigger pending.) |
| batch.completed | (Reserved โ worker-side trigger pending.) |
Register a Webhook
POST
/api/v1/webhooks
POST
/api/v1/webhooks
{
"url": "https://yourdomain.com/wallet-events",
"events": ["pass.created", "pass.updated"],
"secret": "whsec_your_secret_here"
}
โ 201 Created
{
"id": "...",
"url": "https://yourdomain.com/wallet-events",
"events": ["pass.created", "pass.updated"],
"is_active": true,
"secret_prefix": "whse",
"created_at": "2026-05-18T10:00:00Z"
}
URL must be HTTPS only. The full secret is never returned โ only its 4-char prefix.
Signature verification
Every webhook delivery carries an HMAC-SHA256 signature of the raw request body (computed with the secret you supplied):
Header
X-WalletPlatform-Signature: sha256={hex}
Header
X-WalletPlatform-Event: pass.created
Header
X-WalletPlatform-Delivery: {delivery-id}
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody));
var expected = Convert.ToHexString(hash).ToLowerInvariant();
var header = request.Headers["X-WalletPlatform-Signature"]
.ToString().Split('=', 2)[1];
if (!CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected),
Encoding.UTF8.GetBytes(header)))
{
return Unauthorized();
}import crypto from 'node:crypto';
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const header = req.headers['x-walletplatform-signature']
.split('=', 2)[1];
if (!crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(header, 'hex'))) {
return res.status(401).end();
}import hmac, hashlib
expected = hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
header = request.headers['X-WalletPlatform-Signature'].split('=', 1)[1]
if not hmac.compare_digest(expected, header):
return Response(status=401)$expected = hash_hmac('sha256', $rawBody, $secret);
$header = explode('=', $_SERVER['HTTP_X_WALLETPLATFORM_SIGNATURE'], 2)[1];
if (!hash_equals($expected, $header)) {
http_response_code(401);
exit;
}Delivery retries
A 2xx response is required within 30 seconds. Failures (non-2xx, timeout, exception) trigger exponential-backoff retries:
- Attempt 1: immediate
- Attempt 2: after 1 minute
- Attempt 3: after 5 minutes
- Attempt 4: after 30 minutes
- Attempt 5: after 2 hours
After 5 failed attempts the delivery is dead-lettered. Inspect the history via:
GET
/api/v1/webhooks/{id}/deliveries
Other endpoints
GET
/api/v1/webhooksGET
/api/v1/webhooks/{id}PATCH
/api/v1/webhooks/{id}DELETE
/api/v1/webhooks/{id}