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:

FieldDescription
webhook_urlThe HTTPS endpoint that will receive events
webhook_nameA human-readable name for the webhook
webhook_event_typeThe type of event this webhook receives
basic_auth_usernameUsername for Basic Authentication on delivery
basic_auth_secretPassword for Basic Authentication on delivery
hmac_secretSecret used for HMAC signature verification
originator_identifierOptional identifier for the originator
statusactive 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 TypeDescriptionTypical Use
transactionCard transaction occurred (purchase, refund, etc.)Loyalty earning, transaction tracking
locationLocation data updateGeofencing, location-based offers
claimed_offerCardholder claimed an offerOffer fulfillment, engagement tracking
card_statusA cardholder card's lifecycle state changedIssuance outcomes, activation state tracking

Payment Events

Payment events are used for real-time authorization with payment gateway integrations:

Event TypeDescription
paymentGeneral payment event
payment.holdAuthorization hold — reserve funds
payment.captureCapture — finalize a held transaction
payment.release_holdRelease a previous authorization hold
payment.hold_and_captureCombined hold and capture in a single message
payment.get_balanceBalance 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 TypeCard Distribution PartnersCurrency ProvidersPayment Gateway ProvidersFull Webhook Access
transactionYesYes—Yes
location—Yes—Yes
claimed_offer—Yes—Yes
card_statusYes——Yes
payment——YesYes
payment.hold——YesYes
payment.capture——YesYes
payment.release_hold——YesYes
payment.hold_and_capture——YesYes
payment.get_balance——YesYes

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:

FieldMeaning
event_idIdentifier of the transition, minted when the status changed and unchanged across redeliveries. Use it as your idempotency key
previous_statusThe status the card moved from. null when the card had no previous status, i.e. its first status on creation
statusThe 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
timestampWhen 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:

StatusMeaning
pendingCard record created; the issuer has not yet confirmed provisioning
issuedProvisioned but not activated — reached when the card was issued with auto_activate disabled
activeUsable. Only active cards can transact
inactiveDeactivated (locked); can be re-enabled
issuer_errorThe issuer could not provision the card. Carries an error object
link_failedCard linking failed (card-linked programs only)
terminatedPermanently 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.

ScenarioPath
Issue with auto_activate: truepending → active
Issue with auto_activate: falsepending → issued → active (activate via Enable a Card)
Deactivate / re-enableactive → inactive → active
Issuer failurepending → issuer_error
Card-link failurepending → link_failed
Reissue or closureany → terminated (permanent)

Subscription and delivery notes

  • One subscription covers every card status change. There is no per-status subscription or filter; a single card_status webhook 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 terminated for 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:

StateMeaning
holdTransaction is authorized (funds held)
clearTransaction is posted/cleared (funds moved)
hold_releaseAuthorization 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:

  1. Read the raw request body
  2. Compute HMAC-SHA256(hmac_secret, request_body)
  3. Compare the computed signature with the one in the Authorization header, which has the form HMAC_SHA256 <webhook_uuid>;<hex-signature>
  4. 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 2xx status code to acknowledge receipt
  • Non-2xx responses 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

  1. Respond quickly — Especially for payment events, minimize processing time before returning a response
  2. Validate signatures — Always verify HMAC signatures to ensure request authenticity
  3. Handle duplicates — Design your handler to be idempotent in case of retried deliveries
  4. Use HTTPS — All webhook URLs must use HTTPS for secure transport
  5. Monitor availability — Webhook failures may result in missed events; monitor your endpoint health

Related Guides


Did this page help you?