---
openapi: post /open/forms/{form}/integrations
---

# Create Webhook Integration

Add a new webhook integration to send form submissions to an external endpoint.

## Authentication & Scope

This endpoint requires a **Personal Access Token** with the `manage-integrations` ability.

## Request

<ParamField path="form" type="number" required>
    The ID of the form to which the webhook will be added.
</ParamField>

<ParamField body="integration_id" type="string" required>
    Must be set to `"webhook"` for webhook integrations.
</ParamField>

<ParamField body="status" type="string" required>
    The initial status of the webhook. Allowed values: `"active"`, `"inactive"`.
</ParamField>

<ParamField body="data" type="object" required>
Configuration object containing webhook details.

<Expandable title="data properties">
<ParamField body="webhook_url" type="string" required>
The URL where form submissions will be sent. Must be a valid HTTPS URL that resolves only to public IP addresses. Private, loopback, link-local, and cloud metadata addresses are rejected.
</ParamField>

<ParamField body="webhook_secret" type="string">
    Optional signing secret for HMAC-SHA256 validation. When provided, webhook
    requests will include an `X-Webhook-Signature` header. Must be at least 12
    characters. Recommended for security. Should be a random, cryptographically
    secure string.
</ParamField>

<ParamField body="webhook_headers" type="object">
Optional custom HTTP headers to send with each webhook request. Provided as key-value pairs where both keys and values are strings. Maximum 10 headers allowed, each value max 255 characters.

**Blocked headers** (cannot be customized): `Authorization`, `X-Webhook-Signature`, `Content-Type`, `Host`, `Cookie`, `X-CSRF-Token`, `Content-Length`, and others reserved for security.

Example:

```json
{
    "X-API-Key": "your-api-key",
    "X-Custom-ID": "custom-value"
}
```

</ParamField>
</Expandable>
</ParamField>

<ParamField body="logic" type="object">
    Optional conditional logic to trigger the webhook only when specific
    conditions are met.
</ParamField>

<RequestExample>
```bash cURL
curl -X POST 'https://api.opnform.com/open/forms/123/integrations' \
  -H 'Authorization: Bearer YOUR_PAT' \
  -H 'Content-Type: application/json' \
  -d '{
    "integration_id": "webhook",
    "status": "active",
    "data": {
      "webhook_url": "https://example.com/opnform-hook",
      "webhook_secret": "whsec_1234567890abcdefghijklmnop",
      "webhook_headers": {
        "X-API-Key": "my-api-key",
        "X-Custom-Header": "custom-value"
      }
    }
  }'
```
</RequestExample>

## Response

`200 OK` – Webhook created successfully.

<ResponseExample>
```json Success
{
  "message": "Form Integration was created.",
  "form_integration": {
    "id": 42,
    "form_id": 123,
    "integration_id": "webhook",
    "status": "active",
    "data": {
      "webhook_url": "https://example.com/opnform-hook",
      "webhook_secret": "whsec_1234567890abcdefghijklmnop",
      "webhook_headers": {
        "X-API-Key": "my-api-key",
        "X-Custom-Header": "custom-value"
      }
    }
  }
}
```
</ResponseExample>

`403 Forbidden` – The token does not have `manage-integrations` ability or insufficient form permissions.

`404 Not Found` – Form not found.

`422 Unprocessable Entity` – Validation error (e.g., invalid or non-public webhook URL, webhook_secret too short, blocked header).

<ResponseExample>
```json Error
{
  "message": "The given data was invalid.",
  "errors": {
    "data.webhook_url": ["The webhook URL must use HTTPS."],
    "data.webhook_secret": ["The webhook secret must be at least 12 characters."],
    "data.webhook_headers": ["The 'Authorization' header cannot be customized for security reasons."]
  }
}
```
</ResponseExample>

## Security

If you provide a `webhook_secret` when creating the webhook, OpnForm will sign each webhook request with an HMAC-SHA256 signature. This allows you to verify that the webhook came from OpnForm and hasn't been tampered with.

Webhook URLs are validated when they are saved and again before each delivery. OpnForm does not follow webhook redirects, and private network destinations are blocked unless the instance operator explicitly enables private webhook URLs for a self-hosted deployment.

Each webhook request will include:

-   **`X-Webhook-Signature` header**: Contains the signature in format `sha256=HEXADECIMAL_VALUE`
-   **Custom headers**: Any headers you specified in `webhook_headers` (except blocked headers)
-   **JSON body metadata**: The payload includes `form_id` and `submission_id` so you can correlate webhook deliveries with OpnForm API submission management endpoints

### Blocked Headers

For security reasons, the following headers cannot be customized:

-   `Authorization`
-   `X-Webhook-Signature`
-   `Content-Type`
-   `Content-Length`
-   `Host`
-   `Cookie`
-   `X-CSRF-Token`
-   `X-Forwarded-For`
-   `X-Forwarded-Proto`
-   `X-Real-IP`

See [Validating Webhook Signatures](/api-reference/integrations/webhook-security) for implementation examples.

<Warning>
    Do not commit webhook secrets to version control. Use environment variables
    or secure vaults to manage them.
</Warning>
