> ## Documentation Index
> Fetch the complete documentation index at: https://getequity.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> [card] Highlight
>
> Webhooks allow you to set up a notification system that can be used to receive
> updates on certain requests made to the GetEquity API.

## Introduction

Generally, when you make a request to an API endpoint, you expect to get a near-immediate response. However, some requests may take a long time to process, which can lead to timeout errors. In order to prevent a timeout error, a pending response is returned. Since your records need to be updated with the final state of the request, you need to listen to events by using a webhook URL.

> [!TIP]
>
> We recommend that you use webhook to provide value to your customers over
> using callbacks or polling. With callbacks, we don't have control over what
> happens on the customer's end. Neither do you. Callbacks can fail if the
> network connection on a customer's device fails or is weak or if the device
> goes off after a transaction.

## Create a webhook URL

A webhook URL is simply a `POST` endpoint that a resource server sends updates to. The URL needs to parse a JSON request and return a `200 OK`:

```javascript Node theme={null}
// Using Express
app.post('/webhook-url', function (req, res) {
  // Retrieve the request's body
  const event = req.body;
  // Do something with event
  res.send(200);
});
```

```php PHP theme={null}

<?php
  // Assuming you're using a basic PHP script
  // Retrieve the request's body
  $request_body = file_get_contents('php://input');
  // Decode the JSON data
  $event = json_decode($request_body, true);

  // Do something with the event

  // Send a response
  http_response_code(200);
?>

```

When your webhook URL receives an event, it needs to parse and acknowledge the event. Acknowledging an event means returning a `200 OK` in the HTTP header. Without a `200 OK` in the response header, we’ll keep sending events for the next 15mins

> [!WARNING]
>
> If you have long-running tasks in your webhook function, you should
> acknowledge the event before executing the long-running tasks. Long-running
> tasks will lead to a request timeout and an automatic error response from your
> server. Without a 200 OK response, we retry as described in the paragraph
> above.

## Verify event origin

Since your webhook URL is publicly available, you need to verify that events originate from GetEquity and not a bad actor. Signature validation is a way to ensure events to your webhook URL are from GetEquity:

## Signature Validation

Events sent from GetEquity carry the `x-getequity-signature` header. The value of this header is a `HMAC SHA256` signature of the event payload signed using your secret key. Verifying the header signature should be done before processing the event:

```javascript Node theme={null}
const crypto = require('crypto');
const secret = process.env.SECRET_KEY;

. . .
. . .

// Using Express
app.post('/webhook-url', function (req, res) {
  //validate event
  const hashParams = Buffer.from(JSON.stringify(req.body));
  const hash = crypto
    .createHmac('sha256', secret)
    .update(hashParams)
    .digest('base64');
  if (hash == req.headers['x-getequity-signature']) {
    // Retrieve the request's body
    const event = req.body;
    // Do something with event
  }
  res.send(200);
});
```

```python Python theme={null}
import hashlib
import hmac
import base64
from flask import Flask, request, Request

app = Flask(__name__)


def verify_webhook(request: Request, secret: str):
    # Extract x-getequity-signature and x-getequity-signature-2 headers from the request
    hmac_header = request.headers.get("x-getequity-signature")

    # Create a hash based on the raw body
    hash = base64.b64encode(
        hmac.new(
            secret.encode(), request.data, hashlib.sha256
        ).digest()
    ).decode()

    # Compare the created hash with the value of the x-getequity-signature
    # Also check x-getequity-signature-2 header in case the secret was rolled
    return hash == hmac_header


@app.route("/<path:path>", methods=["POST"])
def handle(path):
    if not verify_webhook(request):
        return {"status": "UNAUTHORIZED"}, 403
    else:
        return {"status": "ACCEPTED"}, 200


if __name__ == "__main__":
    app.run(debug=True, port=3031)
```

## Go live checklist

Now that you’ve successfully created your webhook URL, here are some ways to ensure you get a delightful experience:

1. Add the webhook URL on your [GetEquity dashboard](https://business.getequity.io/onboarding/signin)
2. Ensure your webhook URL is publicly available (localhost URLs cannot receive events)
3. Test your webhook to ensure you’re getting the JSON body and returning a `200 OK` HTTP response
4. If we don’t get a `200 OK` HTTP response from your webhooks, we flagged it as a failed attempt
5. Failed attempts are retried every 5 minutes for the next 15 minutes

## Supported Events

Every webhook payload is a single JSON object containing a `webhook_event` field that identifies the event, plus event-specific fields. Select an event from the sidebar for its full payload reference.

## Types of Events

Below are the events we currently send. This list might be expanded accordingly in the future.

| Event | Recipient organisation | Triggered when |
| :- | :- | :- |
| `member.created` | Parent organisation | A dealroom user is provisioned under the organisation |
| `order.created` | Order owner's organisation | A `Buy` or `Sell` order is placed on the exchange |
| `order.cancelled` | Order user's organisation (only `organisation` owners) | An order is cancelled |
| `transaction.created` | Buyer organisation **and** seller organisation | A buy/sell match is fulfilled (fired once per side) |
| `wallet.funded` | User's organisation | A wallet topup payment is processed successfully |
| `listing.created` | Issuing organisation | A token created from an approved token request is persisted |
| `token.transferred` | Sender's org (debit) **and** receiver's org (credit) | A blockchain token transfer is completed |
| `withdrawal.created` | User's organisation | A user creates a withdrawal request |
| `withdrawal.approved` | Withdrawal owner's organisation | An admin approves a withdrawal request |
| `loan.created` | Creditor organisation | A user is issued a credit loan |
| `loan.repaid` | Creditor organisation | A borrower fully repays a loan |
| `esop.transferred` | Issuing organisation | An employee stock transfer request is approved |
| `token.gifted` | Sender's organisation | A sender gifts tokens to another user |
| `token.gift.accepted` | Receiver's organisation | A recipient accepts a pending token gift |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.