data shape underneath depends on which family the event belongs to.
Event families
A few notes on how these map to what you did:
- An operation is never the namespace. A successful
capturemoves a row fromauthorization.*intopayment.*(payment.captured); a failed capture leaves the hold intact and reports underauthorization.*instead (authorization.capture_declined/authorization.capture_failed) — the row never became a payment, so it isn’t reported as one. - Every other operation (
void,refund) reports both its success and its outcome events inside the same namespace it already belongs to. - A transaction moving through settlement (
SUBMITTED→SETTLING→SETTLED) does not re-firepayment.capturedorrefund.capturedat each stage — those statuses all map to the same event you already received. Track a transaction’s progress into settlement viareferences.settlement_batch_idplus the batch-levelsettlement.*event, not a per-stage payment event. chargeback.createdis reserved for a future release. It’s declared so you can see it coming, but it’s never emitted today — don’t build against it yet.
Envelope
Every delivery shares this top-level shape, regardless of family:string
required
Event ID, for example
evt_…. Use this to dedupe retried and duplicate deliveries — see Retries, ordering, and duplicates.string
required
The event type, for example
payment.captured. See Event families for the full catalogue.string
required
Schema version for the
data block. Currently always "v1".object
required
The event body. Shape depends on the family — see Transaction events and Settlement events.
Unknown-field tolerance. Treat every field as optionally growing over time:
RadiumOne may add new fields to
data in a future release without bumping
payload_version. Parse defensively — ignore fields you don’t recognize rather than
rejecting the payload.Transaction events (authorization.*, payment.*, refund.*)
Shared by every authorization.*, payment.*, and refund.* event.
Settlement events
See Settlement and reconciliation for
how these events fit into batch tracking.
Loyalty redemptions
On a paired sale (a card payment with a loyalty redemption), RadiumOne sends one event per redemption sale — the loyalty outcome rides inside that same event’s payload, not as a separateloyalty.* event. The transaction event’s data.transaction.leg tells you which acquirer leg this row settles to (PAYMENT or LOYALTY), and data.transaction.redemption carries the split:
APPROVED: points moved.points_amountwas redeemed from loyalty;card_amountis the residual charged to the card.DECLINED: the loyalty host refused the redemption, or it moved and was later reversed. The payment leg can still be a healthyAUTHORIZED— a declined redemption does not by itself mean the sale failed. See Pay with points for the degraded-sale behavior this reports.NOT_ATTEMPTED: no confirmed redemption — either none was attempted, or its outcome isn’t known yet.
leg and redemption are null on a standalone (non-redemption) sale. A full redemption — points cover the whole sale, no card charge — reports as a single event with leg: "LOYALTY" and data.references.transaction_group_id: null (there’s no paired card leg to group with). If a loyalty leg is later reversed, that reversal is its own separate event, not folded into the original.
Amounts are always integers in the currency’s minor unit. For example,
5000 for SGD means SGD 50.00.Fields RadiumOne doesn’t publish yet
The gateway’s internal event payload also carriesprocessor.rrn, processor.auth_code,
and terminal.acquirer_id. These aren’t published today, pending a decision on exposing
acquirer- and host-level identifiers to merchants. If your integration needs one of
these, contact support rather than relying on an undocumented
field appearing in a future payload — additions here are additive, but nothing
guarantees a specific field ships on a specific date.
Next steps
Verify webhook signatures
Algorithm, key encoding, and generated verifier code.
Retries, ordering, and duplicates
Delivery semantics, the backoff schedule, and endpoint suspension.