Onboarding webhooks
Wire up a webhook handler that responds correctly to every event emitted during a child account's onboarding.
Who this section is for
Developers wiring up webhook handlers for child-account onboarding. This page describes the two event families you receive, their payloads, and how to react to each.
Overview
Onboarding emits webhook events at two points in the lifecycle:
- Identity verification events — fire when an identity verification session completes. Tell you whether to proceed or restart the identity verification step.
- Activation events — fire when PayMongo finishes reviewing a child account after you call
Activate. Tell you whether the account is live.
Events are delivered to the parent's registered webhook endpoint. To receive payment, payout, and other transaction events for a child after activation, register webhooks on the child — see Best practices for webhooks.
ScopeThese events fire on child accounts created through either onboarding path (Create an account or Invite) once activation runs. Invitation lifecycle events for the Invite path (sent / accepted / cancelled) belong to Account Linking and are documented under Linked accounts when they ship.
Before you begin
Prerequisites
- Register an HTTPS endpoint via the Webhook resource.
- Subscribe to the events you need from the lists below.
- Configure your handler for signature verification and idempotency.
Event catalog
| Event | Fires when |
|---|---|
account.identity_verification.passed | The authorized representative's identity verification session completed successfully. |
account.identity_verification.failed | The identity verification session was unsuccessful (failed liveness, document issue, expired session, etc.). |
merchant.activated | A child of type merchant has been activated and can transact. |
merchant.declined | A child of type merchant was declined during risk review. |
consumer.activated | A child of type consumer has been activated. |
consumer.declined | A child of type consumer was declined during risk review. |
Identity verification events
account.identity_verification.passed
account.identity_verification.passed{
"data": {
"id": "evt_example_passed",
"type": "event",
"attributes": {
"type": "account.identity_verification.passed",
"livemode": true,
"data": {
"id": "verif_example_passed",
"account_id": "org_example_account",
"status": "completed",
"result": "passed",
"created_at": "2026-04-15T01:34:50Z",
"expired_at": "2026-04-15T01:34:50Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1776149210,
"updated_at": 1776149210
}
}
}What to do: call Update account to fill in any remaining details, then Activate account.
account.identity_verification.failed
account.identity_verification.failed{
"data": {
"id": "evt_example_failed",
"type": "event",
"attributes": {
"type": "account.identity_verification.failed",
"livemode": true,
"data": {
"id": "verif_example_failed",
"account_id": "org_example_account",
"status": "completed",
"result": "failed",
"created_at": "2026-04-15T01:34:50Z",
"expired_at": "2026-04-15T01:34:50Z",
"failure_reason": "Please retry. Move closer to the camera, use even lighting, and ensure the face takes up at least half the image height."
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1776149210,
"updated_at": 1776149210
}
}
}What to do: present failure_reason to the representative, then start a fresh identity verification session. See Best practices for identity verification for retry guidance.
Field reference
| Field | Type | Description |
|---|---|---|
data.id | string | Event ID. Prefixed evt_. |
data.type | string | Always event. |
data.attributes.type | string | account.identity_verification.passed or account.identity_verification.failed. |
data.attributes.livemode | boolean | true for live events. Sandbox support is coming. |
data.attributes.data.id | string | Verification session ID. Prefixed verif_. |
data.attributes.data.account_id | string | The child account this verification belongs to. Prefixed org_. |
data.attributes.data.status | string | pending, in_progress, or completed. |
data.attributes.data.result | string | pending, passed, failed, or invalid. |
data.attributes.data.expired_at | string | When the verification session expires (sessions are valid for 72 hours from creation). |
data.attributes.data.failure_reason | string | Human-readable reason; present on failed events only. |
Activation events
These events fire when PayMongo finishes risk review after you call Activate account. They are the reliable signal that a child is ready to transact.
merchant.activated
merchant.activated{
"data": {
"id": "evt_xxxxxxxx",
"type": "event",
"attributes": {
"type": "merchant.activated",
"livemode": true,
"data": {
"merchant_id": "org_xxxxxxxx",
"account": {
"legal_name": "ABC Trading",
"trade_name": "ABC Store",
"type": "sole_proprietor"
},
"activation_status": "activated",
"features": ["qrph"],
"keys": {
"pk_live": "pk_live_xxxxxxxx",
"pk_test": "pk_test_xxxxxxxx",
"sk_live": "sk_live_xxxxxxxx",
"sk_test": "sk_test_xxxxxxxx"
}
},
"previous_data": {},
"created_at": 1746755984,
"updated_at": 1746755984
}
}
}{
"data": {
"id": "evt_xxxxxxxx",
"type": "event",
"attributes": {
"type": "merchant.activated",
"livemode": true,
"data": {
"merchant_id": "org_xxxxxxxx",
"account": {
"legal_name": "Legal Name",
"trade_name": "PayMongo Test Account",
"type": "partnership"
},
"activation_status": "activated",
"features": ["standard_wallet"],
"wallet": {
"id": "wallet_xxxxxxxx",
"account_number": "123456789101",
"keys": {
"sk_live": "sk_live_xxxxxxxx",
"sk_test": "sk_test_xxxxxxxx"
},
"status": "activated",
"type": "standard"
}
},
"previous_data": {},
"created_at": 1746755984,
"updated_at": 1746755984
}
}
}What to do: mark the child as ready in your system, persist the returned keys (when applicable), and optionally subscribe to that child's transaction events. See Best practices for webhooks.
merchant.declined
merchant.declined{
"data": {
"id": "evt_xxxxxxxx",
"type": "event",
"attributes": {
"type": "merchant.declined",
"livemode": true,
"data": {
"merchant_id": "org_xxxxxxxx",
"account": {
"legal_name": "Jose Rizal",
"trade_name": "Jose Rizal",
"type": "individual"
},
"activation_status": "declined",
"declined_message": "The information provided, along with our independent verification process, failed to provide a sufficient basis to meet the required Know-Your-Customer standards."
},
"previous_data": {},
"created_at": 1715684435,
"updated_at": 1715684435
}
}
}What to do: show declined_message to your team. Activation declines are final for the account; if you believe a decline was made in error, contact [email protected].
consumer.activated
consumer.activated{
"data": {
"id": "evt_xxxx",
"type": "event",
"attributes": {
"type": "consumer.activated",
"livemode": true,
"data": {
"consumer_id": "org_xxxx",
"account": {
"first_name": "John",
"last_name": "Doe",
"email_address": "[email protected]"
},
"activation_status": "activated",
"features": ["fully_verified_standard_wallet"],
"wallet": {
"id": "wallet_xxxx",
"account_number": "1234567890",
"keys": {
"sk_live": "sk_live_xxxx",
"sk_test": "sk_test_xxxx"
},
"status": "activated",
"type": "fully_verified_standard"
}
},
"previous_data": {},
"created_at": 1746675190,
"updated_at": 1746675190
}
}
}consumer.declined
consumer.declined{
"data": {
"id": "evt_xxxx",
"type": "event",
"attributes": {
"type": "consumer.declined",
"livemode": true,
"data": {
"consumer_id": "org_xxxxx",
"account": {
"first_name": "John",
"last_name": "Doe",
"email_address": "[email protected]"
},
"activation_status": "declined",
"decline_message": "The information provided, along with our independent verification process, failed to provide a sufficient basis to meet the required Know-Your-Customer standards."
},
"previous_data": {},
"created_at": 1746598148,
"updated_at": 1746598148
}
}
}Field reference
| Field | Type | Description |
|---|---|---|
data.attributes.type | string | merchant.activated, merchant.declined, consumer.activated, or consumer.declined. |
data.attributes.data.merchant_id / consumer_id | string | The child account ID. Prefixed org_. |
data.attributes.data.account | object | Snapshot of the child's profile at the time of the event. |
data.attributes.data.activation_status | string | activated or declined. |
data.attributes.data.features | array | Capabilities provisioned at activation. Always present on activated events. |
data.attributes.data.keys | object | API keys for the activated child. Present on merchant.activated for fully-owned accounts. |
data.attributes.data.wallet | object | Wallet details when a wallet was provisioned at activation. |
data.attributes.data.declined_message / decline_message | string | Human-readable reason; present on declined events only. |
Delivery and retries
Webhook events are delivered via HTTPS POST. Respond with a 2xx status to acknowledge receipt. PayMongo retries up to twelve times using exponential backoff if your endpoint returns anything else. For full delivery semantics, signing keys, and verification examples, see the Webhook resource and Securing webhooks reference.
Related pages
- Best practices for webhooks — idempotency, signature verification, child-account event subscriptions.
- Quick start — full onboarding flow, with webhook listening as step 5.
- Linked transactions — operate on an activated child.
- Troubleshooting — what to do when an event does not arrive or carries an unexpected payload.
Updated about 1 month ago
