Documentation Index
Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt
Use this file to discover all available pages before exploring further.
Webhook Security
Every webhook delivery from Orbit includes an HMAC-SHA256 signature in the X-Devotel-Signature header. Always verify this signature to ensure the request genuinely came from Orbit and hasn’t been tampered with.
How Signature Verification Works
- Orbit computes an HMAC-SHA256 hash of the raw request body using your webhook signing secret
- The hash is sent in the
X-Devotel-Signature header as a hex string
- Your server recomputes the hash and compares it to the header value
Verification Examples
Node.js
import crypto from 'node:crypto';
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf-8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}
// In your webhook handler:
app.post('/webhooks/orbit', (req, res) => {
const signature = req.headers['x-devotel-signature'];
const isValid = verifyWebhookSignature(req.rawBody, signature, 'whsec_your_secret');
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.rawBody);
// Process the event...
res.status(200).json({ received: true });
});
Python
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# In your webhook handler (Flask example):
@app.route('/webhooks/orbit', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Devotel-Signature')
is_valid = verify_webhook_signature(request.data, signature, 'whsec_your_secret')
if not is_valid:
return jsonify({'error': 'Invalid signature'}), 401
event = request.get_json()
# Process the event...
return jsonify({'received': True}), 200
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func verifyWebhookSignature(payload []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expected))
}
Security Best Practices
- Always verify signatures — never process webhooks without checking the signature
- Use timing-safe comparison — prevents timing attacks (
crypto.timingSafeEqual in Node.js, hmac.compare_digest in Python)
- Use HTTPS — your webhook endpoint must use HTTPS to protect payloads in transit
- Rotate secrets — rotate your webhook signing secret periodically in Settings > Webhooks
- Idempotency — use the event
id field to deduplicate, since Orbit guarantees at-least-once delivery
- Respond quickly — return a
2xx within 30 seconds; process heavy work asynchronously
- IP allowlisting — optionally restrict incoming webhooks to Orbit’s IP ranges (available in the dashboard under Settings > Security)
Signing Secret
Your webhook signing secret is generated when you create a webhook. Retrieve it from the dashboard under Webhooks > [Your Webhook] > Signing Secret, or via the API:
curl https://orbit-api.devotel.io/api/v1/webhooks/wh_abc123 \
-H "X-API-Key: dv_live_sk_..."
The secret is prefixed with whsec_ and should be stored securely — never expose it in client-side code or logs.