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

# Verifying Webhook Authenticity

## Verifying Webhook Authenticity

Webhook requests sent from Lendflow include HTTP headers that can be used to verify the request actually came from Lendflow and that the request body was not modified while in transit.

* `Lendflow-Verification-Signature` — the signature to validate against
* `Lendflow-Verification-Timestamp` — a timestamp value

Webhook verification is optional, but recommended.

## Verifying a Signature

Signature verification is done within your application's code by attempting to recreate and match the signature sent in the `Lendflow-Verification-Signature` header. The process in pseudocode is as follows:

```bash Bash theme={"system"}
base64encode(hmac('sha256', timestamp + body, webhook_secret))
```

If signature verification fails, the webhook request should be rejected.

<Note>
  **The Webhook Secret**

  Verification signatures are signed by your organization's webhook secret; a token that should be viewed and treated like a password.
  **Your organization's webhook secret must be requested from Lendflow's customer success team.**
</Note>

### Code Examples

#### PHP

```php webhook.php theme={"system"}
<?php

$signature = $_SERVER['HTTP_Lendflow_Verification_Signature'];
$timestamp = $_SERVER['HTTP_Lendflow_Verification_Timestamp'];
$body = file_get_contents('php://input');
$secret = 'your-webhook-secret-here';

$verification = base64_encode(
    hash_hmac('sha256', $timestamp . $body, $secret, binary: true),
);
    
    
if ($signature !== $verification) {
    http_response_code(400);
    echo "Invalid Webhook";
    exit;
}

echo "Valid Webhook";
```

#### Javascript

```
const express = require('express');
const crypto = require('crypto');
const app = express();

const PORT = 3000;

let body = '';

app.use((req, res, next) => {
  req.setEncoding('utf8');
  req.on('data', (chunk) => body += chunk);
  req.on('end', () => next());
});

app.post('/webhook', (req, res) => {
  const signature = req.headers['lendflow-verification-signature'];
  const timestamp = req.headers['lendflow-verification-timestamp'];
  const secret = 'your-webhook-secret-here';

  const verification = crypto
    .createHmac('sha256', secret)
    .update(timestamp + body)
    .digest('base64');

  console.log('signature:', signature);
  console.log('timestamp:', timestamp);
  console.log('body:', body);

  if (signature !== verification) {
    return res.status(400).send('Invalid Webhook');
  }

  return res.status(200).send('Valid Webhook');
});

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
```

<Warning>
  **Request bodies.** If even a single character in the request body changes between departure from Lendflow and receipt on your end, the verification will fail. This notably includes how escape characters are handled by your server.
  The above examples work without needing to adjust the request bodies, but we've seen integrations which require escape characters to be re-escaped. In Javascript this means working with this adjusted variable:

  ```
  const properlyEscapedBody = body.replace(/\//g, '\\/')
  ```
</Warning>

## FAQ

<AccordionGroup>
  <Accordion title="Is webhook verification required?">
    No. Webhook verification is optional, but recommended. It confirms the request came from Lendflow and that the body was not modified in transit.
  </Accordion>

  <Accordion title="Where do I get the webhook secret?">
    Verification signatures are signed by your organization's webhook secret. Treat that token like a password. Request it from Lendflow's customer success team.
  </Accordion>

  <Accordion title="Why does signature verification fail?">
    Recreate the signature as `base64encode(hmac('sha256', timestamp + body, webhook_secret))` and match it to `Lendflow-Verification-Signature`. If even a single character in the body changes before you hash it, including how escape characters are handled, verification fails. Reject the request when the signatures do not match.
  </Accordion>
</AccordionGroup>

## Next steps

<Card title="Custom Workflows Webhooks" icon="webhook" href="/lendflow-external/docs/custom-workflows-webhooks" horizontal>
  Choose which workflow events send webhooks and where they are delivered.
</Card>

<Card title="Submit A New Application Via The API" icon="paper-plane" href="/api-docs/docs/submit-a-new-application-via-the-api" horizontal>
  Authenticate, set workflow\_template\_id, and submit a deal payload.
</Card>
