Capability request webhooks
Every API call that moves a capability request, the event it emits, and the payload your handler receives.
Who this section is for
Developers who open capability requests over the API instead of the merchant dashboard, and
need to know when PayMongo has reviewed one. Everything the dashboard can do here is available
as an API call, and every status change the dashboard shows is also pushed as a webhook event.
A capability request is an application to turn something on for an account: a payment method
(GCash, Maya, GrabPay, ShopeePay, cards), account activation, a standard wallet, a tax document
filing, or a change to the business profile. PayMongo reviews it, and the request moves through a
fixed set of statuses. Each move emits one capability_request.* event.
Related but differentAccount onboarding events — identity verification and activation of a child account — are a
separate family. See Onboarding webhooks.
Overview
Triggered by your API calls
| API call | New status | Event emitted |
|---|---|---|
POST /v1/requests | draft | (none — a create is not a status change) |
POST /v1/requests/:id/submit | submitted | capability_request.submitted |
POST /v1/requests/:id/review | under_review | capability_request.under_review |
POST /v1/requests/:id/review/finish | processing | capability_request.processing |
POST /v1/requests/:id/resubmit | under_review | capability_request.under_review |
POST /v1/requests/:id/cancel | cancelled | capability_request.cancelled |
Triggered by PayMongo review
No API call on your side — these arrive as webhooks only.
| New status | Event emitted |
|---|---|
pending_clarification | capability_request.pending_clarification |
processing | capability_request.processing |
completed | capability_request.completed |
declined | capability_request.declined |
Looking for the endpoints?Creating a request, submitting, resubmitting, cancelling, and the full product code list are
documented in Capability requests API under
Onboarding-as-a-Service. This page covers only what PayMongo pushes back to you.
Status lifecycle:
flowchart LR A[draft] -->|submit| B[submitted] B -->|review| C[under_review] C -->|needs info| D[pending_clarification] D -->|resubmit| C C -->|approved| E[processing] E --> F[completed] C -->|rejected| G[declined]
Entry points. Most requests start in draft and move on submit, but payment method,
account activation and tax document requests are created directly in submitted.
Terminal statuses. completed, declined and cancelled. A declined request can only be
reopened by PayMongo, which re-emits capability_request.submitted on the same request ID.
Cancelling. Allowed from draft, submitted, under_review and pending_clarification —
these paths are left off the diagram to keep it readable. Cancel is rejected from processing,
because the capability is already being provisioned, and from all three terminal statuses.
Before you begin
Prerequisites
- Register an HTTPS endpoint and verify its signatures — see
Setup & Management.- Subscribe to the
capability_request.*events you need. All seven are listed in the
event catalog below.- Authenticate API calls with the account's secret key using HTTP Basic auth
(Authorization: Basic base64(sk_xxx:)).
Register on the account that owns the requestEvents are delivered to the webhook endpoints registered on the account named in
data.account_id— the account the capability is being requested for, which is the account
whose secret key created the request. Registering only on a parent account will not receive a
child's capability request events.Match the mode too. An event's
livemodeis fixed by the key that opened the request — a
request created with a live secret key emitslivemode: truefor its whole lifecycle, one
created with a test key emitslivemode: false. A webhook only receives events of its own
mode, and a webhook's mode is set by the key that registered it. So a request opened live needs
a webhook registered live.This holds for every later transition too, including the ones PayMongo drives during review —
the mode is recorded when the request is created and does not change afterwards.
Event catalog
Listed in lifecycle order. Only completed, declined and cancelled are terminal.
| Event | Fires when | Your move |
|---|---|---|
capability_request.submitted | The request was submitted for review from draft, or a declined request was reopened by PayMongo. | Nothing. |
capability_request.under_review | A reviewer has picked up the request, or the merchant answered a clarification request. | Show "in review". |
capability_request.pending_clarification | The reviewer needs more information. Read data.merchant_note for what to supply. | Collect what was asked for, PATCH, then resubmit. |
capability_request.processing | Review passed and PayMongo is provisioning the capability. No further merchant action. | Nothing. |
capability_request.completed | The request was approved and applied. | Mark the capability live. Terminal. |
capability_request.declined | The request was rejected. Read data.merchant_note for the reason. | Show the reason. Terminal — open a new request to retry. |
capability_request.cancelled | The request was withdrawn before review finished. | The product code is free again. Terminal. |
Payload structure
Every event shares the standard envelope. attributes.data is the capability request resource
itself — the same object GET /v1/requests/{id} returns. There is no separate event-shaped
body, so one parser handles both the polled and the pushed copy.
| Field | Type | Description |
|---|---|---|
data.id | string | Event ID. Prefixed evt_. |
data.type | string | Always event. |
data.attributes.type | string | The event type, e.g. capability_request.completed. |
data.attributes.livemode | boolean | true when the request was opened with a live secret key, false for a test key. Fixed at creation and constant for the request's whole lifecycle. |
data.attributes.data | object | The capability request resource. See Resource fields. |
data.attributes.previous_data | object | Always {} for these events. |
data.attributes.created_at | integer | Unix timestamp of the transition. |
data.attributes.updated_at | integer | Unix timestamp of the transition. |
The transition is described by attributes.type together with the resource's own status and
its per-transition timestamp (processed_at, completed_at, …). There is no previous_status
field — if you need the prior status, keep the last event you processed for that request ID.
Event samples
capability_request.submitted
capability_request.submittedSample payload
{
"data": {
"id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "event",
"attributes": {
"type": "capability_request.submitted",
"livemode": true,
"data": {
"id": "req_xxxxxxxxxxxxxxxxxxxxxxxx",
"account_id": "acct_xxxxxxxxxxxxxxxxxxxxxxxx",
"request_type": "onboarding",
"product_code": "prd_svc_paymongo_std_wallet",
"status": "submitted",
"kyb_data": { "business_industry": "retail" },
"merchant_note": {},
"submitted_at": "2026-08-24T01:55:00Z",
"created_at": "2026-08-24T01:40:00Z",
"updated_at": "2026-08-24T01:55:00Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1787882100,
"updated_at": 1787882100
}
}
}What to do: nothing. Note this fires on POST /submit from draft, and when PayMongo
reopens a declined request — not on POST /v1/requests.
capability_request.under_review
capability_request.under_reviewSample payload
{
"data": {
"id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "event",
"attributes": {
"type": "capability_request.under_review",
"livemode": true,
"data": {
"id": "req_xxxxxxxxxxxxxxxxxxxxxxxx",
"account_id": "acct_xxxxxxxxxxxxxxxxxxxxxxxx",
"request_type": "product_request",
"product_code": "prd_svc_paymongo_ewallet_maya",
"status": "under_review",
"kyb_data": {},
"merchant_note": {},
"submitted_at": "2026-08-24T02:10:11Z",
"assigned_at": "2026-08-24T03:04:57Z",
"created_at": "2026-08-24T02:10:11Z",
"updated_at": "2026-08-24T03:04:57Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1787886297,
"updated_at": 1787886297
}
}
}What to do: nothing. Surface "in review" in your UI.
capability_request.pending_clarification
capability_request.pending_clarificationSample payload
{
"data": {
"id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "event",
"attributes": {
"type": "capability_request.pending_clarification",
"livemode": true,
"data": {
"id": "req_xxxxxxxxxxxxxxxxxxxxxxxx",
"account_id": "acct_xxxxxxxxxxxxxxxxxxxxxxxx",
"request_type": "product_request",
"product_code": "prd_svc_paymongo_ewallet_maya",
"status": "pending_clarification",
"kyb_data": {},
"merchant_note": {
"note": "The uploaded valid ID is expired. Please upload a current one.",
"set_at": "2026-08-24T05:41:02Z",
"requested_files": ["valid_id"]
},
"submitted_at": "2026-08-24T02:10:11Z",
"assigned_at": "2026-08-24T03:04:57Z",
"pending_clarification_at": "2026-08-24T05:41:02Z",
"created_at": "2026-08-24T02:10:11Z",
"updated_at": "2026-08-24T05:41:02Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1787895662,
"updated_at": 1787895662
}
}
}What to do: show merchant_note.note to the merchant. merchant_note.requested_fields
lists kyb_data keys to resupply and merchant_note.requested_files lists document purposes to
re-upload. Then PATCH the request if needed and call POST /v1/requests/{id}/resubmit.
The clarification loop repeats
pending_clarification → under_review → pending_clarificationis a normal cycle, so you can
receive the same event type for the same request more than once. Distinguish occurrences by
attributes.created_atanddata.pending_clarification_at, not by event type alone.
capability_request.processing
capability_request.processingSample payload
{
"data": {
"id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "event",
"attributes": {
"type": "capability_request.processing",
"livemode": true,
"data": {
"id": "req_xxxxxxxxxxxxxxxxxxxxxxxx",
"account_id": "acct_xxxxxxxxxxxxxxxxxxxxxxxx",
"request_type": "product_request",
"product_code": "prd_svc_paymongo_ewallet_maya",
"status": "processing",
"kyb_data": {},
"merchant_note": {},
"submitted_at": "2026-08-24T02:10:11Z",
"assigned_at": "2026-08-24T03:04:57Z",
"processed_at": "2026-08-24T06:02:44Z",
"created_at": "2026-08-24T02:10:11Z",
"updated_at": "2026-08-24T06:02:44Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1787896964,
"updated_at": 1787896964
}
}
}What to do: nothing — and do not treat this as approval. The request can no longer be
cancelled from here, but the capability is not usable until completed.
capability_request.completed
capability_request.completedSample payload
{
"data": {
"id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "event",
"attributes": {
"type": "capability_request.completed",
"livemode": true,
"data": {
"id": "req_xxxxxxxxxxxxxxxxxxxxxxxx",
"account_id": "acct_xxxxxxxxxxxxxxxxxxxxxxxx",
"request_type": "product_request",
"product_code": "prd_svc_paymongo_ewallet_maya",
"status": "completed",
"kyb_data": {},
"merchant_note": {},
"submitted_at": "2026-08-24T02:10:11Z",
"assigned_at": "2026-08-24T03:04:57Z",
"processed_at": "2026-08-24T06:02:44Z",
"completed_at": "2026-08-24T06:19:30Z",
"created_at": "2026-08-24T02:10:11Z",
"updated_at": "2026-08-24T06:19:30Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1787897970,
"updated_at": 1787897970
}
}
}What to do: mark the capability as approved. For a payment method, confirm it is live by
reading the account's enabled payment methods before routing traffic to it — approval and the
provider's own configuration can settle a short time apart.
capability_request.declined
capability_request.declinedSample payload
{
"data": {
"id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "event",
"attributes": {
"type": "capability_request.declined",
"livemode": true,
"data": {
"id": "req_xxxxxxxxxxxxxxxxxxxxxxxx",
"account_id": "acct_xxxxxxxxxxxxxxxxxxxxxxxx",
"request_type": "product_request",
"product_code": "prd_svc_paymongo_ewallet_maya",
"status": "declined",
"kyb_data": {},
"merchant_note": {
"note": "The business industry is not supported for this payment method.",
"set_at": "2026-08-24T06:31:08Z"
},
"submitted_at": "2026-08-24T02:10:11Z",
"assigned_at": "2026-08-24T03:04:57Z",
"declined_at": "2026-08-24T06:31:08Z",
"created_at": "2026-08-24T02:10:11Z",
"updated_at": "2026-08-24T06:31:08Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1787898668,
"updated_at": 1787898668
}
}
}What to do: show merchant_note.note. A declined request is terminal — open a new request if
the underlying reason is resolved. If you believe a decline was made in error, contact
[email protected]. A decline that PayMongo reverses arrives as
capability_request.submitted on the same request ID.
capability_request.cancelled
capability_request.cancelledSample payload
{
"data": {
"id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
"type": "event",
"attributes": {
"type": "capability_request.cancelled",
"livemode": true,
"data": {
"id": "req_xxxxxxxxxxxxxxxxxxxxxxxx",
"account_id": "acct_xxxxxxxxxxxxxxxxxxxxxxxx",
"request_type": "product_request",
"product_code": "prd_svc_paymongo_ewallet_maya",
"status": "cancelled",
"kyb_data": {},
"merchant_note": {},
"submitted_at": "2026-08-24T02:10:11Z",
"cancelled_at": "2026-08-24T04:00:00Z",
"created_at": "2026-08-24T02:10:11Z",
"updated_at": "2026-08-24T04:00:00Z"
},
"previous_data": {},
"pending_webhooks": 1,
"created_at": 1787889600,
"updated_at": 1787889600
}
}
}What to do: the product code is free again — a new request for it will now be accepted.
Resource fields
Fields you will not see
requester_id,assignee_emailandinternal_noteare internal to PayMongo's review process
and are removed from everything a merchant receives — the public endpoints and this webhook
alike. They appear in the OpenAPI reference only under the internal request schema, never under
the merchant one, so build against the fields listed below.The account a request belongs to is
account_id. That is the only identity in the payload, and
it is all a merchant needs.
Fields of data.attributes.data. Timestamp fields are RFC 3339 strings and are omitted until
the corresponding transition happens, so their presence is itself a signal.
| Field | Type | Description |
|---|---|---|
id | string | Capability request ID. Prefixed req_. |
account_id | string | The account the capability is requested for, and the account the event is delivered to. |
request_type | string | onboarding, product_request, config_update, or account_update. |
product_code | string | The capability requested. See Product codes. |
status | string | draft, submitted, under_review, pending_clarification, processing, completed, declined, cancelled. |
kyb_data | object | Application data as submitted. {} for payment method activation. |
merchant_note | object | Reviewer note. {} when none has been left. |
merchant_note.note | string | Human-readable reason, on declined and pending_clarification. |
merchant_note.set_at | string | When the note was written. |
merchant_note.requested_fields | array | kyb_data keys the reviewer asked you to resupply. Absent when none were named. |
merchant_note.requested_files | array | Document purposes the reviewer asked you to re-upload. Absent when none were named. |
organization_data | object | Profile snapshot, present on some product codes. |
submitted_at | string | When the request entered submitted. |
assigned_at | string | When a PayMongo reviewer picked the request up. Absent while it waits in the queue, and cleared if it is returned to the queue. A resubmission does not change it. |
pending_clarification_at | string | When the reviewer last asked for clarification. |
processed_at | string | When provisioning started. |
completed_at | string | When the request was approved. |
declined_at | string | When the request was declined. |
cancelled_at | string | When the request was cancelled. |
created_at | string | When the request was created. |
updated_at | string | When the request last changed. |
Internal review fields — the assigned reviewer's identity, internal notes, and the audit trail —
are never included. assigned_at is the one review detail you do receive: it is a timestamp
only, and names nobody.
What does not emit an event
- Creating a request. Not a status change. Read the create response.
- Reading a request.
GETcalls emit nothing. - A
PATCHthat does not move the status. Editingkyb_datawhile a request sits in
pending_clarificationemits nothing until you callresubmit. - Internal reassignment. Which PayMongo reviewer holds a request is not merchant-visible and
emits no event.
Delivery and retries
Events are delivered as HTTPS POST requests, signed with the Paymongo-Signature header.
Respond 2xx to acknowledge. PayMongo retries up to twelve times with exponential backoff
for any other response, and a webhook that fails three deliveries in a row is disabled
automatically. For signing keys and verification examples, see the
Webhook resource and Securing webhooks.
Delivery is at-least-once and ordering is not guaranteed. Make handlers idempotent on
data.id, and prefer data.attributes.data.status over arrival order when deciding the current
state — or re-read GET /v1/requests/{id}, which returns the same object.
Related pages
- Capability requests API — the endpoints that move a request, and product codes.
- Events — every webhook event PayMongo sends.
- Onboarding webhooks — identity verification and child account activation events.
- Webhook resource — registering endpoints, signatures, retries.
- Retry logic — backoff schedule and auto-disable rules.
- Best practices — idempotency and handler design.
- Troubleshooting — requests stuck in
pending_clarification, failed liveness checks, declines.
Updated 9 days ago
