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

# Webhooks

> Partner outbound HMAC webhooks (BizBionic → your systems).

# Outbound webhooks (partner)

BizBionic delivers events to your HTTPS endpoint with durable outbox, retry, and DLQ.

## Configure

```bash theme={null}
curl -s -X POST https://app.bizbionic.com/rpc \
  -H "Authorization: Bearer $BB_PARTNER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"partner.configureWebhook","params":{"webhook_url":"https://partner.example.com/hooks/bizbionic","webhook_secret":"whsec_replace"}}'
```

### Drain the outbox

After events enqueue (e.g. successful `partner.send`), deliver pending rows:

```bash theme={null}
curl -s -X POST https://app.bizbionic.com/rpc \
  -H "Authorization: Bearer $BB_PARTNER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"partner.processOutbox","params":{"limit":20}}'
```

You own a public HTTPS endpoint. Local tunnels (e.g. cloudflared) work for development.

## Signature

Header:

```http theme={null}
X-BizBionic-Signature: t=<unix_seconds>,v1=<hex_hmac_sha256>
```

Signed payload:

```text theme={null}
{timestamp}.{raw_body}
```

Verify with ±5 minute skew. Support dual secrets during rotation.

### Node verify sketch

```js theme={null}
import crypto from 'node:crypto';

function verify(secret, rawBody, header, toleranceSec = 300) {
  const m = /t=(\d+),v1=([a-f0-9]+)/i.exec(header || '');
  if (!m) return false;
  const t = parseInt(m[1], 10);
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - t) > toleranceSec) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return expected === m[2].toLowerCase();
}
```

## Events (examples)

| Event                     | When                               |
| ------------------------- | ---------------------------------- |
| `message.finalized`       | After successful partner send path |
| `contact.opted_out`       | After STOP handled in-platform     |
| inbound / trust / balance | As wired                           |

STOP is applied **before** partner outbox notify.

## Next steps

* [Partner platform](/connect/partner-platform)
* [Errors](/errors)
