Webhooks

read time 3 mins

Webhooks enable secret changes in Doppler to be integrated into your continuous delivery flow. For example, a webhook could be used by Doppler restart or redeploy an application when a secret is changed.

Each webhook receives a single POST request from Doppler whenever a change is made to secrets for that project.

πŸ“˜

Multiple webhooks can be defined for a single project

Creating a Webhook

  1. Navigate to a project config, then click Webhooks from the project menu
  2. Click the + Add button which will open a dialog asking you to provide the webhook URL and an optional Secret value to enable the signing of each webook request.
  3. After adding the webhook, you will see it listed in the Active Webhooks list.
1280

πŸ“˜

Webhook Request Signing

If a Secret value is provided, Doppler uses this to cryptographically sign each webhook request, enabling you to verify the request originated from Doppler. See the Request Signing section to learn more.

Accepting Webhooks

Your endpoint will receive a JSON payload with the following structure.

FieldDescription
typeEvent types available:
config.secrets.update
configConfig object
projectProject object
workplaceWorkplace object

Here is an example payload:

{
    "type": "config.secrets.update",
    "config": {
        "name": "dev_2",
        "created_at": "2019-05-30T08:18:33.634Z",
        "environment": "dev",
        "project": "714faaa5958"
    },
    "project": {
        "id": "714faaa5958",
        "name": "Backend",
        "description": "Your first project!",
        "created_at": "2019-05-26T01:42:30.256Z"
    },
    "workplace": {
        "id": "fcd76",
        "name": "Pied Piper",
        "billing_email": "[email protected]"
    }
}

Verify Webhook With Request Signing

Doppler can optionally sign the webhook events by including a signature in each event’s X-Doppler-Signature header. This allows you to verify that the events were sent by Doppler, not by a third party.

To enable webhook verification, a value for Secret must be supplied when creating the webhook. The X-Doppler-Signature header will then contain a SHA256 hash of the request body prepended with sha256= (e.g. X-Doppler-Signature: sha256=724cd2c1110335a8ea6207f74cd74fb198a8e59ca1b7b39d67678e0f97df832e).

To verify the signature, create your own SHA256 hash of the request body using the Secret value. Then compare the header value against your own computed value using a timing-safe equality function. Here is an example of how you might accomplish this using Node.js.

const bodyParser = require("body-parser");
const crypto = require("crypto");
const express = require("express");

const app = express();
app.use(bodyParser.json({
  limit: '200kb',
}));

app.post('/webhooks/doppler', (req, res) => {
  const requestHMAC = req.header("X-Doppler-Signature");
  const secret = process.env.WEBHOOK_SECRET;
  const computedHMAC = `sha256=${crypto.createHmac('sha256', secret).update(JSON.stringify(req.body)).digest('hex')}`;
  const signatureMatches = crypto.timingSafeEqual(Buffer.from(requestHMAC), Buffer.from(computedHMAC));
  
  if (signatureMatches) {
    // do stuff
  }

  res.sendStatus(signatureMatches ? 200 : 401);
});
require "sinatra"
require "json"
require "openssl"

set :port, ENV["PORT"] || 8081

post "/webhooks/doppler" do
  if valid_webhook_signature?(request)
    # do something
    status 200
  else
    status 401
  end
end

def valid_webhook_signature?(request)
  request.body.rewind
  
  data = request.body.read
  key = ENV["WEBHOOK_SECRET"]
  digest = OpenSSL::Digest.new("sha256")

  computedHMAC = OpenSSL::HMAC.hexdigest(digest, key, data)
  requestHMAC = request.env["HTTP_X_DOPPLER_SIGNATURE"].delete_prefix("sha256=")

  return OpenSSL.secure_compare(requestHMAC, computedHMAC)
end

Removing a Webhook

A webhook can be removed by clicking the Remove button next to the webhook you wish to have deleted.

Once confirmed, it will be removed by from the list of active webhooks.