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.
| Event | Fires when |
|---|---|
transaction.approved | The processor approved the transaction. |
transaction.declined | The processor declined the transaction. |
transaction.pending | The transaction was accepted but is not yet settled or finalized. |
transaction.errored | The 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
| Field | Description |
|---|---|
id | The transaction ID, prefixed txn_. |
amount | The requested amount, in the currency's minor units. |
currency | ISO currency code. |
fee | Fee applied to the transaction. |
type | The transaction type, such as sale or drawdown. |
createdOn | When the transaction was created, ISO 8601 UTC. |
merchantId | The merchant the transaction belongs to, prefixed mid_. |
platformId | Your platform, prefixed pfm_. |
authorization | The processor's response. See below. |
payment | The payment instrument used. See below. |
processor | id of the merchant's processor connection (prefixed midCon_) and the processor name. |
plan | The processing plan applied to the transaction. |
metadata | Any metadata supplied on the original request, as an object. Present only if metadata was sent. |
authorization
authorization| Field | Description |
|---|---|
status | Preczn's normalized status: A approved, D declined, P pending. |
approvedAmount | The amount actually approved. May be less than amount when partial is true. |
partial | Whether this was a partial approval. |
avs | Address verification result, as returned by the processor. |
cvv | Card security code verification result, as returned by the processor. |
processorCode | The processor's own response code. |
processorMessage | The processor's own response message. |
processorTransactionId | The processor's identifier for the transaction. Use this when reconciling directly with the processor. |
processorCodeandprocessorMessageare passed throughThese 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
payment| Field | Description |
|---|---|
bin | The card's BIN. |
brand | Card brand, such as VISA. |
last4 | Last four digits of the account number. |
type | Instrument type, such as CREDIT. |
token | The payment token ID. Present only if the original transaction request was made with tokenize=true. |
No cardholder dataTransaction 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:
last4is omitted, because there is no card account number.externalLoanIdis added, carrying the lender's identifier for the loan.usedTokenis added and set totrue.
{
"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.
Updated about 2 hours ago
