View as Markdown

llms.txt

This guide isn't available right now

This instance couldn't load its guide catalog. The guide returns as soon as the catalog is readable again.

Back to the guides

No such guide

This instance publishes no guide under that address. It may have been renamed, or it may belong to a feature this installation hasn't enabled.

Back to the guides

That guide is part of the product documentation

This guide is written for someone operating WinkPG through its screens rather than integrating against it, so it lives in the application's own help section instead of here. Sign in to WinkPG and open Help to read it.

Back to the guides

Guides Integration

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. Seven things to know before wiring order fulfilment to them:

  • Transaction.Captured isn't a funds signal. Capture marks a transaction ready for settlement; funds move when the batch settles, which is what Transaction.Settled reports.
  • One payment keeps one TransactionId. A capture updates the original transaction in place rather than creating a new one. An authorization that's captured and later settles therefore raises Transaction.Authorized, Transaction.Captured and Transaction.Settled, all carrying the id you received at authorization. A refund is a separate transaction with its own id.
  • Transaction.Settled is the funds signal, and it arrives hours later. Card settlement is a batch process, so this event lands well after the capture, and its OccurredAtUtc is when the row settled rather than when the notification was sent. ClosedSettlementBatchId, GatewayBatchId and ProcessorBatchId let you reconcile a delivery against a settlement batch. ACH doesn't settle through this pipeline: use Transaction.AchStatusChanged for those. If a settlement batch is rolled back and the transaction settles again later, a second event arrives with a different batch id.
  • Group settled events on ClosedSettlementBatchId. It's the settlement batch's own identifier. GatewayBatchId identifies the settlement run, which closes one batch per processor, so it can't separate two batches settled in the same run. ClosedSettlementBatchId is null on a settlement applied outside the normal batch-close path and on transactions settled before the field was published; fall back to the GatewayBatchId and ProcessorBatchId pair when it's absent.
  • Declined and Failed are different outcomes. Declined means the payment was refused (the issuer, the processor, or a fraud or policy rule said no), so retrying the same card unchanged won't help. Failed means the gateway couldn't process the request at all, so the payment was never decided and a retry may succeed.
  • A partial reversal is identifiable. On Transaction.Reversed, IsPartialReversal is true and AuthorizedAmount minus CumulativeReversedAmount is the amount still authorized. Each reversal of a transaction delivers its own event.
  • A partial approval has its own event. Subscribe to Transaction.PartiallyApproved to hear when the issuer approves less than you asked for. It carries RequestedAmount, AuthorizedAmount, BalanceDue and the Disposition the gateway resolved (Accepted, Voided, or Pending while an operator decides), and it isn't sent again when a Pending disposition moves. When the gateway voids a partial approval on your behalf, Transaction.Voided carries a VoidReasonCode: PARTIAL_APPROVAL_ACKNOWLEDGMENT_TIMEOUT when nobody accepted the reduced amount before its deadline, PARTIAL_APPROVAL_AUTO_VOID when your strategy voids partial approvals automatically, PARTIAL_APPROVALS_NOT_ACCEPTED when you don't accept partial approvals, and PARTIAL_APPROVAL_VOIDED otherwise. Every other void carries null.

Configure 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 POST requests.
  • 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's 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.

correlationId is your own value, echoed back

When your API request carries an X-Correlation-Id header, WinkPG adopts that value as the correlation id for the operation and returns it here on every event that operation raises. WinkPG generates a GUID only when you send no header.

That makes the field yours on both sides, so treat it as data you own:

  • Send nothing personal or sensitive in it. Whatever you send is stored on the delivery record and delivered to every endpoint subscribed to the event, including endpoints owned by another party in your account hierarchy. An order number or an internal request id is a good value; a customer name, email address, or account number isn't.
  • Keep it short and printable. WinkPG normalizes the value before storing or delivering it: surrounding whitespace is trimmed and control characters are removed. A value longer than 128 characters is omitted rather than shortened, so correlationId arrives as null and you lose the trace. Shortening it would be worse: you would receive an id that looks real, matches nothing, and can't be told apart from one you sent.
  • Don't treat it as unique. Nothing stops two operations from carrying the same correlation id, because nothing but your own client chooses it. Use X-WinkPG-Delivery-Id for idempotency and the envelope id for event identity.

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 don't break existing receivers.

