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

# Webhooks

> Receive real-time notifications when events occur in Hey Chocolate.

Webhooks deliver real-time HTTP callbacks when events occur in Hey Chocolate.

## Setup

Configure endpoints in the [Dashboard](https://dashboard.heychocolate.com) or via the API:

```graphql theme={null}
mutation {
  createWebhook(input: {
    url: "https://your-app.com/webhooks/heychocolate"
    events: [PRODUCT_IMPACT_UPDATED, ORDER_CARBON_CALCULATED, DPP_GENERATED]
    secret: "whsec_your_signing_secret"
  }) {
    id
    url
    events
    status
  }
}
```

## Payload format

```json theme={null}
{
  "id": "evt_abc123",
  "type": "product.impact_updated",
  "createdAt": "2025-09-01T12:00:00Z",
  "data": {
    "productId": "prod_abc123",
    "previousScore": { "co2Equivalent": 14.2 },
    "newScore": { "co2Equivalent": 12.4 },
    "reason": "EPD data updated"
  }
}
```

## Signature verification

Verify the `X-HC-Signature` header to ensure requests are from Hey Chocolate:

```javascript theme={null}
import crypto from 'crypto';

function verifyWebhookSignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}
```

## Event types

### Product events

| Event                    | Description                   |
| ------------------------ | ----------------------------- |
| `product.created`        | A new product was added       |
| `product.updated`        | Product data was modified     |
| `product.impact_updated` | Impact score was recalculated |
| `product.deleted`        | A product was removed         |

### Order events

| Event                     | Description                         |
| ------------------------- | ----------------------------------- |
| `order.created`           | A new order was recorded            |
| `order.carbon_calculated` | Order carbon footprint was computed |

### Compliance events

| Event                   | Description                          |
| ----------------------- | ------------------------------------ |
| `dpp.generated`         | Digital Product Passport was created |
| `dpp.updated`           | DPP was updated                      |
| `epd.ingested`          | EPD document was parsed              |
| `epd.validation_failed` | EPD parsing failed                   |
| `mki.calculated`        | MKI score was computed               |

### Report events

| Event              | Description                   |
| ------------------ | ----------------------------- |
| `report.generated` | CSRD/ESG report was generated |

### Visualisation events

| Event              | Description              |
| ------------------ | ------------------------ |
| `render.completed` | Asset rendering finished |
| `render.failed`    | Asset rendering failed   |

## Retry policy

| Attempt | Delay      |
| ------- | ---------- |
| 1       | Immediate  |
| 2       | 1 minute   |
| 3       | 5 minutes  |
| 4       | 30 minutes |
| 5       | 2 hours    |
| 6       | 12 hours   |

After 6 failed attempts, the webhook is marked as `FAILED` and can be re-enabled from the Dashboard.

## Managing webhooks

```graphql theme={null}
query {
  webhooks {
    id
    url
    events
    status
    lastDelivery { status responseCode deliveredAt }
  }
}

mutation {
  deleteWebhook(id: "wh_abc123") { success }
}
```
