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

# Webhooks

> Receive real-time event notifications from Shiftsheet

Webhooks allow you to build or set up integrations that subscribe to certain events on Shiftsheet. When one of those events is triggered, we'll send an HTTP POST payload to the webhook's configured URL.

## Configuring Webhooks

1. Log in to your Shiftsheet Dashboard as a **Company Admin**.
2. Navigate to **Settings > Integrations & API**.
3. Under the **Webhooks** tab, click **Add Endpoint**.
4. Enter the URL where you want to receive payloads (e.g., `https://your-domain.com/webhooks/shiftsheet`).
5. Upon creation, you will receive a **Signing Secret**. Copy this secret immediately; it is required to verify the authenticity of the incoming payloads.

## Payload Structure

All webhooks share a standard JSON payload format containing the event type, timestamp, and the associated data.

```json theme={null}
{
  "event": "timesheet.approved",
  "timestamp": "2026-05-18T00:00:00.000Z",
  "data": {
    "id": "ts_12345",
    "userId": "usr_9876",
    "status": "APPROVED",
    "totalHours": 40.5
  }
}
```

## Security & Signature Verification

To ensure that the request truly came from Shiftsheet and the payload hasn't been tampered with, we include an HMAC SHA256 signature in the `X-Shiftsheet-Signature` header of every request.

We also include the `X-Shiftsheet-Event` header, making it easy to route the event without parsing the body first.

### Example: Verifying the Signature in Node.js

You can verify the signature using the built-in `crypto` module in Node.js. Compare the computed HMAC against the value in the header.

```javascript Node.js theme={null}
const crypto = require('crypto');
const express = require('express');
const app = express();

// The secret provided when you created the webhook
const WEBHOOK_SECRET = 'whsec_your_secret_here';

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-shiftsheet-signature'];
  const event = req.headers['x-shiftsheet-event'];
  
  // The signature format is 'sha256=<hash>'
  const expectedSignature = `sha256=${crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex')}`;

  if (crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
    // Signature is valid
    console.log(`Received secure event: ${event}`);
    
    const payload = JSON.parse(req.body);
    // Process the payload...
    
    res.status(200).send('OK');
  } else {
    // Invalid signature
    res.status(401).send('Invalid signature');
  }
});
```

<Warning>
  Always compute the HMAC on the raw request body before parsing it as JSON. Whitespace differences caused by JSON parsers will result in an invalid signature hash.
</Warning>
