# Webhooks

> Signed events that fire when sessions complete, outcomes are ready, actions are executed, or shares are accessed. HMAC-SHA256 signatures so you can verify authenticity. Available on Pro+ tiers.

**Canonical URL:** https://www.synthboard.ai/docs/webhooks  
**Markdown source:** https://www.synthboard.ai/docs/webhooks.md

## Setup

1. **MCP & API** in the sidebar → **Webhooks** tab → **Add endpoint**.
2. Provide an HTTPS URL.
3. Copy the signing secret. Store it in your application.
4. Subscribe to event types.
5. Test from the same panel.

## Event types

| Event | When it fires |
|---|---|
| `session.created` | A new session is started |
| `session.complete` | Session finishes successfully |
| `session.cancelled` | Session is cancelled mid-run |
| `outcomes.ready` | Synthesis is finalized and ready to fetch |
| `action.executed` | An action against an integration was executed |
| `action.undone` | An action was undone within the 30-second window |
| `share.created` | A share link was generated |
| `share.viewed` | A share link was accessed (sampled — not every view) |
| `decision.captured` | An explicit decision was captured against a session |

## Payload shape

Common envelope:

```json
{
  "id": "evt_2c4d8e...",
  "type": "session.complete",
  "created_at": "2026-05-01T12:34:56.789Z",
  "data": { /* event-specific */ },
  "delivery_attempt": 1,
  "api_version": "2026-05-01"
}
```

Per-event examples:

### `session.complete`
```json
{
  "id": "evt_...",
  "type": "session.complete",
  "data": {
    "session_id": "ses_...",
    "title": "...",
    "mode": "stress_test",
    "synth_count": 5,
    "rounds_completed": 3,
    "credits_used": 432,
    "consensus_score": 0.78,
    "outcomes_url": "https://www.synthboard.ai/api/v1/sessions/ses_.../outcomes"
  }
}
```

### `action.executed`
```json
{
  "id": "evt_...",
  "type": "action.executed",
  "data": {
    "session_id": "ses_...",
    "action_id": "act_...",
    "integration": "linear",
    "operation": "create_issue",
    "status": "completed",
    "undo_until": "2026-05-01T12:35:26.789Z",
    "external_id": "TEAM-1234"
  }
}
```

## Signature verification

Every request includes:

```
X-SynthBoard-Signature: t=1714562096,v1=5d6e7f...
```

Verify like this (Node example):

```js
const crypto = require('crypto');
const sig = req.headers['x-synthboard-signature'];
const [tPart, sPart] = sig.split(',');
const timestamp = tPart.split('=')[1];
const signature = sPart.split('=')[1];

const payload = `${timestamp}.${rawBody}`;
const expected = crypto
  .createHmac('sha256', process.env.SYNTHBOARD_WEBHOOK_SECRET)
  .update(payload)
  .digest('hex');

const valid = crypto.timingSafeEqual(
  Buffer.from(signature),
  Buffer.from(expected),
);

// Optional: reject if timestamp is too old (replay protection).
const age = Date.now() / 1000 - Number(timestamp);
if (age > 300) reject();
```

The signing secret is per-endpoint, not per-account.

## Retries

Failed deliveries (non-2xx response or timeout > 10s) retry with exponential backoff:

- Attempt 1: immediately
- Attempt 2: +30s
- Attempt 3: +5m
- Attempt 4: +30m
- Attempt 5: +2h
- Attempt 6: +6h

After 6 failed attempts the endpoint is marked unhealthy and you get an email. Re-enable from the same panel.

## Best practices

- Respond 2xx fast (under 1s); do work async.
- Use the event `id` for idempotency. Duplicates are possible.
- Verify signature before any side effect.
- Reject events older than 5 minutes.
- Log the raw payload for debugging — re-deliveries are available from the Webhooks panel for 30 days.

## Tier availability

- Free: not available.
- Pro: yes, up to 3 endpoints, all event types.
- Max: yes, up to 10 endpoints.
- Ultra: unlimited endpoints.
- Enterprise: unlimited + custom delivery (SQS, Pub/Sub, etc.).

## Related

- [API reference](https://www.synthboard.ai/docs/api)
- [Marketing API page](https://www.synthboard.ai/api)

## How to cite this page

> SynthBoard webhooks — https://www.synthboard.ai/docs/webhooks

Site: https://www.synthboard.ai
