# Webhook Notification System

  
The **webhook system** is built upon the existing **email notification process** and allows automatic delivery of domain mutation events to external systems.

---

#### **1. Enabling or Disabling Webhooks**

The IPs to whitelist are: **185.87.187.130** and **34.34.72.46.**

Webhook notifications can be managed via the `modifyResellerRequest` API call. This API allows enabling/disabling webhook notifications and configuring webhook parameters.

##### **✅ API Parameters**

<div id="bkmrk-%C2%A0-parameter-%C2%A0-%C2%A0type-"><div><figure><table style="width: 100%;"><thead><tr><th colspan="1" rowspan="1" style="width: 48.3909%;"> Parameter

</th><th colspan="1" rowspan="1" style="width: 8.22266%;"> Type

</th><th colspan="1" rowspan="1" style="width: 43.3864%;"> Description

</th></tr></thead><tbody><tr><td colspan="1" rowspan="1" style="width: 48.3909%;">`notificationsSettings.isWebhookEnabled`

</td><td colspan="1" rowspan="1" style="width: 8.22266%;">`bool`

</td><td colspan="1" rowspan="1" style="width: 43.3864%;">Enables/disables webhook notifications for domain events.

</td></tr><tr><td colspan="1" rowspan="1" style="width: 48.3909%;">`notificationsSettings.isEmailEnabled`

</td><td colspan="1" rowspan="1" style="width: 8.22266%;">`bool`

</td><td colspan="1" rowspan="1" style="width: 43.3864%;">Reserved for future use (currently has no effect).

</td></tr><tr><td colspan="1" rowspan="1" style="width: 48.3909%;">`notificationsSettings.webhookSettings.host`

</td><td colspan="1" rowspan="1" style="width: 8.22266%;">`string`

</td><td colspan="1" rowspan="1" style="width: 43.3864%;">HTTPS URL endpoint that receives webhook POST requests.

</td></tr><tr><td colspan="1" rowspan="1" style="width: 48.3909%;">`notificationsSettings.webhookSettings.apiKey`

</td><td colspan="1" rowspan="1" style="width: 8.22266%;">`string`

</td><td colspan="1" rowspan="1" style="width: 43.3864%;">Token used for webhook authorization (sent in the `Authorization` header).

</td></tr><tr><td colspan="1" rowspan="1" style="width: 48.3909%;">`notificationsSettings.webhookSettings.signatureSecret`

</td><td colspan="1" rowspan="1" style="width: 8.22266%;">`string`

</td><td colspan="1" rowspan="1" style="width: 43.3864%;">**Optional.** Shared secret used to generate HMAC-SHA256 request signatures. When set, each request includes an `X-Webhook-Signature` header containing both the timestamp and signature. When not set, no signature header is sent. Secret rotation is fully controlled by the reseller. See Section 3.

</td></tr></tbody></table>

</figure>---

</div></div>##### **🧾 XML API Example**

```
<?xml version="1.0" encoding="UTF-8"?>
<openXML>
    <credentials>
        <username>username</username>
        <password>password</password>
        <hash></hash>
    </credentials>
    <modifyResellerRequest>
        <notificationsSettings>
            <isWebhookEnabled>1</isWebhookEnabled>    <!-- 0/1/true/false -->
            <webhookSettings>
                <host>https://webhook.example.com/endpoint</host>
                <apiKey>your_api_key_here</apiKey>
                <signatureSecret>your_signature_secret</signatureSecret>    <!-- optional -->
            </webhookSettings>
        </notificationsSettings>
    </modifyResellerRequest>
</openXML>
```

#####   
**🧾 REST API Example**

```
curl --location --request PUT '<op_api_host>/v1beta/resellers/<resellerId>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <op_auth_token>' \
--data-raw '{
  "notifications_settings": {
    "is_webhook_enabled": true,
    "webhook_settings": {
      "host": "https://webhook.example.com/endpoint",
      "api_key": "your_api_key_here",
      "signature_secret": "your_signature_secret"
    }
  }
}'
```

<div id="bkmrk--1"><div></div><div>---

