Webhook API Concepts
Understand webhook registration, event types, delivery mechanics, and security — how mCards notifies partners of real-time events.
Webhook API Concepts
The Webhook API allows partners to register HTTP endpoints that receive real-time event notifications from the mCards platform. Webhooks are used for transaction events, card lifecycle changes, location updates, offer claims, and payment authorization.
Webhook Registration
Each webhook registration includes:
| Field | Description |
|---|---|
webhook_url | The HTTPS endpoint that will receive events |
webhook_name | A human-readable name for the webhook |
webhook_event_type | The type of event this webhook receives |
basic_auth_username | Username for Basic Authentication on delivery |
basic_auth_secret | Password for Basic Authentication on delivery |
hmac_secret | Secret used for HMAC signature verification |
originator_identifier | Optional identifier for the originator |
status | active or inactive |
Each webhook registration is scoped to a single event type. To receive multiple event types, register multiple webhooks.
Event Types
Standard Events
| Event Type | Description | Typical Use |
|---|---|---|
transaction | Card transaction occurred (purchase, refund, etc.) | Loyalty earning, transaction tracking |
location | Location data update | Geofencing, location-based offers |
claimed_offer | Cardholder claimed an offer | Offer fulfillment, engagement tracking |
card_status | A cardholder card's lifecycle state changed | Issuance outcomes, activation state tracking |
Payment Events
Payment events are used for real-time authorization with payment gateway integrations:
| Event Type | Description |
|---|---|
payment | General payment event |
payment.hold | Authorization hold — reserve funds |
payment.capture | Capture — finalize a held transaction |
payment.release_hold | Release a previous authorization hold |
payment.hold_and_capture | Combined hold and capture in a single message |
payment.get_balance | Balance inquiry from the platform |
See Payment API Concepts for the full authorization flow.
Event Type Availability
Not all event types are available to all partners. Availability depends on your integration type:
| Event Type | Card Distribution Partners | Currency Providers | Payment Gateway Providers | Full Webhook Access |
|---|---|---|---|---|
transaction | Yes | Yes | — | Yes |
location | — | Yes | — | Yes |
claimed_offer | — | Yes | — | Yes |
card_status | Yes | — | — | Yes |
payment | — | — | Yes | Yes |
payment.hold | — | — | Yes | Yes |
payment.capture | — | — | Yes | Yes |
payment.release_hold | — | — | Yes | Yes |
payment.hold_and_capture | — | — | Yes | Yes |
payment.get_balance | — | — | Yes | Yes |
Partners with access to multiple APIs receive the union of all event types across their integration types. See API Scoping and Permissions for full details.
Webhook Payload Examples
All webhook deliveries are HTTP POST requests with a JSON body. Every payload includes a webhook_event_type field indicating the event type.
Transaction Event Payload
{
"webhook_event_type": "transaction",
"card_transaction_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"amount": 42.50,
"currency_code": "USD",
"created_at": "2025-03-15T14:22:31.000Z",
"updated_at": "2025-03-15T14:22:31.000Z",
"cardholder_card_uuid": "f9e8d7c6-b5a4-3210-fedc-ba0987654321",
"location": {
"location_uuid": "11223344-5566-7788-99aa-bbccddeeff00",
"name": "Corner Coffee Shop",
"full_address": "123 Main St, Anytown, US 12345",
"latitude": 40.7128,
"longitude": -74.0060
},
"claimed_offer": {
"claimed_offer_uuid": null,
"title": null,
"amount": null,
"offer_uuid": null,
"offer_reward": {
"title": null,
"subtitle": null,
"summary": null,
"amount_type": null,
"amount_currency": null
},
"created_at": null,
"updated_at": null
}
}The claimed_offer object is always present but its fields are null when no offer was redeemed during the transaction.
Location Event Payload
{
"webhook_event_type": "location",
"event": "location",
"source": "gps",
"user_uuid": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"timestamp": "2025-03-15T14:30:00Z",
"data": {
"latitude": 40.7128,
"longitude": -74.0060,
"accuracy": 10.0,
"altitude": 15.2,
"speed": 0.0
}
}Card Status Event Payload
card_status reports card lifecycle progress. It matters most for issuing a card: identity verification runs inline and its results come back in the POST .../issue_card response, but issuer provisioning does not. The 201 means the identity checks completed and a card record exists in the pending state — it is not a guarantee that the card was provisioned or is usable. Without a card_status subscription there is no way to learn the outcome.
{
"webhook_event_type": "card_status",
"event_type": "card_status",
"event_id": "8f3c1e0a-6d1b-4f0e-9a2c-1d0f5b8e7c31",
"previous_status": "issued",
"status": "active",
"timestamp": "2026-05-21T14:35:00Z",
"cardholder": {
"cardholder_uuid": "3f20731f-3cf5-4bf1-80da-0a20a057bcec",
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "+12025551234"
},
"cardholder_card": {
"cardholder_card_uuid": "f9e8d7c6-b5a4-3210-fedc-ba0987654321",
"last_four": "4242"
},
"originator_identifier": "ORD-48213"
}Every delivery carries both webhook_event_type and event_type, each set to card_status.
The event describes a single transition, from previous_status to status:
| Field | Meaning |
|---|---|
event_id | Identifier of the transition, minted when the status changed and unchanged across redeliveries. Use it as your idempotency key |
previous_status | The status the card moved from. null when the card had no previous status, i.e. its first status on creation |
status | The status the card moved to. This is the state as of the transition, not the card's current state — a later transition may already have occurred by the time the delivery arrives |
timestamp | When the card reached this status. Unchanged by a retried delivery |
originator_identifier echoes the reference you supplied on the send_card request that produced this card, so you can tie the card back to your own order or member record without a lookup. It is null for cards that did not come from a send, or when no identifier was supplied.
cardholder_card carries identity only (cardholder_card_uuid, last_four), so that a historical event never contradicts itself. For the card's current activation state, card type and distributor card program, look the card up through the Distributor API.
The status values are:
| Status | Meaning |
|---|---|
pending | Card record created; the issuer has not yet confirmed provisioning |
issued | Provisioned but not activated — reached when the card was issued with auto_activate disabled |
active | Usable. Only active cards can transact |
inactive | Deactivated (locked); can be re-enabled |
issuer_error | The issuer could not provision the card. Carries an error object |
link_failed | Card linking failed (card-linked programs only) |
terminated | Permanently ended — for example the old card of a reissue |
On issuer_error only, an error object is included:
{
"webhook_event_type": "card_status",
"event_type": "card_status",
"event_id": "5c7d2b91-4e83-4a17-b0f2-9c6e1a4d8b30",
"previous_status": "pending",
"status": "issuer_error",
"timestamp": "2026-05-21T14:36:12Z",
"cardholder": {
"cardholder_uuid": "3f20731f-3cf5-4bf1-80da-0a20a057bcec",
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "+12025551234"
},
"cardholder_card": {
"cardholder_card_uuid": "f9e8d7c6-b5a4-3210-fedc-ba0987654321",
"last_four": null
},
"error": {
"message": "Card issuer did not respond within the expected timeframe",
"retryable": false
}
}retryable is false for issuance errors: the card cannot be recovered and needs a new issue request.
Lifecycle paths to expect
These are the paths a card normally takes. Drive your logic off the status value in each delivery rather than off an assumed sequence.
| Scenario | Path |
|---|---|
Issue with auto_activate: true | pending → active |
Issue with auto_activate: false | pending → issued → active (activate via Enable a Card) |
| Deactivate / re-enable | active → inactive → active |
| Issuer failure | pending → issuer_error |
| Card-link failure | pending → link_failed |
| Reissue or closure | any → terminated (permanent) |
Subscription and delivery notes
- One subscription covers every card status change. There is no per-status subscription or filter; a single
card_statuswebhook receives all of them. - Handle deliveries idempotently. Deduplicate on
event_id, which is stable across redeliveries of the same transition, and treat the card's current status from the Distributor API as the source of truth when you need certainty. - You may receive cards you did not just issue. Card lifecycle events are not narrowed to cards you created in the current session: replacement/reissue records under your card programs produce their own deliveries, including
terminatedfor the card being replaced. - Keep your endpoint healthy. Repeated delivery failures pause delivery to that endpoint for a cooldown period. Reconcile card statuses against the Distributor API after any outage of your endpoint.
Claimed Offer Event Payload
{
"webhook_event_type": "claimed_offer",
"card_transaction_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"transaction_amount": 42.50,
"created_at": "2025-03-15T14:22:31.000Z",
"updated_at": "2025-03-15T14:22:31.000Z",
"cardholder_card_uuid": "f9e8d7c6-b5a4-3210-fedc-ba0987654321",
"claimed_offer_state": "hold",
"claimed_offers": [
{
"claimed_offer_uuid": "cc112233-4455-6677-8899-aabbccddeeff",
"title": "10% Off Your Next Purchase",
"amount": 4.25,
"offer_uuid": "dd112233-4455-6677-8899-aabbccddeeff",
"offer_reward": {
"title": "10% Cashback",
"subtitle": "Earn cashback on qualifying purchases",
"summary": "10% cashback reward",
"amount_type": "percentage",
"amount_currency": "USD"
},
"created_at": "2025-03-15T14:22:31.000Z",
"updated_at": "2025-03-15T14:22:31.000Z"
}
],
"location": {
"location_uuid": "11223344-5566-7788-99aa-bbccddeeff00",
"name": "Corner Coffee Shop",
"full_address": "123 Main St, Anytown, US 12345",
"latitude": 40.7128,
"longitude": -74.0060
}
}The claimed_offer_state reflects the transaction lifecycle:
| State | Meaning |
|---|---|
hold | Transaction is authorized (funds held) |
clear | Transaction is posted/cleared (funds moved) |
hold_release | Authorization was reversed or cancelled |
Payment Event Payload
Payment events (payment.hold, payment.capture, etc.) are delivered to payment gateway providers during real-time authorization. See Payment API Concepts for the full authorization flow and expected response format.
Delivery Security
mCards secures webhook deliveries using two mechanisms:
Basic Authentication
Every webhook delivery includes a Basic authorization header using the basic_auth_username and basic_auth_secret you provide during registration. Your endpoint should validate these credentials on every request.
HMAC Signature Verification
Each delivery also includes an HMAC signature computed from the request body using the hmac_secret you provide. To verify:
- Read the raw request body
- Compute
HMAC-SHA256(hmac_secret, request_body) - Compare the computed signature with the one in the
Authorizationheader, which has the formHMAC_SHA256 <webhook_uuid>;<hex-signature> - Reject the request if signatures do not match
This ensures the payload has not been tampered with and originated from mCards.
Delivery Behavior
- Webhooks are delivered as HTTP POST requests to your registered URL
- Your endpoint should respond with a
2xxstatus code to acknowledge receipt - Non-
2xxresponses or timeouts may trigger retries (see Error Handling and Reliability) - Payment authorization webhooks (
payment.*) require a timely response — the cardholder is waiting at the point of sale
Best Practices
- Respond quickly — Especially for payment events, minimize processing time before returning a response
- Validate signatures — Always verify HMAC signatures to ensure request authenticity
- Handle duplicates — Design your handler to be idempotent in case of retried deliveries
- Use HTTPS — All webhook URLs must use HTTPS for secure transport
- Monitor availability — Webhook failures may result in missed events; monitor your endpoint health
Related Guides
- Payment API Concepts — Payment gateway authorization flow
- API Scoping and Permissions — Webhook entitlements by integration type
- API Authentication and Credentials — HMAC authentication for API requests
- Webhook API Reference — Full endpoint documentation
Updated 20 days ago