> For the complete documentation index, see [llms.txt](https://developer.goshiip.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.goshiip.com/getting-started/webhook.md).

# Webhook

## What are Webhooks?

Webhooks provide a mechanism for automated communication between applications. When specific events are triggered within your integration with our API, we send a notification containing an event payload with the relevant data.

For example, when a shipment status is updated for one of your users, our system will send an event that includes the shipment details and its new status. This allows you to deliver real-time updates to your users or process the payload as needed.

To enable event notifications, you must configure a webhook URL in your application settings. This can be done through our business web app by navigating to **Settings** > **API Settings** and updating your webhook URL. Proper configuration ensures your system receives event data promptly for further processing or integration with your workflow.

<figure><img src="/files/ZRdE9NqGItD1zw9gaj41" alt=""><figcaption><p>Add Your Webhook Url Here</p></figcaption></figure>

## Rules to Know

When setting up a webhook for your application, keep the following considerations in mind:

* Ensure that the endpoint is configured to accept **POST** requests.
* The timeout for our webhook is **30 seconds**. If your webhook processing is resource-intensive, consider handling it asynchronously to avoid timeouts.
* Perform a test request on your endpoint to confirm it can successfully receive and process the payload.

Additionally, in production, webhooks are sent every **30 minutes**, whereas in the Sandbox/Staging environment, webhooks are dispatched every **5 minutes**.

## Listening to Events

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const crypto = require('crypto');
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.raw({ type: 'application/json' }));

const secret = "your_secret_key";

app.post('/webhook', (req, res) => {
    const payload = req.body; // Raw body
    const receivedSignature = req.headers['x-signature'];

    // Generate the HMAC signature using the secret key
    const calculatedSignature = crypto
        .createHmac('sha256', secret)
        .update(payload)
        .digest('hex');

    // Compare the calculated signature with the received one
    if (crypto.timingSafeEqual(Buffer.from(calculatedSignature), Buffer.from(receivedSignature))) {
        // Signature is valid
        res.status(200).send('Valid signature');
    } else {
        // Invalid signature
        res.status(403).send('Invalid signature');
    }
});

app.listen(3000, () => {
    console.log('Server is listening on port 3000');
});

```

{% endtab %}

{% tab title="Python" %}

```python
import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
secret = b'your_secret_key'

@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.data
    received_signature = request.headers.get('X-Signature')

    # Generate the HMAC signature using the secret key
    calculated_signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()

    # Compare the calculated signature with the received one
    if hmac.compare_digest(calculated_signature, received_signature):
        return 'Valid signature', 200
    else:
        abort(403, 'Invalid signature')

if __name__ == '__main__':
    app.run(port=3000)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'sinatra'
require 'openssl'

secret = "your_secret_key"

post '/webhook' do
  request.body.rewind
  payload = request.body.read
  received_signature = request.env['HTTP_X_SIGNATURE']

  # Generate the HMAC signature using the secret key
  calculated_signature = OpenSSL::HMAC.hexdigest('sha256', secret, payload)

  # Compare the calculated signature with the received one
  if calculated_signature == received_signature
    status 200
    body 'Valid signature'
  else
    status 403
    body 'Invalid signature'
  end
end
```

{% endtab %}

{% tab title="Golang" %}

```go
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"
)

const secret = "your_secret_key"

func main() {
	http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
		// Get the raw POST body (payload)
		payload, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Cannot read payload", http.StatusBadRequest)
			return
		}

		// Get the signature from the headers
		receivedSignature := r.Header.Get("X-Signature")

		// Generate the HMAC signature using the secret key
		h := hmac.New(sha256.New, []byte(secret))
		h.Write(payload)
		calculatedSignature := hex.EncodeToString(h.Sum(nil))

		// Compare the calculated signature with the received one
		if hmac.Equal([]byte(calculatedSignature), []byte(receivedSignature)) {
			w.WriteHeader(http.StatusOK)
			fmt.Fprintf(w, "Valid signature")
		} else {
			w.WriteHeader(http.StatusForbidden)
			fmt.Fprintf(w, "Invalid signature")
		}
	})

	http.ListenAndServe(":3000", nil)
}

```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
// Receiver side

// The secret key (shared between the sender and receiver)
$secret = "your_secret_key";

// Get the signature from the headers
$receivedSignature = $_SERVER['HTTP_X_SIGNATURE'];

// Get the raw POST data (payload)
$payload = file_get_contents('php://input');

// Generate the hash using the same secret key
$calculatedSignature = hash_hmac('sha256', $payload, $secret);

// Compare the calculated signature with the received one
if (hash_equals($calculatedSignature, $receivedSignature)) {
    // Signature is valid, proceed with processing the webhook event
} else {
    // Invalid signature, reject the request
    http_response_code(403);
    exit('Invalid signature');
}
?>

```

{% endtab %}
{% endtabs %}

## Webhook Event Sample

```json
{
  "shipment": {
    "data": {
      "destination": "David Ola Ojeniyi St, Ibadan, Nigeria",
      "origin": "1 Kayode Otitoju St, Lekki Phase I 106104, Lekki, Nigeria",
      "reference": "5lqijs7rm9n0",
      "id": "565",
      "status": "Delivered",
      "extra": "pending,In progress,picked up,delivered."
    },
    "carrier": "DHL"
  }
}
```

### Status & Description

Here’s a breakdown of the shipment statuses in a tabular form:

| **Status**      | **Description**                                                                                   |
| --------------- | ------------------------------------------------------------------------------------------------- |
| **Pending**     | The shipment has been created, but the pickup process has not yet started. Awaiting confirmation. |
| **In Progress** | The shipment is currently being processed, and preparations are being made for pickup or transit. |
| **Picked Up**   | The shipment has been collected by the courier and is in transit to the destination.              |
| **Delivered**   | The shipment has been successfully delivered to the recipient at the specified location.          |
