> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moduluslabs.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Payment Webhooks Quickstart

> Register an endpoint, retrieve its signing secret, and receive a signed event

## 1. Create an endpoint

Use an API key with `webhooks.write`. Partner- and merchant-scoped keys must
select an authorized branch according to your account hierarchy.

```bash theme={null}
curl -X POST "${MODULUS_API_BASE_URL}/v1/webhook_endpoints" \
  -H "X-API-Key: ${MODULUS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://merchant.example/webhooks/modulus",
    "enabled_events": ["payment.succeeded", "payment.declined", "payment.failed", "payment.expired"],
    "description": "Production payment events"
  }'
```

Save the returned endpoint `id`.

The create response does not include a signing secret. Retrieve it through the
dedicated endpoint in the next step.

## 2. Retrieve the signing secret

```bash theme={null}
curl "${MODULUS_API_BASE_URL}/v1/webhook_endpoints/${ENDPOINT_ID}/signing_secret" \
  -H "X-API-Key: ${MODULUS_API_KEY}"
```

```json theme={null}
{
  "signing_secret": "whsec_dGVzdF9zaWduaW5nX3NlY3JldA=="
}
```

Store the returned `whsec_` value in a secret manager. It is separate from the
API key and must never be exposed in browser code or logs.

## 3. Receive and verify

Modulus sends a JSON `POST` with `Webhook-Id`, `Webhook-Timestamp`, and
`Webhook-Signature`. Verify the signature against the exact raw request bytes
before parsing JSON. See [Verify signatures](/docs/webhooks/payment/signatures).

## 4. Acknowledge safely

Persist and deduplicate the event by its envelope `id`, return a `2xx` response
quickly, and process fulfillment asynchronously. Non-`2xx` responses and
timeouts are retried automatically.

<Warning>
  Never use your Modulus API key to verify a webhook. Every endpoint has its own
  endpoint signing secret.
</Warning>
