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

# Webhooks

> Get real-time notifications about events in your Drivly account.

export const StoryBoard = ({children}) => <div className="p-6 bg-gray-100/50 dark:bg-gray-800/25 rounded">
    <p className="text-base py-2 leading-6">{children}</p>
  </div>;

Webhooks are a great way for you to get up-to-date feedback about what happens on your Drivly account. You can use
webhooks to get notifications about events such as when an invoice is created, a service order completes, and more.

They are a powerful tool that can help you automate your workflows and keep your systems in sync with your Drivly account.

## Creating a Webhook

Our API supports creating webhooks programmatically. You can create a webhook by sending a POST request to the `/webhooks`
endpoint.

<Info>
  Want to listen to multiple events of the same resource? You can use wildcards in the event name. For example, `service.*`,
  will match all events related to the `service` resource and forward them to your server!
</Info>

```curl Creating a Webhook theme={null}
curl -X POST \
  https://commerce.driv.ly/api/webhooks \
  -H 'Authorization: Bearer apiKey or JWT \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com/webhook",
    "events": [ "invoice.created", "service.*" ],
    "notes": "A demo webhook for showcasing the Drivly API!"
  }'
```

```json Webhook Response theme={null}
{
  "id": "webhook_EkhLoMcTSiFBwtZ",
  "url": "https://example.com/webhook",
  "events": [ "invoice.created", "service.*" ],
  "notes": "A demo webhook for showcasing the Drivly API!",
  "secret": "UujwbzDIqpOjZVhtThcVjZgnKJXqDFzVyV"
}
```

<Warning>
  Make sure to keep your webhook secret safe. Treat it like a password, and never expose it to the public.
</Warning>

Once you've created your webhook, you'll start receiving notifications about the events you've subscribed to:

<CodeGroup>
  ```json Schema theme={null}
  {
    // Each event gets a unique ID so you can
    // Repeat, or review the event in the dashboard.
    "id": "event_EkhLoMcTSiFBwtZ",
    // The name of the event that triggered the webhook.
    // This can be used to filter events on your server.
    "event": "invoice.created",
    // The timestamp of the event.
    "timestamp": "2021-09-01T12:00:00Z",
    // The type of the event, this can be used to
    // further categorize on your server.
    "type": "reconditioning",
    // The body of the event, containing the data
    // that triggered the webhook.
    "data": {
      ...
    }
  }
  ```

  ```json Full Example theme={null}
  {
    "id": "event_EkhLoMcTSiFBwtZ",
    "event": "service.completed",
    "timestamp": "2021-09-01T12:00:00Z",
    "type": "reconditioning",
    // Data is specific to the event that triggered the webhook.
    // This can be an invoice, a service order, or any other resource.
    "data": {
      "id": "order_cuRxvZfmcgAchV",
      "status": "completed",
      "estimatedTime": "2024-01-01T12:00:00Z",
      "receiptLink": "https://checkout.driv.ly/quote_sf7Y7c",
      "paid": true,
      "total": 350,
      "items": [
        {
          "service": "reconditioning",
          "price": 350,
          "name": "Reconditioning",
          "description": "fault_EbaCojKxsJL",
          "quantity": 1
        }
      ],
      "history": [
        {
          "status": "pending",
          "paid": true,
          "time": "2024-01-01T12:00:00Z"
        },
        {
          "status": "in-progress",
          "time": "2024-01-01T12:00:00Z"
        },
        {
          "status": "completed",
          "time": "2024-01-01T12:00:00Z"
        }
      ]
    }
  }
  ```
</CodeGroup>

```http Headers Example theme={null}
POST /webhook HTTP/1.1

Content-Type: application/json
X-Drivly-Signature: t=1714749612,hmac=a9e4b71b06c4d18be93306ab0bcd9f36741ac3a18b2d2748c5b173ac12f90613
```

## Verifying Webhook Signatures

To ensure that the webhook notification you receive is from Drivly, you can verify the signature of the request. The
signature is a HMAC SHA-256 hash of the request body, using the webhook secret as the key.

To verify the signature, you can follow these easy steps:

