Webhook Security
All webhook deliveries are signed using HMAC-SHA256 so you can verify that requests are genuinely from Replicer.
Signing mechanism
Each webhook endpoint has a unique signing secret (shown once at creation). Replicer signs every payload with this secret and includes the signature in the X-Replicer-Signature header.
The signature is computed as:
HMAC-SHA256(signing_secret, request_body)Verifying signatures
Node.js
javascript
import crypto from 'crypto'
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}
// Express middleware
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-replicer-signature']
const secret = process.env.WEBHOOK_SECRET
if (!verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' })
}
const event = JSON.parse(req.body)
// Process the event...
res.status(200).json({ received: true })
})Python
python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Replicer-Signature')
secret = os.environ['WEBHOOK_SECRET']
if not verify_webhook(request.data, signature, secret):
return jsonify({'error': 'Invalid signature'}), 401
event = request.get_json()
# Process the event...
return jsonify({'received': True}), 200Go
go
func verifySignature(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))
}Additional headers
Use these headers for extra verification:
| Header | Description |
|---|---|
X-Replicer-Timestamp | Unix timestamp of when the event was sent |
X-Replicer-Delivery-Id | Unique ID for this delivery attempt |
X-Replicer-Event | Event type (e.g., call.ended) |
Best practices
Security recommendations
- Always verify signatures before processing webhook events
- Use
timingSafeEqual(or equivalent) to prevent timing attacks - Store secrets securely in environment variables or a secret manager
- Respond quickly — process asynchronously and return
200immediately - Be idempotent — you may receive the same event multiple times due to retries
- Check the timestamp — reject events older than 5 minutes to prevent replay attacks
Timestamp verification
To prevent replay attacks, verify the X-Replicer-Timestamp header:
javascript
const timestamp = parseInt(req.headers['x-replicer-timestamp'])
const now = Math.floor(Date.now() / 1000)
const fiveMinutes = 5 * 60
if (Math.abs(now - timestamp) > fiveMinutes) {
return res.status(401).json({ error: 'Timestamp too old' })
}
