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

# Managing Webhooks

> Learn how to configure, manage, and troubleshoot webhook notifications for real-time document status updates

## Overview

Webhooks provide real-time notifications when document processing status changes occur. When configured, your application will receive HTTP POST requests to specified endpoints whenever documents transition through different processing states.

Webhooks are essential for building responsive applications that need immediate awareness of document processing results, failures, or other status changes.

## Webhook Delivery

### Delivery Mechanism

Webhooks are delivered via HTTP POST requests to your configured endpoint:

* **Method**: POST
* **Content-Type**: application/json
* **Authentication**: Include `X-Webhook-Token` header with your webhook token
* **Payload**: JSON:API formatted document status change event

### Retry Policy

Our webhook system implements intelligent retry logic:

* **Initial Delivery**: Immediate attempt after status change
* **Max Retries**: 5 attempts before marking as failed
* **Dead Letter Queue**: Failed deliveries are queued for manual retry

### Security

<Steps>
  <Step title="Secure your endpoint">
    Always use HTTPS endpoints in production. HTTP is only allowed in sandbox environments.
  </Step>

  <Step title="Validate webhook tokens">
    Verify the `X-Webhook-Token` header matches your stored webhook token. Store tokens securely - they cannot be retrieved after creation.
  </Step>

  <Step title="Implement idempotency">
    Use the document ID and status from the webhook payload to handle duplicate deliveries gracefully.
  </Step>
</Steps>

## Verifying Webhook Signatures

You can use the `X-Webhook-Signature` header to verify that the webhook payload was sent by Beltic. The signature is generated using an HMAC-SHA256 of the request body signed with your webhook secret.

Here is a complete example of how to verify the signature in a Node.js application:

```typescript theme={null}
import crypto from 'crypto';

/**
 * Example function to verify a webhook signature
 *
 * @param payload - The raw request body as a string
 * @param signature - The X-Webhook-Signature header value
 * @param secret - The secret key provided when creating the webhook
 * @returns boolean - True if signature is valid
 */
export function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  if (!payload || !signature || !secret) {
    return false;
  }

  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  // Use timingSafeEqual to prevent timing attacks
  const signatureBuffer = Buffer.from(signature);
  const expectedBuffer = Buffer.from(expectedSignature);

  if (signatureBuffer.length !== expectedBuffer.length) {
    return false;
  }

  return crypto.timingSafeEqual(signatureBuffer, expectedBuffer);
}

// Example usage with Express
import express from 'express';

const app = express();

// Use raw body parser to ensure exact payload matching
app.use(express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-webhook-signature'] as string;
  const secret = process.env.WEBHOOK_SECRET;

  if (!verifyWebhookSignature(req.body.toString(), signature, secret)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body.toString());
  // Process event...
});
```

## Creating a Webhook Configuration

<Steps>
  <Step title="Prepare your endpoint">
    Create an HTTPS endpoint capable of receiving POST requests with JSON payloads. The endpoint should:

    * Accept `application/json` content type
    * Validate the `X-Webhook-Token` header
    * Return 2xx status codes for successful processing
    * Handle duplicate deliveries gracefully

    ```javascript theme={null}
    // Example webhook receiver (Node.js/Express)
    app.post('/webhooks/documents', (req, res) => {
      const token = req.headers['x-webhook-token'];

      // Validate token
      if (token !== process.env.WEBHOOK_TOKEN) {
        return res.status(401).json({ error: 'Invalid token' });
      }

      // Process webhook payload
      const { data } = req.body;
      console.log(`Document ${data.id} status changed to ${data.attributes.status}`);

      // Return success
      res.status(200).json({ received: true });
    });
    ```
  </Step>

  <Step title="Choose subscribed statuses">
    Select which document status changes should trigger webhooks:

    * `processed`: Document processing completed successfully
    * `failed`: Document processing failed
  </Step>

  <Step title="Create the webhook configuration">
    Use the [Create Webhook Config](/api-reference/endpoint/webhook-config-create) endpoint:

    ```bash theme={null}
    curl -X POST https://api.beltic.com/v1/webhooks/configs \
      -H "X-Api-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "data": {
          "type": "webhook_config",
          "attributes": {
            "url": "https://your-app.com/webhooks/documents",
            "subscribed_statuses": ["processed", "failed"]
          }
        }
      }'
    ```

    **Important**: Store the returned token securely - it cannot be retrieved again.
  </Step>
</Steps>

## Listing Webhook Configurations

Retrieve all webhook configurations for your organization:

