# 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: 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://docs.hypertune.com/integrations/webhooks/handling-webhooks.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.
