# 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.          |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developer.goshiip.com/getting-started/webhook.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
