Webhooks
Webhooks let your integrations take action in response to events that occur in Eppo.
Experiment Webhook Events
This webhook is a good solution when a custom application needs to display or take action on the most up to date state of an experiment.
The webhook sends four types of experiment update events. If you only want to take action based on certain types of events, you can filter to just the events you are interested in subscribing to based on their event type in the request body.
Experiment Metric Updated
Triggers when:
- Metric is added or removed
- Metric collection is added or removed
Request body:
{
"event": "experiment.metric.updated",
"data": {
"experiment_id": <experiment_id>,
"experiment_key": "<experiment_key>"
},
"signature": "<signature>"
}
Experiment Configuration Updated
Triggers when:
- User edits any configuration
Request body:
{
"event": "experiment.configuration.updated",
"data": {
"experiment_id": <experiment_id>,
"experiment_key": "<experiment_key>"
},
"signature": "<signature>"
}
Experiment Status Updated
Triggers when:
- Experiment's status changes
Request body:
{
"event": "experiment.status.updated",
"data": {
"experiment_id": <experiment_id>,
"experiment_key": "<experiment_key>"
},
"signature": "<signature>"
}
Experiment Calculated Metrics Updated
Triggers when:
- Data pipeline run irrespective of success or failure and mode of trigger manual or scheduled
Request body:
{
"event": "experiment.calculated_metrics.updated",
"data": {
"experiment_id": <experiment_id>,
"experiment_key": "<experiment_key>",
"meta_data": {
"status": "<success | failure>",
"is_manual_refresh": <boolean>,
"is_data_updated": <boolean>,
"time_taken_seconds": <number>,
"is_traffic_imbalance": <boolean>,
"assignments_scan_start_date": "<date>",
"assignments_scan_end_date": "<date>"
}
},
"signature": "<signature>"
}
Flagging Webhooks
The flagging webhook is a good solution when a custom application needs to display or take action on the most up to date state of a flag.
Flag Configuration Updated
This webhook is sent when a flag or its associated models, such as assignments, targeting rules, environments or audiences, are updated.
In short, any operation visible in the Flag History is sent to the webhook.
Request body:
{
"event": "flag.configuration.updated",
"data": {
"flag": {
"id": <flag_id>,
"key": "<flag_key>"
}
},
"signature": "<signature>"
}
Configuring the webhook
To use the webhook, you first need to setup a URL on your side to receive the webhook request.
The configuration is done in the Admin > Integrations page.
Once that is done, contact Eppo support to customize the webhook to your needs.
Webhook Signature
The webhook request is signed with a signature. The signature is a hash of the "data"
object in the webhook request body and the webhook secret.
Integrating and Verifying the Signature
To verify the webhook signature, you'll need to:
- Get the signature from the webhook request body
- Generate a HMAC SHA-256 hash with your webhook secret and the
"data"
in the webhook request body - Compare the generated hash with the received signature
Here's an example in TypeScript/Node.js:
import crypto from 'crypto';
function generateHMACSignature(secret: string, data: string): string {
return crypto.createHmac('sha256', secret)
.update(data)
.digest('hex');
}
function isValidWebhookSignature(
data: string,
signature: string,
webhookSecret: string
): boolean {
const expectedSignature = generateHMACSignature(webhookSecret, data);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Example usage in an Express route handler
app.post('/webhook', (req, res) => {
const signature = req.body.signature;
const data = JSON.stringify(req.body.data);
const webhookSecret = process.env.WEBHOOK_SECRET;
if (!isValidWebhookSignature(data, signature, webhookSecret)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
// ...
});
- Always use a constant-time comparison function (like
crypto.timingSafeEqual
) when comparing signatures to prevent timing attacks. - Ensure JSON data types are preserved before stringifying to generate the signature. For example, a numeric ID should remain a number (e.g., id: 123) and not be coerced into a string (e.g., id: "123"), as this will result in an incorrect signature.