# Webhooks for Application Events

Webhooks let Bead notify your system when important events occur on a merchant onboarding application.

Instead of relying only on repeated polling, your server receives an HTTP `POST` when an application reaches a meaningful lifecycle moment.

Use onboarding webhooks when you want to:

* keep your own merchant records in sync with application progress
* trigger internal workflows when an application is signed, reviewed, or boarded
* notify internal teams when onboarding activity requires follow-up
* reduce repeated polling against the Get Status endpoint
* support operational dashboards and reconciliation workflows

Onboarding webhooks are configured at the partner level. Once configured, Bead sends onboarding-related events for applications associated with that partner.

### How onboarding webhooks work

A typical webhook workflow is:

1. You create or manage onboarding applications under a partner.
2. You configure a partner-level onboarding webhook endpoint.
3. Bead sends an event to your endpoint when an application lifecycle event occurs.
4. Your system validates the request, records the event, and updates internal state.
5. If you need the full current application state, call `GET /merchant-onboarding/applications/{applicationId}` using the `applicationId` from the webhook.

Webhook payloads are event notifications. They are not intended to replace the Get Status endpoint as the full source of application truth.

### Supported onboarding events

Onboarding webhook configuration supports these event types:

| Event type            | Meaning                                                                      | Recommended handling                                                                                        |
| --------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `applicationSigned`   | The signer completed the signing step represented by the onboarding package. | Record the event and call Get Status to confirm the current application state.                              |
| `applicationReviewed` | The application review step produced a review event.                         | Call Get Status to determine whether the application is reviewed, still needs action, or has moved forward. |
| `merchantBoarded`     | The merchant has completed boarding.                                         | Call Get Status and store `onboardedMerchantId` for downstream workflows.                                   |

Design your handler to tolerate additional event types in the future. New event types should not break your webhook consumer.

### Configure the onboarding webhook

To receive onboarding events, configure a webhook URL for your partner.

You can register, update, or remove a partner-level webhook configuration.

| Action                                   | Endpoint                                          |
| ---------------------------------------- | ------------------------------------------------- |
| Register webhook configuration           | `POST /merchant-onboarding/{partnerId}/webhook`   |
| Register or update webhook configuration | `PUT /merchant-onboarding/{partnerId}/webhook`    |
| Remove webhook configuration             | `DELETE /merchant-onboarding/{partnerId}/webhook` |

For most integrations, use `PUT` so the same command can create or update the configuration.

### Endpoint

```http
PUT /merchant-onboarding/{partnerId}/webhook
```

### Authentication and headers

Onboarding requests use API key authentication.

```http
X-Api-Key: {apiKey}
Content-Type: application/json
Accept: application/json
```

### Path parameter

| Parameter   | Description                                                                                                                                   |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `partnerId` | Your partner identifier. The webhook configuration is stored per partner and applies to onboarding applications submitted under that partner. |

### Request body

Use a request body with your webhook endpoint URL and the onboarding event types you want to receive.

```json
{
  "endpointUrl": "https://partner.example.com/webhooks/onboarding",
  "eventTypes": [
    "applicationSigned",
    "applicationReviewed",
    "merchantBoarded"
  ]
}
```

### Field details

| Field         | Required | Description                                                              |
| ------------- | -------- | ------------------------------------------------------------------------ |
| `endpointUrl` | Yes      | HTTPS URL of your server that will receive onboarding webhook callbacks. |
| `eventTypes`  | Yes      | List of onboarding event types to deliver to this endpoint.              |

Your `endpointUrl` should be stable, publicly reachable, HTTPS-only, and controlled by your organization.

You can call the `PUT` endpoint more than once to update the same partner’s webhook configuration.

### Response

The response confirms the current webhook configuration.

Typical fields include:

* `partnerId`
* `endpointUrl`
* `isEnabled`
* `webhookSecret`
* `eventTypes`

Example response:

```json
{
  "partnerId": "partner_123",
  "endpointUrl": "https://partner.example.com/webhooks/onboarding",
  "isEnabled": true,
  "webhookSecret": "whsec_example_secret",
  "eventTypes": [
    "applicationSigned",
    "applicationReviewed",
    "merchantBoarded"
  ]
}
```

Store the `webhookSecret` securely. Treat it as a secret and do not expose it in client-side code.

### Receiving webhook events

After you configure an onboarding webhook, Bead sends HTTP `POST` requests to your `endpointUrl` when selected onboarding events occur.

High-level behavior:

* method is `POST`
* content type is JSON
* body contains event metadata plus a small application summary
* your endpoint should return a `2xx` response when processing succeeds

