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

# Creating a Webhook

> Step-by-step guide to creating and configuring webhooks in the Revolv3 portal. Learn how to set up your endpoint and verify it works.

## What is Webhook Setup?

Setting up a webhook tells Revolv3 where to send event notifications. Once configured, Revolv3 will automatically send HTTP POST requests to your server when events occur (like payments completing, invoices changing status, etc.).

**Why you need this:**

* **Real-time updates**: Get notified immediately when events happen
* **Essential for ACH**: ACH payments take days—webhooks tell you when they complete
* **Better user experience**: Update your UI instantly
* **Efficient**: No need to constantly poll the API

## Prerequisites

Before creating a webhook, you need:

1. **Public endpoint**: Your webhook URL must be publicly accessible (not localhost)
2. **HTTPS**: Use HTTPS for security (webhooks contain sensitive data)
3. **Endpoint ready**: Your server should be able to receive POST requests
4. **Revolv3 account**: Access to the Revolv3 portal

## Step 1: Prepare Your Webhook Endpoint

Before configuring in Revolv3, make sure your endpoint is ready to receive webhooks.

### Your Endpoint Should:

* Accept HTTP POST requests
* Return 200 OK quickly (process asynchronously if needed)
* Be publicly accessible (not behind a firewall or localhost)
* Use HTTPS (required for security)
* Handle the webhook payload (JSON with Body and Entropy fields)

### Example Endpoint (Node.js/Express)

```javascript theme={null}
app.post('/webhooks/revolv3', (req, res) => {
  // 1. Verify signature (important!)
  const signature = req.headers['x-revolv3-signature'];
  const isValid = verifySignature(req.body, signature, webhookKey, webhookUrl);
  
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }
  
  // 2. Parse the Body field (it's a JSON string)
  const eventData = JSON.parse(req.body.Body);
  
  // 3. Process the event
  handleWebhookEvent(eventData);
  
  // 4. Respond quickly with 200 OK
  res.status(200).send('OK');
});
```

## Step 2: Create Webhook in Revolv3 Portal

