# Transaction webhooks

> Receive asynchronous transaction notifications and tell them apart from Merchant Callbacks.

Source: https://help.boltapp.com/custom-cart/transaction-webhooks

Merchant Callbacks tell Bolt what to do during checkout. Transaction webhooks tell your server what happened afterwards. They are configured in different places and do not share a URL.

## Callbacks are not webhooks [#callbacks-are-not-webhooks]

|               | Merchant Callbacks                            | Transaction webhooks                       |
| ------------- | --------------------------------------------- | ------------------------------------------ |
| Direction     | Bolt asks, checkout waits for your answer     | Bolt informs, nothing waits                |
| Timing        | Synchronous, blocking                         | Asynchronous, retried                      |
| Failure       | Can break the checkout                        | Retried until acknowledged                 |
| Configured at | **Administration > API > Merchant Callbacks** | **Administration > Developers > Webhooks** |
| Stored as     | Callback URLs on the division                 | Webhook subscriptions                      |

They are stored separately, so a webhook endpoint does not fall back to your Universal Merchant API URL. Configuring callbacks and expecting webhooks to arrive at the same URL results in a checkout that works and an order record that never appears.

<Warning>
  Do not look for transaction webhooks under **Administration > API > Merchant Callbacks**. The callback type dropdown there has no transaction webhook option. Webhooks live under **Administration > Developers > Webhooks**.
</Warning>

## If you cannot see the Webhooks page [#if-you-cannot-see-the-webhooks-page]

The page is filtered out of navigation rather than returning an error, so an account without access sees nothing at all rather than a permission message. Three things restrict it:

* Write access to webhook settings. Without it the navigation item is hidden.
* Marketplace sub-merchant accounts cannot configure webhooks.
* Without the v2 webhooks capability a division is limited to a single endpoint.

If the controls your integration needs are missing, ask Bolt to confirm the webhook capabilities for the division. See [prerequisites and readiness](/custom-cart/prerequisites).

## Choose the events for an endpoint [#choose-the-events-for-an-endpoint]

In the Merchant Dashboard, subscribe an endpoint to all events or select the events it should receive. When using the API, provide exactly one of `event_group` or `events`. An endpoint subscribed to all events must switch on **both** the notification type and the object it concerns, and acknowledge events it does not process.

| Category        | Public event types                                                                                                                                                           |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Transaction     | `payment`, `credit`, `capture`, `void`, `auth`, `pending`, `rejected_irreversible`, `rejected_reversible`, `failed_payment`, `risk_insights`                                 |
| Shopper account | `newsletter_subscription`, `credit_card_deleted`                                                                                                                             |
| Subscription    | `subscription_created`, `subscription_renewed`, `subscription_canceled`, `subscription_payment_failed`, `subscription_paused`, `subscription_unpaused`, `subscription_ended` |

These values come from the public webhook contract. Subscribe only to events your integration understands, or use `event_group: "all"` and make the handler tolerate new event types. Return HTTP 200 after accepting a delivery. An unacknowledged delivery is retried, so processing must be idempotent.

## Configuring over the API [#configuring-over-the-api]

Webhooks can be created without the dashboard:

If your backend uses Python, the beta [Bolt Python SDK](https://github.com/BoltApp/Bolt-Python-SDK/blob/main/docs/sdks/webhooks/README.md#create_webhook) wraps these endpoints as `webhooks.create_webhook()`, `query_webhooks()`, `get_webhook()`, and `delete_webhook()`. It does not receive or process webhook deliveries; that handler remains part of your application.

```bash title="Request"
curl -X POST https://api-sandbox.boltapp.com/v1/webhooks \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $BOLT_API_KEY" \
  -d '{
    "division_id": "'"$BOLT_DIVISION_ID"'",
    "url": "https://example.com/bolt/webhooks",
    "event_group": "all"
  }'
```

List what is registered with `GET /v1/webhooks`, and callbacks with `GET /v1/merchant/callbacks`. Both require `division_id`, which is the second segment of your publishable key. See [prerequisites](/custom-cart/prerequisites#find-your-division-id).

<Note>
  `POST /v1/merchant/callbacks` can set a callback URL but cannot clear one. An empty URL is rejected rather than treated as a removal, so recovering from a bad callback URL means overwriting it with a working one or using the dashboard. Webhook subscriptions are not affected by this.
</Note>

## Verifying webhooks [#verifying-webhooks]

Webhooks are signed the same way callbacks are. See [verifying the request came from Bolt](/api-reference/merchant-callback/conventions#verifying-the-request-came-from-bolt).

## Verify the completed integration [#verify-the-completed-integration]

Complete a sandbox checkout and confirm that:

1. The client opens checkout with a token created by your backend.
2. Bolt reaches the Merchant Callback endpoints needed by the checkout.
3. Your webhook endpoint receives the events you subscribed to.
4. Replaying a delivery does not create a duplicate update in your system.

If one of these checks fails, continue to [Custom cart troubleshooting](/custom-cart/troubleshooting).
