Mintcash
ConceptsWebhooks

Payload reference

The JSON shape of every webhook envelope and entity. Use this to write strongly-typed handlers.

Every webhook MintCash sends shares a common envelope. The variable part is data, whose shape is determined by type.

Envelope

FieldTypeNotes
eventIdstring (UUID)Stable across retries of the same event. Use as your dedupe key.
eventstringThe transition: payment.succeeded, subscription.failed, etc.
typestringDetermines the shape of data: payment, subscription, or refund.
apiVersionstringWebhook schema version (2026-05-01).
environmentstringlive or test.
createdAtstring (ISO8601)When the event was emitted.
dataobjectThe entity. Shape depends on type.

Payment payload (type: "payment")

Fires for: payment.pending, payment.authorized, payment.succeeded, payment.failed.

{
  "eventId": "01HXX...",
  "event": "payment.succeeded",
  "type": "payment",
  "apiVersion": "2026-05-01",
  "environment": "live",
  "createdAt": "2026-05-22T14:32:08.412Z",
  "data": {
    "id": "pmt_01HXX...",
    "externalId": "order_123",
    "status": "succeeded",
    "amount": 49.99,
    "currency": "USD",
    "providerTransactionId": "provider_txn_abc",
    "processor": "corefy",
    "processorTransactionId": "processor_txn_abc",
    "processorMerchantId": "processor_mid_abc",
    "instrument": "card",
    "failureCode": null,
    "failureMessage": null,
    "amountRefunded": 0,
    "amountRefundable": 49.99,
    "customer": {
      "id": "cust_01HXX...",
      "externalId": "cust_42",
      "email": "buyer@example.com",
      "name": "Jane Doe",
      "phone": "+15551234567"
    },
    "billingAddress": {
      "country": "US",
      "postalCode": "10001",
      "name": "Jane Doe",
      "line1": "1 Madison Ave",
      "line2": null,
      "city": "New York",
      "state": "NY"
    },
    "card": {
      "brand": "mastercard",
      "first6": "555544",
      "last4": "0000",
      "mask": "555544******0000",
      "fingerprint": "9f2b1c7e04d8a3f6b5e1c0d9a8b7f6e5d4c3b2a1908f7e6d5c4b3a2918f7e6d5",
      "expMonth": "12",
      "expYear": "2030",
      "saved": true,
      "token": "tok_01HXX..."
    }
  }
}
type Customer = {
  id: string;
  externalId: string;
  email: string;
  name: string;
  phone: string | null;
};

type Address = {
  country: string;
  postalCode: string;
  name: string | null;
  line1: string | null;
  line2: string | null;
  city: string | null;
  state: string | null;
};

type Card = {
  brand: string;
  first6: string;
  last4: string;
  mask: string;          // already-masked PAN, e.g. 555544******0000
  fingerprint: string | null; // same card → same value within your account; not a credential
  expMonth: string;
  expYear: string;
  saved: boolean;        // true when the customer consented to save this card
  token: string | null;  // reusable card token — non-null only when saved is true
};

type PaymentData = {
  id: string;
  externalId: string;
  status:
    | "pending"
    | "authorized"
    | "succeeded"
    | "failed"
    | "refunded"
    | "partially_refunded";
  amount: number;            // major units
  currency: string;
  providerTransactionId: string | null;
  processor: string;         // which payment provider ran the charge
  processorTransactionId: string | null;  // acquirer's own ID for the charge
  processorMerchantId: string | null;     // merchant ID (MID) at the acquirer
  instrument: "card" | "apple_pay" | "google_pay" | null;
  failureCode: string | null;
  failureMessage: string | null;
  amountRefunded: number;
  amountRefundable: number;
  customer: Customer | null;
  billingAddress: Address | null;  // null when no address was captured (Google Pay, card-only checkout)
  card: Card | null;         // null until the PSP confirms the charge
};

type PaymentEvent = {
  eventId: string;
  event:
    | "payment.pending"
    | "payment.authorized"
    | "payment.succeeded"
    | "payment.failed";
  type: "payment";
  apiVersion: string;
  environment: "live" | "test";
  createdAt: string;
  data: PaymentData;
};

Card fingerprint

card.fingerprint identifies the card number. The same card produces the same fingerprint across all your payments and customers, so you can use it to spot one card behind several of your accounts, or to avoid storing a card twice when a customer saves it again.

Fingerprints are scoped to your merchant account. The same card has a different fingerprint at any other merchant, so don't compare them across accounts.

It is not a credential and can't be charged, so unlike card.token it is sent whether or not the customer saved the card. For Apple Pay and Google Pay it identifies the wallet's device token, not the physical card, so the same card in a wallet and typed at checkout will have different fingerprints.

Refund payload (type: "refund")

Fires for: payment.partially_refunded, payment.refunded, payment.refund_failed. The envelope type is "refund": data is the refund, and the payment it was issued against is nested at data.payment (the full payment shape from above).

