# Manage endpoints (/guides/manage-webhooks)



The [Webhooks](/guides/webhooks) contract says what arrives and how to verify it. This page is the
receiving side: where deliveries go, and what you do when they stop.

Everything below is also a screen in the [console](https://app.molnpay.com/webhooks). From the API it needs
the `webhooks:write` scope (replay needs `webhooks:replay`, reads need `webhooks:read`); any key kind may
hold them.

<AppPicture name="webhooks" alt="A sample active webhook endpoint and successful deliveries in the dashboard." caption="The dashboard controls, shown with sample data." />

<Steps>
  <Step>
    ## Register [#register]

    ```bash
    curl -X POST https://api.molnpay.com/v1/webhooks \
      -H "Authorization: Bearer $MOLNPAY_KEY" -H "Content-Type: application/json" \
      -d '{"url":"https://example.com/molnpay/webhook","event_types":["deposit.confirmed","deposit.orphaned"]}'
    ```

    The `201&#x60; is the endpoint plus its &#x2A;*`secret`** (`whsec_…`) — the only time it is ever shown. Store it
    now; no endpoint returns it again, and a lost secret is answered by rotating. An empty `event_types`, the
    default, subscribes to **every** event, including types added later, so keep a default branch in your
    handler.

    The URL must be `https`, on port 443 or 8443, with no credentials, resolving to a public address. It is
    checked before the endpoint exists and again on every send, because DNS is mutable. Up to 16 endpoints
    per project.
  </Step>

  <Step>
    ## Prove it [#prove-it]

    ```bash
    curl -X POST https://api.molnpay.com/v1/webhooks/$ENDPOINT_ID/test \
      -H "Authorization: Bearer $MOLNPAY_KEY"
    ```

    Queues one `endpoint.test` through the real pipeline — signed, SSRF-checked, retried — and answers `202`
    with a `delivery_id`. Read `GET /v1/webhook_deliveries/{delivery_id}` until `status` is `succeeded`; if
    it is not, `response_status`, `response_body` and `error_detail` say why, in that order of usefulness.
  </Step>

  <Step>
    ## Rotate the secret [#rotate-the-secret]

    ```bash
    curl -X POST https://api.molnpay.com/v1/webhooks/$ENDPOINT_ID/rotate_secret \
      -H "Authorization: Bearer $MOLNPAY_KEY" -H "Content-Type: application/json" \
      -d '{"overlap_hours":24}'
    ```

    Until `overlap_until`, every delivery carries **two** `v1` values and either secret verifies, so switch
    your receiver at your own pace. `0` hours is for a leaked secret. The new secret is shown only in that
    response.
  </Step>

  <Step>
    ## Pause, resume, re-enable [#pause-resume-re-enable]

    `PATCH /v1/webhooks/{id}` with `{"status":"paused"}` stops sends while still recording every event as a
    `skipped` delivery; `{"status":"active"}` resumes. The same PATCH re-enables an endpoint **we** disabled —
    five consecutive exhausted deliveries, or a `410 Gone` from you — and resets its strike count. Fix the
    receiver first: re-enabling a broken one burns the retry budget again.
  </Step>

  <Step>
    ## Recover a gap [#recover-a-gap]

    ```bash
    curl -X POST https://api.molnpay.com/v1/webhook_deliveries/replay \
      -H "Authorization: Bearer $MOLNPAY_KEY" -H "Content-Type: application/json" \
      -d '{"endpoint_id":"'$ENDPOINT_ID'","status":"skipped","since":"2026-09-15T00:00:00Z"}'
    ```

    Re-sends up to 100 `failed`, `exhausted` or `skipped` deliveries for the endpoint, oldest first, each as a
    **new** delivery of the **same** event — so a handler that dedupes on `event.id` is unaffected. Call it
    again for a larger gap. One delivery at a time: `POST /v1/webhook_deliveries/{id}/replay`.
  </Step>
</Steps>

## The event log [#the-event-log]

`GET /v1/events` is every event your project emitted, newest first, ordered by `sequence` — the number the
envelope carries. Reconciling "did we miss one?" is a comparison against this list, never a guess.
`GET /v1/events/{id}` returns the event with its `data`; `GET /v1/events/{id}/deliveries` shows where
each send of it stood.

<Callout title="Deleting an endpoint deletes its delivery history">
  `DELETE /v1/webhooks/{id}` removes the endpoint and every delivery record. The events themselves stay.
  If you are debugging a receiver, pause it instead.
</Callout>

The full shapes are in the [Webhooks](/reference/webhooks) and [Events](/reference/events) reference.
