Notifications
Webhooks
Outbound merchant notifications and inbound KYC provider callbacks. JSON bodies. No PAN or CVV. Not live acquiring.
Merchant notification URL
Set on My Profile. Must be http:// or https:// (HTTPS preferred) or blank to disable. The sandbox POSTs Content-Type: application/json and waits up to 5 seconds. There is no retry queue.
Sale captured/declined and refund created use the same sale shape (TXNTYPE=NEWORDER) plus HASH from the merchant secret. See HASH.
Staff email, SMS, and WhatsApp
When a sale becomes Captured, Mr Ivory can also notify merchant staff by email, SMS, and WhatsApp. This is extra fan-out next to the webhook. It fires once per TXN_ID and does not re-send on status refresh. Shopper receipts are out of scope.
Toggles and destinations are on My Profile. Email defaults on (sign-in email). SMS and WhatsApp default off until an E.164 number is saved. Platform env: SENDGRID_API_KEY, TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_SMS_FROM, TWILIO_WHATSAPP_FROM. Missing channel secrets skip that channel and log; they never fail the payment.
Live WhatsApp needs a Meta-approved utility template before production. Delivery is never assumed if the template or messaging credentials are missing. Staff copy is short: You have just been paid USD 1.00. Email subject matches; the body may add the order id in a tiny footer. Never PAN or CVV.
| Field | Meaning |
|---|---|
APP_ID | Merchant id |
TXN_ID, ORDER_ID | Sale or refund ids |
STATUS, RESPONSE_CODE, RESPONSE_MESSAGE | Same success rule as the APIs |
AMOUNT | Minor units (integer string) |
AMOUNT_DECIMAL | Major units for display |
CURRENCY_CODE / CURRENCY_CODE_ALPHA | Numeric and alpha |
CUST_EMAIL, CUST_NAME, address fields | From the sale — no card number |
HASH | Signed over the payload fields |
{
"APP_ID": "1000221129001154",
"TXN_ID": "16-digit id",
"ORDER_ID": "S2S-001",
"STATUS": "Captured",
"RESPONSE_CODE": "000",
"AMOUNT": "1000",
"AMOUNT_DECIMAL": "10.00",
"CURRENCY_CODE": "784",
"CURRENCY_CODE_ALPHA": "AED",
"TXNTYPE": "NEWORDER",
"CUST_EMAIL": "buyer@example.test",
"HASH": "…"
}Recurring events
Same merchant URL. These bodies include EVENT and HASH. They never include PAN.
| EVENT | When |
|---|---|
recurring.invoice.paid | Recurring charge captured |
recurring.invoice.failed | Recurring charge declined or failed |
subscription.cancelled | Subscription cancelled |
subscription.paused | Subscription paused |
Shared fields: APP_ID, subscriptionId, planId, invoiceId, amount (minor), currency, status, txnId, failureMessage, failCount, RESPONSE_DATE_TIME, HASH.
{
"EVENT": "recurring.invoice.paid",
"APP_ID": "1000221129001154",
"subscriptionId": "sub_…",
"planId": "plan_…",
"invoiceId": "inv_…",
"amount": 1000,
"currency": "784",
"status": "paid",
"txnId": "16-digit id",
"HASH": "…"
}Inbound KYC webhook
Provider → this host, not merchant → this host.
POST /pgui/jsp/kycWebhookPOST /pgui/services/kyc/webhook(same handler)
JSON body:
{
"applicantId": "kyc_sbx_…",
"status": "approved",
"checkId": "chk_sbx_…",
"reason": "optional"
}status is approved, rejected, or pending (aliases are normalised). If KYC_WEBHOOK_SECRET is set, send x-kyc-signature as hex HMAC-SHA256 of the raw body. Unknown applicantId returns 404. Invalid signature returns 401. Success returns { "ok": true, "status": "Approved", "appId": "…" }.
This updates the KYB pack only. It does not start live acquiring.
Inbound banking / payouts / cards webhook
Partner rail → this host only. Not card eCom. The endpoint must be HTTPS on port 443 on the Mr Ivory origin.
POST /webhooks/upp— platform / env signing secret first (white-label rail), then any stored merchant override that verifiesPOST /webhooks/upp/<APP_ID>— that APP_ID’s ops-overridesigning_secretfirst, then platform / env
Required headers: Webhook-Id, Webhook-Timestamp, Webhook-Signature (v1= HMAC-SHA256 of timestamp + "." + raw_body). Invalid signature or a timestamp outside 300 seconds returns 401. A first delivery is persisted under Webhook-Id; a retry of the same id returns { "received": true, "duplicate": true } without re-running side effects. Banking, card issuing, and other unknown event types are stored and acknowledged with 2xx. Known payout status events update a matching payout row when data.id equals that row’s acquirer reference. Verification, KYC, funding-account, and payout events also update status chips on the Banking panel from those stored processor events. No PAN or CVV is stored.
{
"received": true
}