{
  "eventId": "01HXX...",
  "event": "payment.refunded",
  "type": "refund",
  "apiVersion": "2026-05-01",
  "environment": "live",
  "createdAt": "2026-05-22T16:10:11.000Z",
  "data": {
    "id": "rfd_01HXX...",
    "externalId": "refund_order_123_full",
    "status": "succeeded",
    "amount": 49.99,
    "currency": "USD",
    "providerRefundId": "provider_rfn_abc",
    "failureCode": null,
    "failureMessage": null,
    "payment": {
      "id": "pmt_01HXX...",
      "externalId": "order_123",
      "status": "refunded",
      "amount": 49.99,
      "currency": "USD",
      "providerTransactionId": "provider_txn_abc",
      "processor": "corefy",
      "processorTransactionId": "processor_txn_abc",
      "processorMerchantId": "processor_mid_abc",
      "instrument": "card",
      "failureCode": null,
      "failureMessage": null,
      "amountRefunded": 49.99,
      "amountRefundable": 0,
      "customer": {
        "id": "cust_01HXX...",
        "externalId": "cust_42",
        "email": "buyer@example.com",
        "name": "Jane Doe",
        "phone": "+15551234567"
      },
      "billingAddress": {
        "country": "US",
        "postalCode": "10001",
        "name": "Jane Doe",
        "line1": "1 Madison Ave",
        "line2": null,
        "city": "New York",
        "state": "NY"
      },
      "card": {
        "brand": "mastercard",
        "first6": "555544",
        "last4": "0000",
        "mask": "555544******0000",
        "fingerprint": "9f2b1c7e04d8a3f6b5e1c0d9a8b7f6e5d4c3b2a1908f7e6d5c4b3a2918f7e6d5",
        "expMonth": "12",
        "expYear": "2030",
        "saved": false,
        "token": null
      }
    }
  }
}

The refund object carries providerRefundId, failureCode, and failureMessage; on a payment.refund_failed event the failure fields describe the refund (the nested payment keeps its own status). There is no reason or createdAt field on the webhook refund object.

Subscription payload (type: "subscription")

Fires for: subscription.succeeded, subscription.failed, subscription.cancelled, subscription.payment_method_updated. The customer, billing address, and card live on the nested payment object — not at the subscription root. The billing schedule (phases) is returned by the REST API, not on the webhook.

{
  "eventId": "01HXX...",
  "event": "subscription.succeeded",
  "type": "subscription",
  "apiVersion": "2026-05-01",
  "environment": "live",
  "createdAt": "2026-05-22T08:00:00.000Z",
  "data": {
    "id": "sub_01HXX...",
    "externalId": "sub_cust42_plan_pro",
    "status": "active",
    "amount": 19.99,
    "currency": "USD",
    "billingIntervalDays": 30,
    "currentPeriodStart": "2026-05-22T08:00:00.000Z",
    "currentPeriodEnd": "2026-06-21T08:00:00.000Z",
    "cancelledAt": null,
    "paymentMethod": null,
    "payment": {
      "id": "pmt_01HXX...",
      "externalId": "sub_initial_sub_cust42_plan_pro",
      "status": "succeeded",
      "amount": 19.99,
      "currency": "USD",
      "providerTransactionId": "provider_txn_abc",
      "processor": "corefy",
      "processorTransactionId": "processor_txn_abc",
      "processorMerchantId": "processor_mid_abc",
      "instrument": "card",
      "failureCode": null,
      "failureMessage": null,
      "amountRefunded": 0,
      "amountRefundable": 19.99,
      "customer": {
        "id": "cust_01HXX...",
        "externalId": "cust_42",
        "email": "buyer@example.com",
        "name": "Jane Doe",
        "phone": "+15551234567"
      },
      "billingAddress": {
        "country": "US",
        "postalCode": "10001",
        "name": "Jane Doe",
        "line1": "1 Madison Ave",
        "line2": null,
        "city": "New York",
        "state": "NY"
      },
      "card": {
        "brand": "mastercard",
        "first6": "555544",
        "last4": "0000",
        "mask": "555544******0000",
        "fingerprint": "9f2b1c7e04d8a3f6b5e1c0d9a8b7f6e5d4c3b2a1908f7e6d5c4b3a2918f7e6d5",
        "expMonth": "12",
        "expYear": "2030",
        "saved": true,
        "token": "a1b2c3d4-..."
      }
    }
  }
}

cancelledAt is null until the subscription is cancelled, then an ISO 8601 timestamp. status reflects the subscription's own state (created, pending, active, past_due, cancelled, or failed), independent of the nested payment's status.

paymentMethod (on subscription.payment_method_updated only)

Every other subscription event carries paymentMethod: null. When a customer settles a failed renewal through a card-update link, the subscription moves onto the card they just paid with and this object describes the swap:

{
  "event": "subscription.payment_method_updated",
  "type": "subscription",
  "data": {
    "id": "sub_01HXX...",
    "status": "active",
    "paymentMethod": {
      "brand": "visa",
      "first6": "411111",
      "last4": "1111",
      "mask": "411111******1111",
      "fingerprint": "c7df64536470547ef1b9a16358f4f58c62cfb89d6c25dc876610bc7402429f0d",
      "expMonth": "04",
      "expYear": "2031",
      "saved": true,
      "token": "e5f6g7h8-...",
      "previousToken": "a1b2c3d4-..."
    }
  }
}

token is the new card, previousToken the one the subscription billed before the swap — match on it to update your own stored copy. previousToken is null when the card being replaced had no token you were ever given — the customer never consented to saving it. The whole object is null while the new card is still unconfirmed.

The old card is not deleted: payment methods are shared across the customer's other subscriptions and saved-card charges, so only this subscription moves.

currentPeriodStart / currentPeriodEnd describe the period the customer has paid for — the entitlement they currently hold — not the next scheduled charge. On a renewal event, currentPeriodEnd is when the next renewal is due.