> ## 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 title="Highlight" icon="lightbulb" iconType="duotone" color="#ca8b04">
  Webhooks allow you to set up a notification system that can be used to receive
  updates on certain requests made to the GetEquity API.
</Card>

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

## 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`:

<CodeGroup>
  ```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);
  ?>

  ```
</CodeGroup>

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 Avoid long-running tasks>
  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.
</Warning>

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

<CodeGroup>
  ```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)
  ```
</CodeGroup>

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