---
title: Webhook Events
description: "Configure webhook endpoints to receive real-time notifications when documents are received, delivered, or fail in FileRelay."
---

# Webhook Events

FileRelay can send real-time HTTP notifications to your application when key events occur. Webhooks enable you to build custom integrations, trigger workflows, and monitor document flow from your own systems.

## Event Types

FileRelay sends webhooks for the following events:

| Event                  | Fired When                                              |
|-----------------------|---------------------------------------------------------|
| `document.received`   | A new document is received from SAP via ArchiveLink     |
| `document.forwarded`  | A document is successfully delivered to a destination    |
| `document.failed`     | A document delivery fails after all retry attempts      |

### document.received

Fired immediately when FileRelay accepts a document from SAP. This event is sent before delivery to any destination begins.

### document.forwarded

Fired each time a document is successfully delivered to a destination. If a document fans out to three destinations, you receive three separate `document.forwarded` events -- one for each successful delivery.

### document.failed

Fired when delivery to a destination has exhausted all retry attempts and the document is marked as failed. Like `document.forwarded`, this is per-destination -- a document can trigger `document.forwarded` for some destinations and `document.failed` for others.

## Webhook Payload Structure

All webhook events share a common payload structure:

```json
{
  "id": "evt_abc123def456",
  "type": "document.received",
  "created_at": "2026-04-04T10:30:00Z",
  "workspace_id": "ws_xyz789",
  "data": {
    ...
  }
}
```

| Field          | Type   | Description                                |
|---------------|--------|--------------------------------------------|
| `id`          | string | Unique event ID (for idempotency)          |
| `type`        | string | Event type                                 |
| `created_at`  | string | ISO 8601 timestamp of the event            |
| `workspace_id`| string | The workspace where the event occurred     |
| `data`        | object | Event-specific payload (see below)         |

## Example Payloads

### document.received

```json
{
  "id": "evt_recv_001",
  "type": "document.received",
  "created_at": "2026-04-04T10:30:00Z",
  "workspace_id": "ws_xyz789",
  "data": {
    "document_id": "doc_a1b2c3d4",
    "doc_id": "4A3F8B2C1D7E9F0A",
    "repo_id": "Z1",
    "comp_id": "data",
    "content_type": "application/pdf",
    "size_bytes": 245780,
    "received_at": "2026-04-04T10:30:00Z"
  }
}
```

### document.forwarded

```json
{
  "id": "evt_fwd_002",
  "type": "document.forwarded",
  "created_at": "2026-04-04T10:30:02Z",
  "workspace_id": "ws_xyz789",
  "data": {
    "document_id": "doc_a1b2c3d4",
    "doc_id": "4A3F8B2C1D7E9F0A",
    "repo_id": "Z1",
    "destination_id": "dest_s3prod",
    "destination_name": "S3 Production Archive",
    "connector_type": "s3",
    "delivered_at": "2026-04-04T10:30:02Z",
    "path": "/2026/04/04/4A3F8B2C1D7E9F0A.pdf",
    "attempts": 1
  }
}
```

### document.failed

```json
{
  "id": "evt_fail_003",
  "type": "document.failed",
  "created_at": "2026-04-04T14:30:00Z",
  "workspace_id": "ws_xyz789",
  "data": {
    "document_id": "doc_x9y8z7w6",
    "doc_id": "9C1D3E5F7A2B4D6E",
    "repo_id": "Z1",
    "destination_id": "dest_sharepoint",
    "destination_name": "SharePoint Team Site",
    "connector_type": "sharepoint",
    "failed_at": "2026-04-04T14:30:00Z",
    "attempts": 5,
    "last_error": "Authentication failed: OAuth token expired and could not be refreshed."
  }
}
```

## Configuring Webhooks

To set up a webhook endpoint:

1. Navigate to **Settings** in the sidebar
2. Select the **Webhooks** tab
3. Click **Add Webhook**
4. Enter the **URL** of your endpoint (must be HTTPS)
5. Select which **event types** to subscribe to
6. Click **Save**

> **REPLACE:** Screenshot of the webhook configuration dialog with URL field and event type checkboxes

Your endpoint must:

- Accept `POST` requests with a JSON body
- Respond with a `2xx` status code within 30 seconds
- Be accessible over HTTPS

## Signature Verification

Every webhook request includes a signature in the `X-FileRelay-Signature` header. Use this to verify that the request genuinely came from FileRelay and was not tampered with.

### How It Works

FileRelay signs the request body using HMAC-SHA256 with your webhook signing secret:

```
X-FileRelay-Signature: sha256=a1b2c3d4e5f6...
```

### Verifying the Signature

1. Get the raw request body (before any JSON parsing)
2. Compute HMAC-SHA256 using your webhook signing secret as the key
3. Compare the computed signature with the value in `X-FileRelay-Signature`

### Example (Node.js)

```javascript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const computed = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(computed),
    Buffer.from(signature)
  );
}

// In your webhook handler:
app.post('/webhooks/filerelay', (req, res) => {
  const signature = req.headers['x-filerelay-signature'];
  const isValid = verifyWebhookSignature(
    req.rawBody,
    signature,
    process.env.FILERELAY_WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.rawBody);
  // Process the event...

  res.status(200).send('OK');
});
```

### Example (Python)

```python
import hmac
import hashlib

def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
    computed = 'sha256=' + hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(computed, signature)
```

### Finding Your Signing Secret

Your webhook signing secret is displayed when you create the webhook endpoint. Like API keys, it is shown only once. If you lose it, you can regenerate it from the webhook settings (which invalidates the old secret).

## Retry Behavior

If your endpoint does not respond with a `2xx` status code within 30 seconds, FileRelay retries the webhook delivery:

| Attempt | Delay          |
|---------|----------------|
| 1       | Immediate      |
| 2       | 1 minute       |
| 3       | 5 minutes      |
| 4       | 30 minutes     |
| 5       | 2 hours        |

After 5 failed attempts, the webhook event is marked as failed and no further retries are made. Failed webhook deliveries are visible in the webhook settings page.

## Idempotency

Each webhook event has a unique `id` field. Your application should use this ID to handle duplicate deliveries idempotently. If the same event is delivered more than once (due to retries or network issues), your handler should recognize the duplicate and avoid processing it again.

## Best Practices

- **Always verify signatures** to ensure webhook authenticity
- **Respond quickly** with a `2xx` status code, then process the event asynchronously. If your processing takes longer than 30 seconds, you risk timeout-triggered retries.
- **Store the event ID** to handle duplicate deliveries
- **Use a queue** for heavy processing (e.g., push events to a message queue and process them in a worker)
- **Monitor webhook failures** in the FileRelay dashboard to catch endpoint issues early

## Next Steps

- [API Overview](/docs/api/overview) -- Base URL, authentication, and rate limits
- [Documents API](/docs/api/documents) -- Query documents referenced in webhook events
- [Connections Overview](/docs/connections/overview) -- Understand the pipeline that triggers events