</div></div>###### **🔍 Validation**

When `webhookSettings` are saved, the system automatically validates the `host` URL by sending a test webhook. A successful webhook (HTTP **200 OK**) confirms the configuration.

If `signatureSecret` is configured, the test webhook includes the same `X-Webhook-Signature` header as production webhooks, allowing you to validate your signature verification logic end-to-end during setup.

<div id="bkmrk--2"><div>---

</div></div>####   
**2. Webhook Processing**

When domain-related events occur, the system sends a **POST request** to the configured webhook endpoint.

###### **Payload Structure**

The payload includes `schemaVersion` at the root level. `domainId` is located inside the `data` object.

```
{
  "id": 123,
  "schemaVersion": "1.0",
  "eventType": "outgoingTransferCompleted",
  "timeStamp": 1757662480,
  "data": {
    "domainId": 456,
    "domain": "example.net",
    "action": "outgoing transfer succeeded",
    "status": "DEL",
    "orderDate": "2023-08-08",
    "activationDate": null,
    "renewalDate": null,
    "expirationDate": null,
    "comments": "Message from registry: Message1",
    "registryMessage": null
  }
}
```

---

##### **Request Headers**

Each webhook request includes the following headers:

![embedded-image-d7ga3pea.png](https://openprovider.help/uploads/images/gallery/2026-08/embedded-image-d7ga3pea.png)

---

##### **📬 Example Webhook Request**

```auto
curl -v --location --request POST <webhook_host_url> \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <webhook_apiKey>' \
--header 'User-Agent: Openprovider-Webhook/1.0' \
--header 'X-Webhook-Agent: Openprovider-Webhook/1.0' \
--header 'X-Webhook-Event: outgoingTransferCompleted' \
--header 'X-Webhook-Signature: t=1757662480,v1=<hex_hmac>' \
--data-raw '{
  "id": 1,
  "schemaVersion": "1.0",
  "eventType": "outgoingTransferCompleted",
  "timeStamp": 1757662480,
  "data": {
    "domainId": 1,
    "domain": "example.net",
    "action": "outgoing transfer succeeded",
    "status": "DEL",
    "orderDate": "2023-08-08",
    "activationDate": null,
    "renewalDate": null,
    "expirationDate": null,
    "comments": "Message from registry: Message1",
    "registryMessage": null
  }
}'
```

<div id="bkmrk-%E2%9C%85-expected-response"><div><div><div><div>  
**✅ Expected Response**</div></div></div></div></div>- **HTTP 200** → Webhook considered **successfully delivered**.
- **Any other code** → Delivery **failed**, and the retry mechanism is triggered.

---

##### **🔁 Retry Policy**

If a webhook fails (non-200 response or timeout), the system retries delivery up to **5 times** using a **progressive delay** schedule:

<figure id="bkmrk--7">![embedded-image-mzculxqx.png](https://openprovider.help/uploads/images/gallery/2026-08/embedded-image-mzculxqx.png)</figure>If **all attempts fail**, the system:

- Logs the event as undelivered.
- Sends an **email notification** to the reseller with subject: `[OPENPROVIDER] Failed webhooks`

---

#### **3. HMAC Signature Validation (Optional)**

To enable signature validation, set `signatureSecret` in `webhookSettings` (see Section 1). When configured, each request is signed using HMAC-SHA256, allowing your server to verify the request originated from Openprovider and that the payload was not modified in transit.

##### **Signature Header**

`X-Webhook-Signature: t=<unix_timestamp>,v1=<hex_hmac>`

The header contains two values:

- `t` — Unix timestamp (seconds) at the time the webhook was sent.
- `v1` — HMAC-SHA256 hex digest of the signed string.

##### **Signature Formula**

`stringToSign = timestamp + "." + body signature = HMAC-SHA256(stringToSign, signatureSecret)`

Where:

- `timestamp` is the `t=` value from the `X-Webhook-Signature` header.
- `body` is the raw JSON payload exactly as received.
- `signatureSecret` is the secret configured in your webhook settings.

##### **Verifying on Your Server**

1. Extract `t` and `v1` from the `X-Webhook-Signature` header.
2. Reconstruct: `stringToSign = t + "." + rawBody`
3. Compute: `HMAC-SHA256(stringToSign, signatureSecret)`
4. Compare your result with the `v1` value. If they match, the request is authentic.

**Note:** Use a constant-time comparison to prevent timing attacks.

Secret rotation is fully controlled by the reseller through webhook settings, with no downtime required.

---

#### **4. Webhook Event Types**

<div id="bkmrk-%C2%A0-%C2%A0event-type-%C2%A0-%C2%A0des"><figure><table><thead><tr><th colspan="1" rowspan="1"> Event Type

</th><th colspan="1" rowspan="1">##  Description

</th></tr></thead><tbody><tr><td colspan="1" rowspan="1">`testEvent`

</td><td colspan="1" rowspan="1">Test event for validating webhook configuration.

</td></tr><tr><td colspan="1" rowspan="1">`outgoingTransferCompleted`

</td><td colspan="1" rowspan="1">Outgoing transfer successfully completed.

</td></tr><tr><td colspan="1" rowspan="1">`incomingTransferCompleted`

</td><td colspan="1" rowspan="1">Incoming transfer successfully completed.

</td></tr><tr><td colspan="1" rowspan="1">`incomingTransferFailed`

</td><td colspan="1" rowspan="1">Incoming transfer failed.

</td></tr><tr><td colspan="1" rowspan="1">`incomingTransferCanceled`

</td><td colspan="1" rowspan="1">Incoming transfer canceled.

</td></tr><tr><td colspan="1" rowspan="1">`outgoingTransferCanceled`

</td><td colspan="1" rowspan="1">Outgoing transfer canceled.

</td></tr><tr><td colspan="1" rowspan="1">`outgoingTransferPending`

</td><td colspan="1" rowspan="1">Outgoing transfer pending.

</td></tr><tr><td colspan="1" rowspan="1">`incomingTransferPending`

</td><td colspan="1" rowspan="1">Incoming transfer pending.

</td></tr><tr><td colspan="1" rowspan="1">`deletionCompleted`

</td><td colspan="1" rowspan="1">Domain deletion completed.

</td></tr><tr><td colspan="1" rowspan="1">`deletionFailed`

</td><td colspan="1" rowspan="1">Domain deletion failed.

</td></tr><tr><td colspan="1" rowspan="1">`registrationCompleted`

</td><td colspan="1" rowspan="1">Domain registration completed.

</td></tr><tr><td colspan="1" rowspan="1">`registrationPending`

</td><td colspan="1" rowspan="1">Domain registration pending.

</td></tr><tr><td colspan="1" rowspan="1">`registrationFailed`

</td><td colspan="1" rowspan="1">Domain registration failed.

</td></tr><tr><td colspan="1" rowspan="1">`tradeFailed`

</td><td colspan="1" rowspan="1">Trade operation failed.

</td></tr><tr><td colspan="1" rowspan="1">`tradePending`

</td><td colspan="1" rowspan="1">Trade operation pending.

</td></tr><tr><td colspan="1" rowspan="1">`updateFailed`

</td><td colspan="1" rowspan="1">Domain update failed.

</td></tr><tr><td colspan="1" rowspan="1">`updateCompleted`

</td><td colspan="1" rowspan="1">Domain update completed.

</td></tr><tr><td colspan="1" rowspan="1">`messageReceived`

</td><td colspan="1" rowspan="1">New message received from registry.

</td></tr><tr><td colspan="1" rowspan="1">`restoreFailed`

</td><td colspan="1" rowspan="1">Domain restore failed.

</td></tr><tr><td colspan="1" rowspan="1">`disputeReceived`

</td><td colspan="1" rowspan="1">Dispute opened for domain.

</td></tr><tr><td colspan="1" rowspan="1">`disputeClosed`

</td><td colspan="1" rowspan="1">Dispute closed.

</td></tr></tbody></table>

</figure><div>  
 </div></div>