Webhook Integration
Receive, verify, and deduplicate WinkPG platform events at your own HTTPS endpoint.
WinkPG delivers asynchronous platform events (card transaction lifecycle, hosted payment page completion, invoice lifecycle, ACH settlement status, merchant creation, and more) to an HTTPS endpoint you control. This guide covers the wire format, the signature scheme, replay protection, and delivery semantics so your receiver validates and deduplicates events correctly.
The authoritative list of event types is the event catalog in the portal (Notifications then Event Types). Every type it lists is one the platform publishes today, and each entry carries a sample payload for that type. Build against the catalog rather than against a type name you have seen elsewhere.
Card transaction lifecycle events
Transaction.Authorized, Transaction.Declined, Transaction.Captured, Transaction.Voided, Transaction.Reversed, Transaction.Failed and Transaction.Settled cover a card payment's progress. Four things to know before wiring order fulfilment to them:
Transaction.Capturedis not a funds signal. Capture marks a transaction ready for settlement; funds move when the batch settles, which is whatTransaction.Settledreports.Transaction.Settledis the funds signal, and it arrives hours later. Card settlement is a batch process, so this event lands well after the capture, and itsOccurredAtUtcis when the row settled rather than when the notification was sent.GatewayBatchIdandProcessorBatchIdlet you reconcile a delivery against a settlement batch. ACH does not settle through this pipeline: useTransaction.AchStatusChangedfor those. If a settlement batch is rolled back and the transaction settles again later, a second event arrives with a different batch id.DeclinedandFailedare different outcomes.Declinedmeans the payment was refused (the issuer, the processor, or a fraud or policy rule said no), so retrying the same card unchanged will not help.Failedmeans the gateway could not process the request at all, so the payment was never decided and a retry may succeed.- A partial reversal is identifiable. On
Transaction.Reversed,IsPartialReversalistrueandAuthorizedAmountminusCumulativeReversedAmountis the amount still authorized. Each reversal of a transaction delivers its own event.
Configuring a destination
A webhook receiver is registered as a destination, scoped to a tenant, reseller, or merchant. Each destination carries:
- URL: the HTTPS endpoint that receives
POSTrequests. - Secret key: a per-destination shared secret used to sign every request. Required for production destinations; optional for sandbox. Stored encrypted at rest.
- Subscriptions: the set of event types the destination wants.
Rotate the destination secret on a defined cadence (90 days is a reasonable default). Rotation is non-disruptive: validate against both the previous and current secret during the rotation window.
Request format
Every delivery is an HTTPS POST with Content-Type: application/json. The body is a JSON envelope:
{
"id": "<unique event id, GUID>",
"type": "HostedPaymentPage.Transaction.Completed",
"version": 1,
"createdUtc": "2026-05-06T18:42:11.193Z",
"tenantId": "<guid>",
"merchantId": "<guid>",
"correlationId": "<guid>",
"data": { }
}
Use the X-WinkPG-Delivery-Id header as your idempotency key: it is identical on every retry and redelivery of the same delivery, and distinct for each endpoint the same event reaches. The envelope id (also sent as X-WinkPG-Event-Id) identifies the underlying event, so key on that one instead when a side effect must run at most once however many of your endpoints receive the event. The correlationId matches the id emitted in WinkPG logs and metrics for the originating operation, which makes cross-system tracing straightforward.
Signature scheme
Every request is signed with HMAC-SHA256 using the destination secret. The signature header is versioned:
X-WinkPG-Signature: v1=sha256:<hex-digest>
The signed input is the timestamp header, a literal ., then the exact raw request body:
"<X-WinkPG-Timestamp>.<raw request body>"
Compute HMAC_SHA256(secret_key, signature_input) and compare it to the received digest using a constant-time comparison (hmac.compare_digest in Python, crypto.timingSafeEqual in Node) to avoid timing attacks. Check the v1=sha256: prefix before parsing the digest; the prefix exists so future algorithm upgrades do not break existing receivers.
Replay protection
Two defenses work together:
- Timestamp validation: reject requests whose
X-WinkPG-Timestampis outside a tolerance window (plus or minus 5 minutes is recommended). The timestamp is part of the signed input, so an attacker cannot alter it without invalidating the signature. - Deduplication: WinkPG delivers at least once, so the same request may be redelivered. Record processed
X-WinkPG-Delivery-Idvalues (a database row keyed on that id, or a RedisSETNXwith a generous TTL) and short-circuit duplicates with200 OK. Store the id in the same transaction as the side effect so a partial failure rolls back both. Headers have never been part of the signed input, so this header is additive and leaves signature verification unaffected.
Response expectations
| Your receiver responds | WinkPG behavior |
|---|---|
2xx |
Success; no retry |
4xx other than 408 or 429 |
Permanent failure; no retry |
408, 429, or 5xx |
Transient; retried with exponential backoff |
| No response within timeout | Transient; retried with exponential backoff |
Aim to acknowledge within 5 seconds. If processing is expensive, validate the signature, enqueue the raw body, return 200 OK, then process asynchronously.
Security checklist
- Verify the signature with a constant-time compare.
- Reject a missing or wrong
v1=sha256:prefix. - Validate the timestamp against the current clock.
- Deduplicate on
X-WinkPG-Delivery-Id(use the event id instead only when a side effect must run at most once across every endpoint receiving the event). - Acknowledge with
2xxonly after the side effect is committed or durably enqueued. - Never log the secret key; rotate it on a cadence.