```bash theme={null}
curl -X GET "https://api.beltic.com/v1/webhooks/configs" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### Filtering Options

* **Active configs only**: Include `?filter[is_active]=true`
* **Pagination**: Use `page[size]` (default: 15, max: 100)

## Managing Webhook Configurations

### Retrieving a Specific Configuration

Get detailed information about a webhook config:

```bash theme={null}
curl -X GET https://api.beltic.com/v1/webhooks/configs/{config_id} \
  -H "X-Api-Key: YOUR_API_KEY"
```

### Updating a Configuration

Modify webhook settings using the update endpoint:

```bash theme={null}
curl -X PATCH https://api.beltic.com/v1/webhooks/configs/{config_id} \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "webhook_config",
      "id": "{config_id}",
      "attributes": {
        "url": "https://your-app.com/webhooks/documents/v2",
        "subscribed_statuses": ["processed", "failed"],
        "is_active": true
      }
    }
  }'
```

### Deactivating a Configuration

Temporarily disable a webhook without deleting it:

```bash theme={null}
curl -X PATCH https://api.beltic.com/v1/webhooks/configs/{config_id} \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "webhook_config",
      "id": "{config_id}",
      "attributes": {
        "is_active": false
      }
    }
  }'
```

## Webhook Payload Format

All webhook deliveries use JSON:API format:

```json theme={null}
{
  "data": {
    "type": "document",
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "attributes": {
      "status": "processed",
      "created_at": "2026-01-16T10:00:00.000Z",
      "updated_at": "2026-01-16T10:30:00.000Z"
    },
    "relationships": {
      "document_template": {
        "data": {
          "type": "document_template",
          "id": "456e7890-e89b-12d3-a456-426614174000"
        }
      }
    }
  },
  "meta": {
    "event_type": "document.status_changed",
    "previous_status": "processing",
    "timestamp": "2026-01-16T10:30:00.000Z"
  }
}
```

## Handling Webhook Failures

### Manual Resend

If a webhook was missed or needs to be re-delivered, use the resend endpoint:

```bash theme={null}
curl -X POST https://api.beltic.com/v1/webhooks/resend \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "webhook_resend",
      "attributes": {
        "document_id": "123e4567-e89b-12d3-a456-426614174000"
      }
    }
  }'
```

This creates a new delivery attempt for the specified document/status combination.

## Testing Webhooks

### Development Testing

Use tools like ngrok or localtunnel to expose local endpoints:

```bash theme={null}
# Using ngrok to expose local port 3000
ngrok http 3000

# Configure webhook with ngrok URL
curl -X POST https://api.beltic.com/v1/webhooks/configs \
  -H "X-Api-Key: YOUR_SANDBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "webhook_config",
      "attributes": {
        "url": "https://abc123.ngrok.io/webhooks/documents",
        "subscribed_statuses": ["processed", "failed"]
      }
    }
  }'
```

### Manual Testing

Test webhook delivery by manually resending:

```bash theme={null}
# Resend webhook for a specific document
curl -X POST https://api.beltic.com/v1/webhooks/resend \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "type": "webhook_resend",
      "attributes": {
        "document_id": "your-document-id",
        "webhook_config_id": "your-webhook-config-id"
      }
    }
  }'
```

## Best Practices

<Accordion title="Endpoint Design">
  * Use HTTPS endpoints with valid SSL certificates
  * Implement proper logging for webhook deliveries
  * Return appropriate HTTP status codes (200 for success, 4xx/5xx for errors)
  * Process webhooks asynchronously to avoid timeouts
</Accordion>

<Accordion title="Security">
  * Always validate the `X-Webhook-Token` header
  * Store webhook tokens securely (environment variables, secret management)
  * Implement rate limiting to protect against abuse
  * Use idempotency keys to handle duplicate deliveries
</Accordion>

<Accordion title="Error Handling">
  * Log all webhook deliveries with timestamps and payloads
  * Implement alerting for high failure rates
  * Have fallback mechanisms for critical webhook-dependent features
  * Monitor webhook endpoint health and performance
</Accordion>

<Accordion title="Configuration Management">
  * Use different webhook URLs for different environments
  * Document webhook configurations and their purposes
  * Regularly review and clean up unused webhook configurations
  * Test webhook configurations after deployments
</Accordion>

## Troubleshooting

### Common Issues

**Webhook Not Received**

* Check that the webhook configuration is active
* Verify the endpoint URL is accessible and returns 2xx responses
* Ensure the document status is in your subscribed statuses list

**Authentication Failures**

* Verify the `X-Webhook-Token` header is included
* Confirm the token matches what was returned during configuration creation
* Check for token encoding issues

**Duplicate Deliveries**

* Implement idempotency using document ID and status
* Log all received webhooks to detect duplicates
* Handle duplicate deliveries gracefully

**Timeout Issues**

* Process webhooks asynchronously in background jobs
* Return immediate 202 Accepted responses
* Implement proper error handling and retries in your application
