Transaction Webhook Payloads

Payload reference for transaction.approved, declined, pending, and errored, including loan drawdowns.

Preczn supports four transaction events. All four carry the same data shape — the transaction itself — so only eventType tells you the outcome.

EventFires when
transaction.approvedThe processor approved the transaction.
transaction.declinedThe processor declined the transaction.
transaction.pendingThe transaction was accepted but is not yet settled or finalized.
transaction.erroredThe transaction failed due to an error rather than a decline.

Payload

The data object closely mirrors the response body of GET Transaction.

{
  "id": "what_6k022ss0jj8vp9g85xv1z357m7",
  "webhookId": "wh_5jg1dx62za981aqkanv3cd99zh",
  "eventType": "transaction.approved",
  "data": {
    "amount": 145,
    "authorization": {
      "approvedAmount": 145,
      "avs": "U",
      "cvv": "M",
      "partial": false,
      "processorCode": "approved_by_network",
      "processorMessage": "Payment complete.",
      "processorTransactionId": "ch_3MeKMMAzKBz0Hugp0OhsoRNi",
      "status": "A"
    },
    "createdOn": "2023-02-22T15:35:57Z",
    "currency": "USD",
    "fee": 0,
    "id": "txn_h1rn8c9nd81y9bkgzax2k2rax",
    "merchantId": "mid_2zyd88xrnr90xskmjmqpd0x1vj",
    "payment": {
      "bin": "432100",
      "brand": "VISA",
      "last4": "0012",
      "type": "CREDIT"
    },
    "plan": {
      "id": "plan_57jaev7pna8nftrkr6xksaastw",
      "name": "Stripe Only Plan"
    },
    "platformId": "pfm_7aj2pxrrcg8zs8x6cxyyrwmyqe",
    "processor": {
      "id": "midCon_6v59stftbs837axmpahj21m8jf",
      "name": "Stripe"
    },
    "type": "sale"
  }
}

Fields

FieldDescription
idThe transaction ID, prefixed txn_.
amountThe requested amount, in the currency's minor units.
currencyISO currency code.
feeFee applied to the transaction.
typeThe transaction type, such as sale or drawdown.
createdOnWhen the transaction was created, ISO 8601 UTC.
merchantIdThe merchant the transaction belongs to, prefixed mid_.
platformIdYour platform, prefixed pfm_.
authorizationThe processor's response. See below.
paymentThe payment instrument used. See below.
processorid of the merchant's processor connection (prefixed midCon_) and the processor name.
planThe processing plan applied to the transaction.
metadataAny metadata supplied on the original request, as an object. Present only if metadata was sent.

authorization

FieldDescription
statusPreczn's normalized status: A approved, D declined, P pending.
approvedAmountThe amount actually approved. May be less than amount when partial is true.
partialWhether this was a partial approval.
avsAddress verification result, as returned by the processor.
cvvCard security code verification result, as returned by the processor.
processorCodeThe processor's own response code.
processorMessageThe processor's own response message.
processorTransactionIdThe processor's identifier for the transaction. Use this when reconciling directly with the processor.
📘

processorCode and processorMessage are passed through

These come straight from the downstream processor and their values and wording differ between processors. Branch your logic on authorization.status, which Preczn normalizes, and treat the processor fields as diagnostic detail to log or display.

payment

FieldDescription
binThe card's BIN.
brandCard brand, such as VISA.
last4Last four digits of the account number.
typeInstrument type, such as CREDIT.
tokenThe payment token ID. Present only if the original transaction request was made with tokenize=true.
📘

No cardholder data

Transaction notifications never contain a full account number, a CVV, or track data — only the BIN, brand, and last four digits.

Loan drawdown transactions

When type is drawdown, the payment object is shaped for a loan rather than a card:

  • last4 is omitted, because there is no card account number.
  • externalLoanId is added, carrying the lender's identifier for the loan.
  • usedToken is added and set to true.
{
  "bin": "432100",
  "brand": "Greensky",
  "type": "CREDIT",
  "externalLoanId": "1234567890",
  "usedToken": true
}

Handle payment.last4 as optional if you process drawdown transactions. See Loan Webhook Payloads for the loan account lifecycle events themselves.

FAQ

Which fields can I rely on always being present?

id, eventType, webhookId, and the transaction's own id are always present. Beyond that, treat fields as optional and code defensively.

Empty values are stripped from the payload before it is sent rather than being included as null or "", so a field that has no value is absent entirely. Read fields with a null-safe accessor rather than assuming the key exists — this is also why payment.last4 simply disappears on drawdown transactions instead of arriving empty.

Why did I get transaction.pending and then another event for the same transaction?

Because the transaction's outcome changed. A transaction accepted but not yet finalized emits transaction.pending, and when it resolves you receive the event matching its final state. Both notifications describe the same data.id.

Since ordering is not guaranteed, do not assume the second notification you receive reflects the later state — compare against what you have stored and treat the transaction's own fields as the source of truth.

Is the payload identical to the GET Transaction response?

It is very close but assembled separately, so occasional differences are possible and new fields may appear in either without notice. Ignore unknown fields rather than failing on them. If you find a discrepancy that affects you, report it to your Preczn support team.

How do I tell a decline from an error?

A decline is the processor refusing the transaction — the request worked and the answer was no, so transaction.declined carries the processor's reason in authorization.processorCode and processorMessage. An error means the transaction could not be completed as an operation at all, and emits transaction.errored.

The practical difference is what to do next: declines are a business outcome to surface to the merchant or customer, errors are a technical failure that may be worth retrying.

Can I get the full card number by subscribing to transaction events?

No. Full account numbers, CVV, and track data are never included in any webhook payload, and there is no configuration that adds them. To charge the same instrument again, tokenize it — request the transaction with tokenize=true and the resulting payment.token can be reused without you storing card data.


Did this page help you?