The header can carry more than one signature

The header holds one entry per secret the endpoint is currently signed with, separated by commas:

X-WinkPG-Signature: v1=sha256:<current-digest>,v1=sha256:<incoming-digest>

That's one entry in normal operation and two while the endpoint's secret is being rotated. One X-WinkPG-Timestamp covers every entry, and the signed input is the same for each; only the key differs.

Split the header on commas, trim each entry, check its prefix, and accept the delivery when any entry matches your secret. Skip an entry whose prefix you don't recognize instead of rejecting the whole delivery. A receiver that assumes a single value, or that hex-decodes everything after the prefix without splitting first, rejects every delivery for the length of a rotation.

Rotating the signing secret

A secret is replaced through an overlap window, so your receiver is never locked out mid-cutover.

Update your receiver to read multiple signature values before the rotation is started. From the moment the overlap opens the header carries two entries. A receiver that reads only one rejects every delivery, and WinkPG stops delivering to an endpoint after three authentication failures inside fifteen minutes, so a mis-sequenced rotation takes your endpoint out of service until an administrator clears the suppression.

  1. Deploy verification code that splits the header, and confirm it's live on every instance. Deliveries still carry one entry at this point, so this is safe to do at any time.
  2. An administrator starts the rotation. The new secret is shown once and is never returned again. Deliveries begin carrying both signatures, the current secret's first.
  3. Configure the new secret on your receiver, instance by instance. Both signatures are present, so no coordinated cutover is needed.
  4. The administrator promotes the rotation. The new secret becomes the only signing secret, and deliveries return to a single entry.

If you can't be ready in time, ask for the rotation to be cancelled rather than promoted: the new secret is discarded and your current secret keeps working.

Replay protection

Two defenses work together:

  1. Timestamp validation: reject requests whose X-WinkPG-Timestamp is outside a tolerance window (plus or minus 5 minutes is recommended). The timestamp is part of the signed input, so an attacker can't alter it without invalidating the signature.
  2. Deduplication: WinkPG delivers at least once, so the same request may be redelivered. Record processed X-WinkPG-Delivery-Id values (a database row keyed on that id, or a Redis SETNX with a generous TTL) and short-circuit duplicates with 200 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.

Recovery of a missed card event

Card lifecycle events (the Transaction.* family) are raised on a best-effort path inside the payment pipeline. WinkPG never fails or delays a payment because a notification couldn't be raised, so an event can be missed at the moment of the transition.

A missed card event is re-emitted by a daily reconciliation and in almost all cases reaches your endpoint on the next run, as long as the transition happened within the last 48 hours. Treat it as a safety net rather than a delivery guarantee: recovery is bounded by that window, and a re-emission can itself fail and be retried on the following run. The re-emitted event carries the same X-WinkPG-Event-Id as the original and reports when the transition actually happened, so a receiver that deduplicates as described above needs no extra handling. Plan for two things: a recovered event can arrive after later events on the same transaction, and it carries no correlationId. Read the state in the payload rather than inferring it from the order events arrive in.

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.
  • Split the signature header on commas and accept a match on any entry, so a secret rotation doesn't lock you out.
  • Reject a delivery where no entry carries a recognized 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 2xx only after the side effect is committed or durably enqueued.
  • Never log the secret key; rotate it on a cadence.

Reconnecting to the server

Could not reconnect

This session has ended

Attempt 1

Your work on this page is still here. Retrying keeps it; reloading starts the page again.

The server no longer holds this page's state, so it has to be loaded again.