Onboarding webhook payloads are intentionally lean. They are not full application records.

Use the webhook payload to:

* identify which application changed
* identify what event occurred
* correlate the event to your own merchant or partner records
* trigger follow-up actions
* decide whether to fetch the full application status

Use `GET /merchant-onboarding/applications/{applicationId}` when you need the full current state of the application.

### Security and verification

Treat onboarding webhooks as a privileged integration path.

Recommended practices:

* use HTTPS for your webhook endpoint
* accept only `POST` on your webhook route
* preserve the raw request body and request headers
* validate the request body against expected fields
* use the `webhookSecret` from the webhook configuration for verification where your program enables signing or shared-secret verification
* optionally restrict source IPs or networks where appropriate for your environment

The exact signing or authentication mechanism may vary by environment or program. If your environment provides a signature header or specific signing format, validate that before processing the event.

### Payload format

Onboarding application events use a small event envelope with top-level metadata and a nested `data` object.

#### Top-level fields

| Field           | Type   | Description                                          |
| --------------- | ------ | ---------------------------------------------------- |
| `id`            | string | Unique webhook event identifier.                     |
| `type`          | string | Event type that describes what occurred.             |
| `applicationId` | string | Primary onboarding application identifier.           |
| `createdAt`     | string | Timestamp when the event was created.                |
| `data`          | object | Small application summary associated with the event. |

#### `data` object fields

| Field                    | Type           | Description                                                                          |
| ------------------------ | -------------- | ------------------------------------------------------------------------------------ |
| `data.id`                | string         | Application identifier within the nested payload. This should match `applicationId`. |
| `data.merchantName`      | string \| null | Merchant name associated with the application.                                       |
| `data.partnerName`       | string \| null | Partner name associated with the application.                                        |
| `data.partnerId`         | string         | Partner identifier associated with the application.                                  |
| `data.partnerExternalId` | string \| null | Your external partner reference, when available.                                     |

### Example payload

Below is an example `applicationSigned` onboarding webhook event.

```json
{
  "id": "evt_example_application_signed_001",
  "type": "applicationSigned",
  "applicationId": "app_example_001",
  "createdAt": "2026-03-30T20:46:15.111218Z",
  "data": {
    "id": "app_example_001",
    "merchantName": "Example Merchant",
    "partnerName": "Example Partner",
    "partnerId": "partner_example_001",
    "partnerExternalId": "example-partner-001"
  }
}
```

### Field guidance

#### `id`

This is the webhook event identifier. Use it as the primary event-level idempotency key whenever possible.

#### `type`

This tells you what happened.

Supported onboarding event types include:

* `applicationSigned`
* `applicationReviewed`
* `merchantBoarded`

Use `type` as the primary event classifier, but do not treat it as the complete application state. Call Get Status when your system needs to know the current application status.

#### `applicationId`

This is the primary application correlation key.

Use it to:

* link the webhook to your internal onboarding record
* call Get Status for full application detail
* reconcile lifecycle changes over time
* support internal dashboards and support workflows

#### `createdAt`

This is the event timestamp. It is useful for event ordering, auditing, and deduplication.

Do not rely on events arriving in strict chronological order. Use `createdAt` for audit context and call Get Status to confirm the current application state.

#### `data`

This object contains a lightweight application summary.

It is useful for:

* displaying a readable merchant or partner name in logs and dashboards
* routing to the correct partner context
* correlating internal references without requiring an immediate follow-up API call

### Event handling expectations

At minimum, your webhook handler should store:

* `id`
* `type`
* `applicationId`
* `createdAt`

In most integrations, you should also store:

* `data.partnerId`
* `data.partnerExternalId`
* `data.merchantName`
* the raw request body
* the request headers
* your processing result

Treat the payload as an event trigger, not a source of full application truth.

### Recommended processing model

A recommended webhook processing model is:

1. Receive the request.
2. Preserve the raw request body and headers.
3. Verify authenticity using your configured webhook security model.
4. Parse the JSON payload.
5. Use `id` as the event identifier and `applicationId` as the application correlation key.
6. Apply idempotent processing so duplicate deliveries do not create duplicate actions.
7. Call Get Status when you need the full current application state.
8. Update your internal systems.
9. Return a `2xx` response quickly.

### Idempotency and duplicate handling

Treat webhook delivery as at least once.

A good idempotent processing strategy is:

* primary event key: `id`
* fallback key: `applicationId` + `type` + `createdAt`

Your system should be able to safely ignore or reprocess duplicate webhook deliveries without creating duplicate tasks, records, or notifications.

### Using webhooks with Get Status

Webhooks and Get Status work best together.

Use the webhook to:

