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

# Webhooks

> How Terra delivers webhooks with HMAC signing and retry logic

# Webhooks

> Webhooks notify external systems when submissions are created or updated.

## Webhook Configuration

```sql theme={null}
CREATE TABLE webhook_configs (
  id UUID PRIMARY KEY,
  form_id UUID REFERENCES forms(id),
  name TEXT NOT NULL,
  url TEXT NOT NULL,
  events TEXT[] DEFAULT ARRAY['submission.created'],
  secret TEXT NOT NULL,  -- HMAC signing key
  headers JSONB DEFAULT '{}',
  is_active BOOLEAN DEFAULT true
);
```

## Events

| Event                | Trigger             |
| -------------------- | ------------------- |
| `submission.created` | New submission      |
| `submission.updated` | Submission modified |
| `status.changed`     | Status updated      |

## HMAC Signing

Every webhook is signed with HMAC-SHA256:

```typescript theme={null}
function signPayload(payload: string, secret: string): string {
  return crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
}

// Headers sent:
// X-Webhook-Signature: sha256=abc123...
// X-Webhook-Timestamp: 1704000000
```

## Verification (Receiver)

```typescript theme={null}
function verifyWebhook(
  payload: string,
  signature: string,
  timestamp: string,
  secret: string
): boolean {
  // Check timestamp isn't too old (prevent replay)
  const age = Date.now() / 1000 - parseInt(timestamp);
  if (age > 300) return false; // 5 minute window

  // Verify signature
  const expected = signPayload(`${timestamp}.${payload}`, secret);
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}
```

## Retry Strategy

Webhooks use the async queue with 5 retries and exponential backoff.

***

<CardGroup cols={2}>
  <Card title="Queue Architecture" icon="clock" href="/core-systems/async/queue-architecture">
    How retries work
  </Card>

  <Card title="Background Jobs" icon="gears" href="/core-systems/async/background-jobs">
    Queue processing
  </Card>
</CardGroup>