1. **Log in to the Revolv3 Portal**:
   * Production: [portal.revolv3.com](https://portal.revolv3.com)
   * Sandbox: [portal-sandbox.revolv3.com](https://portal-sandbox.revolv3.com)
2. **Navigate to Webhooks**:
   * Go to the **Webhooks** page in the portal
3. **Create a new webhook**:
   * Click the button to create a new webhook
4. **Configure the webhook**:
   * **URL**: Enter your webhook endpoint URL (must be HTTPS and publicly accessible)
   * **Webhook Type**: Select **"Object"** (the "Event" type is deprecated)

<img src="https://mintcdn.com/revolv3/aF1uk33Ryv7fqnth/images/docs/06ede134be81c4dd4cc5fd6841a62bedd37cd92e2a4716b9c62c20fa65d9c23d-image.png?fit=max&auto=format&n=aF1uk33Ryv7fqnth&q=85&s=3890361d37cfabde3364512cdc4d12a9" alt="Webhook configuration screen" width="1439" height="831" data-path="images/docs/06ede134be81c4dd4cc5fd6841a62bedd37cd92e2a4716b9c62c20fa65d9c23d-image.png" />

5. **Copy your Webhook Key**:
   * After creating the webhook, you'll see a **Webhook Key**
   * **Copy this immediately**—you'll need it to verify signatures
   * Store it securely (like your API keys)

## Step 3: Test Your Connection

Before saving, test that Revolv3 can reach your endpoint:

1. **Click "Test Connection"** in the portal
2. **Revolv3 sends a test webhook** to your endpoint
3. **Your endpoint should return 200 OK**
4. **If successful**: The "Save" button will be enabled
5. **If failed**: Check your endpoint, make sure it's accessible and returns 200 OK

**What the test does**: Revolv3 sends a `WebhookTest` event to verify your endpoint is reachable and responds correctly.

## Step 4: Save Your Configuration

Once the test connection succeeds:

1. **Click "Save"** to save your webhook configuration
2. **Your webhook is now active** and will receive events
3. **Monitor your endpoint** to ensure webhooks are being received

## Understanding Webhook Security: Signature Verification

Revolv3 includes a signature in each webhook request so you can verify it came from Revolv3.

### The x-revolv3-signature Header

Each webhook request includes an `x-revolv3-signature` header containing a hashed signature of the payload.

**Why it matters**: This signature proves the webhook came from Revolv3 and wasn't tampered with.

### How to Verify Signatures

Revolv3 uses **HMAC-SHA256** to generate the signature:

1. **Concatenate**: `{your-webhook-url}${request-body}`
   * Note the `$` delimiter between URL and body
2. **Create HMAC**: Use HMAC-SHA256 with your webhook key
3. **Encode**: Base64 encode the result
4. **Compare**: This should match the `x-revolv3-signature` header

### Verification Code Examples

#### C# Example

```csharp theme={null}
public static bool IsValidWebhookEventSignature(
    string requestBody,
    string signatureHeader,
    string signatureKey,
    string url)
{
    if (string.IsNullOrEmpty(signatureKey))
        throw new ArgumentNullException(nameof(signatureKey));
    if (string.IsNullOrEmpty(url))
        throw new ArgumentNullException(nameof(url));
    
    // Concatenate url + "$" + requestBody
    byte[] bytes = Encoding.UTF8.GetBytes(url + "$" + requestBody);
    
    // Create HMAC-SHA256 hash
    using var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(signatureKey));
    
    // Compare signatures
    return Convert.ToBase64String(hmacsha256.ComputeHash(bytes)).Equals(signatureHeader);
}
```

#### PHP Example

```php theme={null}
function IsValidWebhookEventSignature(
    string $requestBody,
    string $signatureHeader,
    string $signatureKey,
    string $url
): bool {
    if (empty($signatureKey)) {
        throw new ArgumentNullException('signatureKey');
    }
    if (empty($url)) {
        throw new ArgumentNullException('url');
    }

    // Concatenate url + "$" + requestBody
    $data = $url . '$' . $requestBody;

    // Create HMAC-SHA256 hash
    $hmac = hash_hmac('sha256', $data, $signatureKey, true);

    // Compare signatures
    return base64_encode($hmac) === $signatureHeader;
}
```

#### JavaScript/Node.js Example

```javascript theme={null}
const crypto = require('crypto');

function isValidWebhookSignature(requestBody, signatureHeader, signatureKey, url) {
  if (!signatureKey || !url) {
    throw new Error('signatureKey and url are required');
  }
  
  // Concatenate url + "$" + requestBody
  const data = url + '$' + requestBody;
  
  // Create HMAC-SHA256 hash
  const hmac = crypto.createHmac('sha256', signatureKey);
  hmac.update(data);
  const hash = hmac.digest('base64');
  
  // Compare signatures (use constant-time comparison for security)
  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(signatureHeader)
  );
}
```

## Important Notes

### Webhook Type: Use "Object"

* **Use "Object" type**: This is the current format
* **Don't use "Event" type**: It's deprecated and will be removed

### URL Requirements

* **Must be HTTPS**: Webhooks require secure connections
* **Must be publicly accessible**: Can't use localhost or private IPs
* **Must return 200 OK**: Your endpoint should respond quickly with 200 status

### Webhook Key Security

* **Store securely**: Treat webhook keys like API keys
* **Never expose**: Don't commit to version control
* **Use environment variables**: Store in secrets manager

## Testing Your Webhook

### Test Connection Button

The "Test Connection" button in the portal:

1. Sends a `WebhookTest` event to your endpoint
2. Waits for a 200 OK response
3. Enables the Save button if successful

### Manual Testing

You can also test manually by:

1. Making a test payment in sandbox
2. Checking if your endpoint receives the webhook
3. Verifying the signature is correct
4. Ensuring you process the event correctly

## Troubleshooting

### Test Connection Fails

**Possible issues**:

* Endpoint not publicly accessible (localhost won't work)
* Endpoint not returning 200 OK
* Firewall blocking Revolv3's requests
* Incorrect URL format

**Solutions**:

* Use a service like ngrok for local testing
* Ensure your endpoint responds with 200 OK
* Check server logs for incoming requests
* Verify the URL is correct and uses HTTPS

### Webhooks Not Received After Setup

**Check**:

* Is your endpoint publicly accessible?
* Is it returning 200 OK?
* Are you checking the correct URL?
* Check Revolv3 portal for webhook delivery status

### Signature Verification Fails

**Check**:

* Are you using the correct webhook key?
* Is the URL exactly as configured (including https\://)?
* Are you concatenating URL + "\$" + body correctly?
* Are you using HMAC-SHA256 and Base64 encoding?

## Best Practices

1. **Always verify signatures**: Never process webhooks without signature verification
2. **Respond quickly**: Return 200 OK immediately, process asynchronously
3. **Handle duplicates**: Make your handler idempotent
4. **Log everything**: Log all webhooks for debugging and auditing
5. **Test thoroughly**: Test in sandbox before production
6. **Monitor delivery**: Check webhook delivery status in the portal

## Next Steps

* [**Webhook Guide**](/docs/webhook-events/webhook) — Learn about webhook events and how to handle them
* [**Invoice Status**](/docs/core-concepts/invoice/invoice-status) — Understand statuses you'll receive in webhooks
* [**Subscriptions**](/docs/core-concepts/subscription/create-subscription) — Learn about subscription webhook events

***
