Merchant Webhook Payloads
Payload reference for merchant.created, updated, and deleted.
Preczn supports three merchant events. All three carry the same data shape — the full merchant record — so eventType is what tells you which happened.
| Event | Fires when |
|---|---|
merchant.created | A merchant record is created, whether through the API, the dashboard, or an import from a processor. |
merchant.updated | A merchant record is modified. |
merchant.deleted | A merchant record is deleted, whether by API request or by a dashboard user. |
Payload
data contains the entire merchant record — business details, owners, banking, and processor connections — not a diff of what changed. It should match the GET Merchant response.
| Field group | Contents |
|---|---|
| Identity | id, name, legalName, active, plan, labels |
| Business details | email, phone, website, businessType, industry, mcc, tin / tinType, incorporation and establishment details, volume and ticket figures |
| Addresses | address, mailingAddress, billingContact |
owners[] | Each beneficial owner, with identity, contact, address, ownership percentage, and role flags |
banking[] | Each bank account, with masked routing and account, type, and whether it is primary |
connections[] | Each processor connection, with status, processor, and any missingRequiredData |
| Audit | modifiedOn, modifiedBy |
Notable fields
| Field | Description |
|---|---|
id | The merchant's ID, prefixed mid_. |
active | Whether the merchant is enabled in Preczn. Inactive merchants cannot transact. Defaults to true on creation unless explicitly set otherwise. |
modifiedBy | What performed the action — User:{email} for a dashboard user, or an API key descriptor such as API Key: Default. |
connections[].status | The processor connection's onboarding status. This, not active, is what tells you whether a merchant can process through a given processor. |
connections[].missingRequiredData | Field paths still outstanding for that connection. |
Sensitive fields are masked, and the shape can driftIdentity and account values are masked —
tin,ssn,dob,driversLicenseNumber, bankroutingandaccountarrive partially redacted, typically as last-four only. Full values are never sent.Webhook payloads are assembled separately from the Merchant API, so slight differences are possible and new fields may be added without notice. Ignore unknown fields rather than failing on them, and report any discrepancy that affects you to your Preczn support team.
Example
The example below is a merchant.updated notification. merchant.created and merchant.deleted have the same structure with a different eventType.
Example merchant payload
{
"id": "what_test_4tbkqf1new9avsxe6mz85a5kfy",
"webhookId": "wh_test_57vn6accfv9hrb5kqehv3k47tx",
"eventType": "merchant.updated",
"data": {
"id": "mid_test_wsqgngrn99gptndb1h2s70dzj",
"active": true,
"plan": "",
"name": "Documentation Corp",
"legalName": "Documentation Corp LLC",
"address": {
"address": "Random Street 1",
"address2": "Random Street 2",
"city": "San Francisco",
"region": "CA",
"postal": "12345",
"country": "USA"
},
"email": "[email protected]",
"phone": "18175698900",
"tin": "****6789",
"tinType": "ein",
"website": "https://www.domain.com",
"mailingAddress": {
"address": "Mailing Address 1",
"address2": "Mailing Address 2",
"city": "San Francisco",
"region": "CA",
"postal": "93000",
"country": "USA"
},
"billingContact": {
"address": "Billing Address 1",
"address2": "Billing Address 2",
"city": "San Francisco",
"region": "CA",
"postal": "93000",
"country": "USA",
"firstName": "Jack",
"lastName": "Harbor",
"email": "[email protected]",
"phone": "18175698900"
},
"businessType": "corp",
"publicCompany": false,
"isBusinessAddress": true,
"industry": "7371",
"stateIncorporated": "CA",
"countryIncorporated": "USA",
"dateEstablished": "1990-10-22",
"customerServicePhone": "18175698900",
"productServiceDescription": "Api Product service description",
"mcc": "7372",
"annualSaleVolume": 20000,
"averageTicket": 3000,
"shipToDays": 7,
"modifiedOn": "2024-01-29T20:01:12Z",
"modifiedBy": "API Key: Default",
"owners": [
{
"firstName": "John",
"middleName": "",
"lastName": "Doe",
"email": "[email protected]",
"phone": "13133204043",
"address": {
"address": "Owner Address 1",
"address2": "Owner Address 2",
"city": "Vancouver",
"region": "BC",
"postal": "A1A1A1",
"country": "CAN"
},
"id": "midOwn_test_5n13b89qj89w2bckk53p50sk7h",
"businessTitle": "CEO",
"citizenshipCountry": "CAN",
"dob": "1986****",
"ssn": "********",
"ownershipPercent": 100,
"type": "Owner",
"politicallyExposed": false,
"primaryRepresentative": true,
"driversLicenseNumber": "****6789",
"driversLicenseState": "BC",
"driversLicenseExpiration": "2006-01-01",
"significantResponsibility": false,
"connectionID": {}
}
],
"banking": [
{
"id": "midBank_test_33892pgvpg9nr9tnkx448vek5c",
"accountName": "First Regional Bank",
"accountType": "personalChecking",
"routing": "****3004",
"account": "****6667",
"primaryAccount": true,
"nameOnAccount": "Merchant Name LLC",
"connectionID": {}
}
],
"connections": [
{
"id": "midCon_test_2sga5q5zxd8qnsha0zvqdg0vyh",
"status": "Requirements",
"processor": "StripeConnect",
"missingRequiredData": [
"connectionspecificRequirements.tosDate",
"connectionspecificRequirements.tosIp"
]
},
{
"id": "midCon_test_3cv1q7vm6k8gftc8hyb3hrq6zq",
"status": "Unconfigured",
"processor": "AffiniPay"
},
{
"id": "midCon_test_6k9k4fh3ta8y6bp3bt113ze44q",
"status": "Ready",
"processor": "Payrix",
"missingRequiredData": [],
"connectionspecificRequirements": {
"tosDate": "2023-12-12",
"tosIp": "73.33.94.202",
"tosVersion": "12345",
"signingContact": {}
},
"actionDescription": "Sample Message",
"actionRequired": true,
"actionDescriptionReviewed": false
}
]
}
}FAQ
How do I tell what actually changed on a merchant.updated?
You cannot from the notification alone — the payload is the complete record, not a diff, and there is no field naming what changed. Compare the payload against your own stored copy to compute the delta.
This is the main argument for storing the merchant record on your side. It also lets you ignore notifications where nothing you care about moved, which matters because merchant.updated fires on every modification including ones irrelevant to your integration.
Why am I getting so many merchant.updated notifications?
Because any modification emits one, including changes made by Preczn's own onboarding processes rather than by you — connection status transitions, requirement evaluation, and data imported from a processor all count. During active onboarding a single merchant can produce a burst of them.
Deduplicate against your stored copy and act only on the fields you use.
Does active: true mean the merchant can process transactions?
No. active is the merchant's enable flag in Preczn and defaults to true, so a brand-new merchant is normally active before any processor has approved it. An inactive merchant definitely cannot transact, but an active one is not necessarily ready either.
Readiness is per-processor and lives in connections[].status. Watch that array rather than active to decide when to let a merchant start processing — see Connection Status Reference and Merchant Lifecycle.
Can I subscribe to connection status changes specifically?
Not as a separate event. Connection status lives in connections[].status on the merchant.updated payload, so subscribe to merchant.updated and watch that array for transitions.
Does merchant.deleted include the merchant's data?
Yes — the full record, the same shape as the other merchant events, captured as it was at deletion. You do not need to have stored it beforehand to know what was removed.
Does merchant.created fire for merchants imported from a processor?
Yes. Merchants created by importing existing accounts from a processor emit merchant.created the same as merchants created through the API or dashboard. A bulk import can therefore produce a large burst of notifications at once — make sure your endpoint tolerates that volume without crossing the 5-second timeout.
Updated about 1 hour ago
