> ## Documentation Index
> 
> Fetch the complete documentation index at: [/docs/llms.txt](/docs/llms.txt)
> 
> Use this file to discover all available pages before exploring further.

[Skip to main content](#content-area)

## 

[​

](#setup)

Setup

1.  Go to **Integrations** in your osforms dashboard
2.  Click **Connect** next to Webhook
3.  Enter your endpoint URL (must be HTTPS)
4.  Copy the **signing secret** and store it securely — it’s shown once
5.  Click **Save**

## 

[​

](#request-format)

Request format

osforms sends a `POST` request with `Content-Type: application/json`:

```
{
  "submissionId": "64f1a2b3c4d5e6f7a8b9c0d1",
  "formId": "64f1a2b3c4d5e6f7a8b9c0d2",
  "formName": "Contact Form",
  "submittedAt": "2026-03-27T12:00:00.000Z",
  "data": {
    "name": "Jane Smith",
    "email": "jane@example.com",
    "message": "Hello!"
  }
}
```

`data` keys match the **field IDs** set in your form schema.

## 

[​

](#verifying-signatures)

Verifying signatures

Every request includes an `X-osforms-Signature-256` header:

```
X-osforms-Signature-256: sha256=<hex>
```

The signature is computed as `HMAC-SHA256(requestBody, signingSecret)` where `requestBody` is the raw JSON string. Verify it in your endpoint:

```
import crypto from 'crypto';

function verifySignature(
  rawBody: string,
  signature: string,
  secret: string
): boolean {
  const expected =
    'sha256=' +
    crypto.createHmac('sha256', secret).update(rawBody).digest('hex');

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

// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-osforms-signature-256'] as string;
  if (!verifySignature(req.body.toString(), sig, process.env.WEBHOOK_SECRET!)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  // Process the submission...
  res.json({ ok: true });
});
```

Always verify the signature before processing a webhook. Use `timingSafeEqual` to prevent timing attacks.

## 

[​

](#retry-behavior)

Retry behavior

Webhooks do not automatically retry on failure. If your endpoint returns a non-2xx status or times out:

-   The delivery is marked as failed in the integration log
-   You receive an in-app notification and email alert
-   You can view the error in **Submissions → View Logs**

## 

[​

](#timeout)

Timeout

Webhook requests time out after **30 seconds**. Ensure your endpoint responds within this window.

## 

[​

](#testing)

Testing

Click **Send Test** in the integration settings to send a sample payload to your endpoint and verify your signature verification logic.

[Google Sheets](/docs/integrations/google-sheets)

⌘I