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

# Webhook setup

> Register your webhook endpoint, verify HMAC signatures, and handle outbound CurbWaste events in your application.

Wire an HTTPS receiver for **inbound** deliveries from CurbWaste. Delivery auth is HMAC (`whsec_…`), not your `cw_live_…` API key. Event catalog and trust model: [Webhooks](/concepts/webhooks).

## Prerequisites

* Access to the [Developer Platform](https://platform.curbwaste.com)
* A publicly reachable **HTTPS** receiver URL
* An API key with write scope if you will trigger a test event (e.g. `customers:write`) — see [Authentication](/authentication)

## Step 1 — Register your endpoint

In the Developer Platform **Webhooks** page:

1. Create an endpoint with your HTTPS URL
2. Select the events to receive (for example `customer.created`, `customer.updated`, `location.created`, `location.updated`)
3. Copy **`signingSecret`** (`whsec_…`) immediately — it is only shown once

```bash theme={null}
export CURBWASTE_WEBHOOK_SECRET='whsec_…'
```

## Step 2 — Handle and verify deliveries

Expose an HTTPS route that accepts `POST` with a **raw** JSON body. Verify `X-CurbWaste-Signature: t=<unix>,s=<hex>` against `{t}.{rawBody}` with HMAC-SHA256. Reject stale timestamps if you enforce skew (see [Webhooks](/concepts/webhooks) trust model).

The samples below show verification shape only — register a public HTTPS URL in the Developer Platform, not a loopback address.

<CodeGroup>
  ```javascript express-webhook.js theme={null}
  const express = require('express');
  const crypto = require('crypto');
  const app = express();

  app.use('/hooks', express.raw({ type: 'application/json' }));

  app.post('/hooks', (req, res) => {
    const header = req.headers['x-curbwaste-signature'] || '';
    const secret = process.env.CURBWASTE_WEBHOOK_SECRET;
    const rawBody = req.body; // Buffer

    const parts = Object.fromEntries(
      String(header).split(',').map((p) => p.trim().split('=')),
    );
    const t = parts.t;
    const s = parts.s;
    if (!t || !s) return res.status(401).send('bad header');

    const expected = crypto
      .createHmac('sha256', secret)
      .update(`${t}.${rawBody.toString('utf8')}`)
      .digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(s, 'hex'))) {
      return res.status(401).send('bad signature');
    }

    const event = JSON.parse(rawBody.toString('utf8'));
    console.log(event.type, event.id, event.data?.id);
    // switch (event.type) { case 'customer.created': ... }

    res.status(204).end();
  });

  app.listen(process.env.PORT || 3000);
  ```

  ```python flask-webhook.py theme={null}
  import hashlib, hmac, json, os
  from flask import Flask, request, abort

  app = Flask(__name__)

  @app.post('/hooks')
  def hooks():
      header = request.headers.get('X-CurbWaste-Signature', '')
      secret = os.environ['CURBWASTE_WEBHOOK_SECRET']
      raw = request.get_data()

      parts = dict(p.strip().split('=', 1) for p in header.split(',') if '=' in p)
      t, s = parts.get('t'), parts.get('s')
      if not t or not s:
          abort(401)

      expected = hmac.new(secret.encode(), f'{t}.{raw.decode()}'.encode(), hashlib.sha256).hexdigest()
      if not hmac.compare_digest(expected, s):
          abort(401)

      event = json.loads(raw)
      print(event['type'], event['id'])
      return ('', 204)
  ```
</CodeGroup>

## Step 3 — Trigger an event

Easiest path: create a customer with `customers:write`. Location writes emit `location.*` the same way — see the [events table](/concepts/webhooks#events).

```bash theme={null}
export CURBWASTE_API_KEY="cw_live_…"

curl -X POST "https://api.curbwaste.com/v1/customers" \
  -H "Authorization: Bearer $CURBWASTE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"displayName":"Webhook Test Co","email":"ops@example.com"}'
```

Your receiver should log `customer.created` with headers `X-CurbWaste-Event-Id` / `X-CurbWaste-Event-Type`.

## Checklist

1. Endpoint registered in the Developer Platform; secret saved
2. Signature verifies with `t.` + raw body
3. Handler branches on `event.type` (not `event.event`)
4. Respond `2xx` quickly; idempotent on envelope `id`

<Note>
  Delivery is queued with automatic retries (up to 8 attempts, exponential backoff from 60 seconds).
</Note>

## Related

<CardGroup cols={3}>
  <Card title="Webhooks" icon="book" href="/concepts/webhooks">
    Events, envelope, HMAC trust model, and retries.
  </Card>

  <Card title="Webhook payloads" icon="code" href="/api-reference/webhooks">
    OpenAPI schemas for each outbound event.
  </Card>

  <Card title="Sync customers" icon="arrows-rotate" href="/guides/sync-customers">
    Outbound push using the UUID join key; pair with webhooks for inbound.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.