> For the complete documentation index, see [llms.txt](https://docs.hypertune.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hypertune.com/integrations/webhooks/handling-webhooks.md).

# Handling webhooks

This page explains how webhook events from Hypertune should be handled by your server.

## Request structure

When an event occurs in your project, we'll notify all the subscribed active webhooks.

We do this by sending them each a POST request to their payload URLs. This request has a body that contains information about the event, for example:

```json
{
  "id": "QAq7JB7nE5tY8mS99qsmM",
  "createdAt": "2023-04-17T12:02:26.095Z",
  "type": "NEW_COMMIT",
  "project": {
    "id": "2489",
    "name": "demo-webhook project"
  },
  "commit": {
    "id": "2792",
    "message": "v5"
  },
  "user": {
    "id": "2431",
    "displayName": "Miraan Tabrez",
    "email": "miraan@hypertune.com"
  }
}
```

Additionally, it will include the `X-Hypertune-Signature` header which will be the HMAC-SHA-256 digest in hexadecimal format of the raw body, using the webhook secret.

We will retry webhook sends with exponential backoff until a limit, so your implementation should be idempotent and handle missing hooks.

## Processing webhooks

To process a request you should:

1. Verify it is from Hypertune. To do this, validate that the `X-Hypertune-Signature` matches the definition above, using the secret you configured when setting up your webhook.
2. Within 10 seconds, respond with a successful status code: one in the range 200-299 inclusive.

## JavaScript example

```typescript
import { createHmac, timingSafeEqual } from 'node:crypto'
import express from 'express'

// Do not hardcode in a production application
const WEBHOOK_SECRET = '1de87274cc2ddba774cbcec5a9ad8727'

const app = express()

// If you want to use body parser JSON, do something like this:
// https://github.com/stripe/stripe-node/issues/341#issuecomment-304733080
app.use(express.raw({ type: '*/*' }))

app.post('/', (req, res) => {
  const actualSignature = [
    req.headers['x-hypertune-signature'],
  ].flat()[0]
  if (!actualSignature) {
    console.log(
      'Rejected webhook request with missing signature'
    )
    res.status(401).send('Missing X-Hypertune-Signature header')
    return
  }

  const expectedSignature = createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body.toString())
    .digest('hex')
  if (
    actualSignature.length !== expectedSignature.length ||
    !timingSafeEqual(
      Buffer.from(actualSignature),
      Buffer.from(expectedSignature)
    )
  ) {
    console.log('Rejected webhook request with bad signature')
    res.status(403).send('Bad signature')
    return
  }

  console.log('Received valid webhook event')
  const data = JSON.parse(req.body.toString())
  // TODO: Do something interesting with the data here

  res.sendStatus(204)
})

const port = 8080

app.listen(port)
console.log(`Server listening at http://localhost:${port}`)
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.hypertune.com/integrations/webhooks/handling-webhooks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
