# Manage Webhooks

GrailPay delivers webhook events to HTTPS URLs that you register through the API. A v3 registration is just a URL:
there is no list of events to subscribe to. Every registered URL receives every event, and your handler decides which
ones to act on by reading the `event` field in each delivery. Registering, listing, inspecting, and removing webhooks
all happen through the `/api/v3/webhooks` endpoints, which replace the v1 `webhook` endpoints and their processor-side
twins.

Registrations belong to the vendor or processor your API token authenticates as. Each vendor or processor can hold up
to 25 registrations, and a URL can be registered once; registering a duplicate, or a twenty-sixth URL, results in an
error. Every URL must be served over HTTPS.

For the events a registration receives, and the payload of each one, see the
[Webhook Events](/docs/technical/webhooks/events/) documentation.

:::note
This route is secured with **processor** and **vendor** token authentication.
:::

:::note
Webhooks are not specific to any entity or event type. One registered URL receives every event for every merchant,
business, and person you work with, so there is nothing to register per merchant or per event. Use `event` to route
each delivery and the UUIDs inside `data` to identify the entity it concerns.
:::

:::caution
**If deliveries to a registered URL fail 30 times in a row, GrailPay deactivates that registration automatically.**
Its `is_active` becomes `false` and events are no longer delivered to it. The registration's `failed_counter` reports
consecutive failures, so a rising count is the signal to look at your endpoint before it reaches the limit.
:::

---

### Registering a Webhook

To register a webhook, submit a `POST` request to `/api/v3/webhooks` with the HTTPS `url` that should receive events.
Nothing else goes in the body. The request supports the [`Idempotency-Key` header](/docs/technical/idempotency), so a
retried registration does not create a second one.

The response is the new registration: its `uuid`, which identifies it in every other webhook call; the `url`; a
`secret`, generated for this registration, which GrailPay uses to sign every delivery sent to it; and its `is_active`
state, `failed_counter`, and timestamps. Store the `secret` when you receive it. It is what your handler needs to verify
the `Signature` header on each event, as described under
[Authenticating Webhooks](/docs/technical/webhooks/events/#authenticating-webhooks).

For full schema details, refer to:

- [Register Webhook](/api/payments/#tag/webhooks/POST/api/v3/webhooks)

---

### Listing Webhooks

To see every registration that belongs to your vendor or processor, send a `GET` request to `/api/v3/webhooks`. The
list is paginated, can be filtered by `is_active` or by `url`, and can be sorted. Deleted registrations are not
included.

For full schema details, refer to:

- [Get All Webhooks](/api/payments/#tag/webhooks/GET/api/v3/webhooks)

---

### Fetching a Webhook

To retrieve a single registration, send a `GET` request to `/api/v3/webhooks/{uuid}`. The response includes the
registration's `secret`, so this is also how to recover it if it was not stored at registration time, along with its
current `is_active` state and `failed_counter`.

For full schema details, refer to:

- [Get Webhook](/api/payments/#tag/webhooks/GET/api/v3/webhooks/{uuid})

---

### De-Registering a Webhook

To stop delivering events to a URL, send a `DELETE` request to `/api/v3/webhooks/{uuid}` with the registration's UUID.
Events are no longer delivered to its URL, and requesting the registration afterwards returns `404`.

For full schema details, refer to:

- [Delete Webhook](/api/payments/#tag/webhooks/DELETE/api/v3/webhooks/{uuid})

---

## Testing

At this time, GrailPay does not support direct simulation of webhook events in the Sandbox environment. However, you
can test your webhook integration by registering a webhook URL in Sandbox and performing actions that naturally trigger
webhook events. This approach allows you to validate that your endpoint receives and processes real webhook payloads as
expected.

For more details on testing in Sandbox, refer to our [Technical Overview](/docs/technical/overview#testing) guide.

---

## Questions?

If you're encountering any issues, please reach out to [support@grailpay.com](mailto:support@grailpay.com).

---