Skip to main content

Delivery and security

Delivery Payload​

Webhook deliveries are sent as POST requests with a JSON body:

{
"id": "8fd6ce2f-8b55-4b2e-9f8c-5b7f6f0b16f1",
"event": "scan.created",
"timestamp": "2024-12-31T23:59:59.000Z",
"organisation_id": "507f1f77bcf86cd799439010",
"schema_version": "1",
"source": "admin",
"data": {
"scan": {
"id": "507f1f77bcf86cd799439015",
"company": "507f1f77bcf86cd799439010",
"source": "web",
"member": {
"id": "507f1f77bcf86cd799439011",
"name": "John Doe",
"email": "john@example.com",
"memberId": "MEM001"
},
"input": "ABCD1234",
"location": "Main Entrance",
"result": "success",
"reason": "Pass accepted",
"timestamp": "2024-12-31T23:59:59.000Z"
}
}
}

Delivery Headers​

HeaderDescription
Content-TypeAlways application/json
User-AgentRemyPass-Webhooks/1.0
X-RemyPass-SignatureHMAC-SHA256 signature of the raw request body
X-RemyPass-EventEvent name, for example scan.created
X-RemyPass-DeliveryStable event ID, shared across endpoints, retries and replays
X-RemyPass-TimestampEvent occurrence time from the payload

Custom headers configured on the webhook subscription are also included with each delivery. Reserved RemyPass headers and transport headers cannot be overridden. Redirects are not followed.

Resource money amounts are integer minor units with currency codes; percentage configuration values remain percentages. source is admin, member_portal, public_checkout, import, integration, or system. Test events add test: true. Event IDs are opaque strings; do not assume a UUID format.

Signature Verification​

Use the subscription secret to verify the raw request body before processing the event.

const crypto = require("crypto");

function verifyRemyPassWebhook(rawBody, signature, secret) {
if (typeof signature !== "string" || !/^[a-f0-9]{64}$/i.test(signature)) return false;
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");

return crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
}

Retries and Timeouts​

Your endpoint should return any 2xx status quickly after accepting the event. If the endpoint returns a non-2xx status or times out, RemyPass retries delivery with exponential backoff.

Defaults:

SettingDefaultLimit
Delivery timeout30 seconds1 to 300 seconds
Retry delay60 seconds1 to 3600 seconds
Max retries30 to 10

Webhook subscriptions are disabled after 10 consecutive exhausted deliveries. Attempts are stored in MongoDB, processed by a background worker and resumed after restarts. An expired processing lease makes an interrupted delivery eligible for another attempt. Event fan-out is idempotent.

Delivery is at least once once the event has been persisted: deduplicate using id and do not rely on arrival order. A successful request can be repeated if the worker stops before saving its response. Replay preserves the event ID and payload. History and event deduplication records are retained for 90 days.

Application model writes emit events; writes made directly to MongoDB do not. When the application write uses a transaction, its outbox record uses the same transaction, so rolled-back changes do not send events. Non-transactional writes and their outbox writes are separate operations; a process or database failure between them can leave a committed change without a webhook.

See subscription management to send tests, inspect delivery history, and replay events.