* detect that something changed
* know which application changed
* route the event internally
* trigger follow-up workflows

Use Get Status to:

* retrieve the current full application state
* confirm the latest status before irreversible actions
* check for `onboardedMerchantId` after `merchantBoarded`
* surface complete details in your portal or internal tools

This is especially important because onboarding webhook events are intentionally smaller than the full application record.

### Mapping events to application status

Webhook event types and application statuses are related, but they are not the same thing.

| Webhook event         | Related status context                                                                                           | Recommended action                                                                            |
| --------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `applicationSigned`   | Often associated with signer completion and statuses such as `signed`, `reviewing`, or later states.             | Call Get Status and update your internal record using the returned status.                    |
| `applicationReviewed` | Often associated with review completion and statuses such as `reviewed`, `boarding`, `withdrawn`, or `rejected`. | Call Get Status and determine the next operational step from the current status.              |
| `merchantBoarded`     | Usually associated with the `boarded` status and presence of `onboardedMerchantId`.                              | Call Get Status, store `onboardedMerchantId`, and proceed with downstream merchant workflows. |

Do not assume the webhook event alone contains the final current status. Always call Get Status when the next action depends on the exact current application state.

### Using webhooks with manual risk and review workflows

Webhooks also complement manual risk, review, and operations workflows.

A common pattern is:

1. receive an onboarding webhook event
2. call Get Status for the latest state
3. surface the current state to your operations team or merchant-facing portal
4. take the next internal action based on the returned status and any onboarding exceptions

This is especially useful when an application needs review, follow-up, or operational investigation.

### Testing your onboarding webhook

Before going live:

1. Implement a simple listener endpoint that logs incoming requests and returns `200 OK`.
2. Configure your Sandbox partner to use that endpoint.
3. Include the event types you want to test.
4. Submit test onboarding applications.
5. Capture real webhook events.
6. Confirm your processing is idempotent.
7. Confirm your system can use `applicationId` to fetch full application state.

Recommended events to test over time:

* `applicationSigned`
* `applicationReviewed`
* `merchantBoarded`

For a complete Sandbox workflow test, use Test the Full Onboarding Workflow in Sandbox and confirm your webhook listener receives the expected lifecycle events as the application moves through signing, review, and boarding.

### Troubleshooting

#### I am not receiving onboarding webhook events

Check:

* the webhook is configured on the expected `partnerId`
* the applications were created under that partner
* the configured `eventTypes` include the event you expect to receive
* your endpoint is publicly reachable
* your endpoint accepts `POST`
* your endpoint returns a `2xx` response
* your endpoint does not time out

#### I received a webhook but need more application detail

Use `applicationId` and call:

```http
GET /merchant-onboarding/applications/{applicationId}
```

#### I am unsure how to verify authenticity

Use the configured `webhookSecret` and your program’s signing or shared-secret verification method.

If your environment uses a specific header or signature format, validate that before processing.

#### The webhook payload does not include everything I need

This is expected. The onboarding webhook payload is designed as a lightweight event notification.

Use it to trigger follow-up logic, then call Get Status when you need the full application record.

#### I received the same webhook more than once

This can happen with at-least-once webhook delivery.

Use `id` as your event-level idempotency key. Your system should safely ignore duplicates or reprocess them without creating duplicate tasks, records, emails, or status changes.

#### I received an event type I do not recognize

Store the event, avoid failing your handler, and return `2xx` if the request is otherwise valid.

Then call Get Status using `applicationId` to determine the current application state.

### Best practices

* Treat onboarding webhook events as event notifications, not full application records.
* Configure only the event types your system is prepared to process.
* Use `id` as the primary event identifier.
* Use `applicationId` as the primary application correlation key.
* Return a `2xx` response quickly.
* Make your consumer idempotent.
* Keep your webhook endpoint HTTPS-only.
* Store `webhookSecret` securely.
* Capture and test real Sandbox events before Production.
* Call Get Status before taking irreversible or operationally sensitive actions.
* Design your handler to tolerate new event types or additional fields in the future.

### Related pages

* [Submit Application](/onboarding/submit-application.md)
* [Fee Configuration for Onboarding Applications](/onboarding/fee-configuration-for-onboarding-applications.md)
* [Application Attachments](/onboarding/application-attachments.md)
* [Test the Full Onboarding Workflow in Sandbox](/onboarding/test-the-full-onboarding-workflow-in-sandbox.md)
* [Get Status](/onboarding/get-status.md)
* [Resend Application](/onboarding/resend-application.md)
* [Sample Payload](/onboarding/sample-payload.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.bead.xyz/onboarding/webhooks-for-application-events.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
