You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/webhooks/security.md
+152-3Lines changed: 152 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,7 +22,156 @@ OAuth authentication enables secure, token-based access to your webhook endpoint
22
22
When configured with OAuth, epilot will obtain an access token from your authorization server and include it in the `Authorization` header with the `Bearer` scheme. This approach provides enhanced security through token expiration and refresh mechanisms.
23
23
24
24
25
-
### How Webhooks Are Further Secured (Asymmetric Signature)
26
-
To ensure that webhook requests are secure, epilot uses an asymmetric signature mechanism. This involves generating a unique signature for each request that is sent to the external system. The signature is created using a private key that is known only to the epilot platform, and it is verified by the external system using a corresponding [public key](https://webhooks.sls.epilot.io/v1/webhooks/.well-known/public-key). This ensures that only authorized requests are processed, and it prevents unauthorized access to the external system.
25
+
# Webhook Signature Verification
27
26
28
-
To secure the endpoint our webhook is calling, you need to verify the signature of the request. We recommend to use the `verifyEpilotSignature` function by our App SDK as it handles the verification process for you. This function checks the signature against the public key and ensures that the request is valid. We use a standard way to sign & verify the requests according to the [webhook spec](https://github.com/standard-webhooks/standard-webhooks/blob/main/spec/standard-webhooks.md).
27
+
Every webhook request from epilot includes three signature headers:
epilot sends **two** signatures with each webhook request:
38
+
39
+
-**`v1a`** (asymmetric, Ed25519) — Proves the request came from epilot. Verified using epilot's public key, which you can fetch from the `/v1/webhooks/.well-known/public-key` endpoint.
40
+
-**`v1s`** (symmetric, HMAC-SHA256) — Proves the request is intended for your specific webhook. Verified using the `whsec_...` signing secret you received when the webhook was created.
41
+
42
+
Both signatures are computed over the same content:
Every webhook payload includes two system-injected fields that are **always set by epilot after any payload transformations** (including JSONata). These fields cannot be modified or spoofed:
51
+
52
+
-`_org_id` — The epilot organization ID that owns the webhook
53
+
-`_webhook_event_id` — The unique event ID for this webhook invocation
54
+
55
+
## Verification
56
+
57
+
### Option 1: Symmetric Verification (recommended for most use cases)
58
+
59
+
Use the [`standardwebhooks`](https://www.npmjs.com/package/standardwebhooks) npm package to verify the `v1s` signature with your webhook's signing secret.
60
+
61
+
```typescript
62
+
import { Webhook } from"standardwebhooks";
63
+
64
+
const signingSecret ="whsec_..."; // from webhook creation response
65
+
66
+
function verifyWebhook(req:Request):boolean {
67
+
const payload =req.body; // raw request body as string
68
+
const headers =req.headers;
69
+
70
+
// Extract the v1s signature and convert prefix for standardwebhooks compatibility
"publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA...\n-----END PUBLIC KEY-----\n"
170
+
}
171
+
```
172
+
173
+
Cache this key — it only changes if epilot rotates signing keys.
174
+
175
+
## Signing Secret
176
+
177
+
The `whsec_...` signing secret is returned **only once** in the response when you create a webhook. Store it securely. If lost, you'll need to recreate the webhook to get a new one.
0 commit comments