<Steps>
  <Step title="Prepare the signature">
    The signature is a HMAC-SHA256 hash of the request body with the time of the event prepended to it. This is to
    prevent replay attacks. You can find the signature in the `X-Drivly-Signature` header of the request.

    ```http theme={null}
    X-Drivly-Signature:
      t=1714749612,
      hmac=a9e4b71b06c4d18be93306ab0bcd9f36741ac3a18b2d2748c5b173ac12f90613
    ```

    <Note>
      For clarity, we've split the signature into two lines. In practice, it will be a single line.
    </Note>

    The signature is a string containing two parts separated by a comma. The first part is the timestamp of the event,
    and the second part is the HMAC-SHA256 hash of the request body.

    You can use the timestamp to check if the event is recent and the hash to verify the integrity of the request.
  </Step>

  <Step title="Calculate the hash">
    The hash is a concatenation of the timestamp and the request body. You can calculate the hash using the following
    formula:

    ```js theme={null}
    const mixin = `${timestamp}.${body}`
    ```

    For example, if the timestamp is `1714749612` and the body is `{"event": "invoice.created"}`, the mixin string will
    be `1714749612.{"event": "invoice.created"}`.
  </Step>

  <Step title="Verify the signature">
    To verify the signature, you can calculate the HMAC-SHA256 hash of the mixin string using the webhook secret as the
    key. If the calculated hash matches the hash in the `X-Drivly-Signature` header, the request is valid.

    ```js theme={null}
    const calculatedHash = hmacSHA256(mixin, webhookSecret)
    const isValid = calculatedHash === receivedHash
    ```

    If the calculated hash is equal to the received hash, the request is valid, and you can process the event.

    <Warning>
      If the time is more than 5 minutes in the past, you should reject the request. This means the event is most likely stale,
      or the request is a replay attack.

      <b>If the request was resent, either by the dashboard, or the API, then the timestamp will be set to the time of the resend</b>.
    </Warning>
  </Step>
</Steps>

## Resending Events

If you missed an event, had an error, or just want to test your webhook, you can resend events from the dashboard, or from the API. Since
the events each have a unique ID, you can resend them individually, or in bulk. You can also determine a time-period to resend events from.

<Info>
  To best mimic the behavior of real-time events, these events that are resent will be sent one at a time. This is to prevent
  overwhelming your server with a large number of events at once, as well as to ensure that the order of events is maintained.
</Info>

<CodeGroup>
  ```curl Resending an Event theme={null}
  curl -X POST \
    https://commerce.driv.ly/api/webhooks/resend \
    -H 'Authorization: Bearer apiKey or JWT \
    -H 'Content-Type: application/json' \
    -d '[
      "event_EkhLoMcTSiFBwtZ",
      "event_OCKvyLqOSwwqaLv",
    ]'
  ```

  ```curl Resending Events by Time theme={null}
  curl -X POST \
    https://commerce.driv.ly/api/webhooks/resend \
    -H 'Authorization: Bearer apiKey or JWT \
    -H 'Content-Type: application/json' \
    -d '{
      "from": "2021-09-01T12:00:00Z",
      "to": "2021-09-01T12:00:00Z"
    }'
  ```

  ```json Resend Response theme={null}
  {
    "message": "Events have been queued for resending.",
    "queued": 2
  }
  ```
</CodeGroup>

## Automatic Retries & Backoff

We understand that sometimes your server might be down, or there might be network issues that prevent you from receiving
webhook notifications. If we dont receive a `200 OK` response from your server, we will retry sending the webhook up to 5 times.

The first retry will be sent after 1 minute, and each subsequent retry will be sent after 2, 4, 8, and 16 minutes. If we still
don't receive a `200 OK` response after 5 retries, we will stop sending the webhook. You can always resend the event manually
from the dashboard or the API.

## Logs

You can view the logs of your webhooks from the dashboard. The logs contain information about the events that were sent, the
response from your server, and the status of the webhook. You can use the logs to debug issues with your webhooks and ensure
that you're receiving all the notifications you expect.

```json Logs Example theme={null}
{
  "id": "log_EkhLoMcTSiFBwtZ",
  "webhook": "webhook_EkhLoMcTSiFBwtZ",
  "event": "event_EkhLoMcTSiFBwtZ",
  "status": "success",
  "response": {
    "status": 200,
    "body": "OK"
  },
  "timestamp": "2021-09-01T12:00:00Z"
}
```

***

## Next Steps

<Card title="Reference – Webhooks" icon="link" href="/commerce/endpoints/webhooks/find-paginated-webhooks">
  Explore the Listings Search API endpoint and parameters.
</Card>
