> For the complete documentation index, see [llms.txt](https://docs.ggwp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ggwp.com/webhooks/receiving-events.md).

# Receiving Events

## **General flow for event handling**

1. When an event occurs (trigger), GGWP webhook will send a payload (POST request) to the target URL.
   1. v1.0 : The payload sent will be in JSON string format, with the possibility of  whitespaces between key & value pairs. `Content-Type`  header is set to `application/json` .
   2. v1.1 : The payload sent will be in JSON string format, without whitespaces. This reduces an extra step of stripping the payload of whitespaces for HMAC validation. `Content-Type`  header is set to `application/octet-stream` .
2. Validate incoming request via HMAC. Additionally, servers can rely on the custom headers (set during subscription creation) for authentication.
3. Once the payload is received by the target URL, the target should send back a **200 status code** to indicate successful receipt of payload.
4. If 200 status code is not returned within 1 minute, then the trial is assumed as failure. Three retries are done post this before finally giving up on the URL calls for the event.&#x20;

To receive a subscription event, customer is expected to write APIs which can take the payload being sent by GGWP Webhooks.

## Creating the target URLs

Upon any event trigger GGWP delivers the information by calling the target URL. The target URL is expected to handle such events by supporting `POST` method on the API.

## Request Validation

All the incoming requests to the target URLs need to be validated to ensure they are coming from GGWP. This can be done by calculating a digital signature. Each webhook request includes a base64 encoded `x-ggwp-hmac-sha256` header, which is generated using the subscription secret along with the data sent in the request.

{% hint style="warning" %}
For v1.0 implementation, to ensure accurate signature validation, the payload data must be stripped of all whitespace before calculating the HMAC digest. This is because extra whitespace characters can be introduced during formatting or transmission, which do not affect the actual content but can lead to mismatched signatures if not accounted for.
{% endhint %}

To validate, compute the HMAC digest according to the following algorithm, and if the HMAC digest and the header value match then the webhook event is sent from GGWP.

{% code lineNumbers="true" fullWidth="false" %}

```python
# Example in Python

import base64
import hashlib
import hmac
import json


# Secret received upon creating the webhook subscription
SECRET_KEY = 'my_secret_key'

def verify_webhook(payload, hmac_header):
    digest = hmac.new(
        SECRET_KEY.encode('utf-8'), 
        json.dumps(payload, separators=(',', ':')).encode("utf-8"),
        digestmod=hashlib.sha256
    ).digest()
    computed_hmac = base64.b64encode(digest)

    return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))

```

{% endcode %}

> For GoLang based servers, it is recommended to use only Version 1.1, as hmac validation on 1.0 will not work. HMAC computation is always done on a compacted JSON (without spaces).

{% code lineNumbers="true" %}

```go
// Example in Go

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
)

// Secret received upon creating the webhook subscription
const SECRET_KEY = "my_secret_key"

func verifyWebhook(payload string, hmacHeader string) bool {
	mac := hmac.New(sha256.New, []byte(SECRET_KEY))
	mac.Write([]byte(payload))
	digest := mac.Sum(nil)
	computedHmac := base64.StdEncoding.EncodeToString(digest)
	return hmac.Equal([]byte(computedHmac), []byte(hmacHeader))
}

```

{% endcode %}
