Webhooks are an important part of your SSL management. They allow BrandSSL to notify you about events that happen on your account, such as a domain name being added or disabled.
A webhook URL is an endpoint on your server where you can receive notifications about such events. When an event occurs, we'll make a POST request to that endpoint, with a JSON body containing the details about the event, including the type of event and the data associated with it.
Structure of a webhook payload
All webhook payloads follow the same basic structure:
- an event field describing the type of event
- a data object. The contents of this object will vary depending on the event, but typically it will contain details of the event, including:
- an id containing the ID of the resource
- a status, describing the status of the request
- domain or endpoint details, if applicable
Here are some sample webhook payloads for domain management:
{
"event": "domain.created",
"data": {
"id": "907589706",
"url": "www.turningpoint.com",
"endpoint": {},
"timestamp": "2022-06-16 14:58:47.326591+00:00",
"admin_graphql_api_id": "gid://brandssl/Domain/907589706"
}
}
{
"event": "domain.disabled",
"data": {
"id": "907589706",
"url": "www.turningpoint.com",
"endpoint": {},
"timestamp": "2022-06-16 14:58:47.326591+00:00",
"admin_graphql_api_id": "gid://brandssl/Domain/907589706"
}
}
{
"event": "domain.created",
"data": {
"id": "907589706",
"url": "www.turningpoint.com",
"endpoint": {
"id": "75789189brandssl-staging",
"name": "BrandSSL Staging"
},
"timestamp": "2022-06-16 14:58:47.326591+00:00",
"admin_graphql_api_id": "gid://brandssl/Domain/907589706"
}
}
{
"event": "domain.deleted",
"data": {
"id": "907589706",
"url": "www.turningpoint.com",
"endpoint": {},
"timestamp": "2022-06-16 14:58:47.326591+00:00",
"admin_graphql_api_id": "gid://brandssl/Domain/907589706"
}
}
Create a webhook URL
A webhook URL is simply a POST endpoint that a resource server sends updates to. The URL needs to parse a JSON request and return a 200 OK:
// Using Express
app.post("/my/webhook/url", function(req, res) {
// Retrieve the request's body
const event = req.body;
// Do something with event
res.send(200);
});
<?php
// Retrieve the request's body and parse it as JSON
$input = @file_get_contents("php://input");
$event = json_decode($input);
// Do something with $event
http_response_code(200); // PHP 5.4 or greater
?>
When your webhook URL receives an event, it needs to parse and acknowledge the event. Acknowledging an event means returning a 200 OK in the HTTP header. Without a 200 OK in the response header, we’ll keep sending events for the next 36 hours:
Avoid long-running tasks
Enabling webhooks
Here's how to set up a webhook on your BrandSSL account:
- Log in to your dashboard and click on Settings
- Navigate to Webhooks to add your webhook URL
- Save your settings
Tip
Verifying webhook signatures
When enabling webhooks, you have the option to set a secret hash. Since webhook URLs are publicly accessible, the secret hash allows you to verify that incoming requests are from BrandSSL. You can specify any value as your secret hash, but we recommend something random. You should also store it as an environment variable on your server.
If you specify a secret hash, we'll include it in our request to your webhook URL, in a header called x-brandssl-signature. In the webhook endpoint, check if the x-brandssl-signature header is present and that it matches the secret hash you set. If the header is missing, or the value doesn't match, you can discard the request, as it isn't from BrandSSL.
You may need to disable CSRF protection
However, for webhooks to work, you'll need to exempt the webhooks endpoint from CSRF protection (demonstrated in the examples below).
// In an Express-like app:
app.post("/brandssl-webhook", (req, res) => {
// If you specified a secret hash, check for the signature
const secretHash = process.env.BRANDSSL_SECRET_HASH;
const signature = req.headers["x-brandssl-signature"];
if (!signature || (signature !== secretHash)) {
// This request isn't from BrandSSL; discard
res.status(401).end();
}
const payload = req.body;
// It's a good idea to log all received events.
log(payload);
// Do something (that doesn't take too long) with the payload
res.status(200).end()
});
// In a Laravel-like app:
Route::post('/brandssl-webhook', function (\Illuminate\Http\Request $request) {
// If you specified a secret hash, check for the signature
$secretHash = config('services.brandssl.secret_hash');
$signature = $request->header('x-brandssl-signature');
if (!$signature || ($signature !== $secretHash)) {
// This request isn't from BrandSSL; discard
abort(401);
}
$payload = $request->all();
// It's a good idea to log all received events.
Log::info($payload);
// Do something (that doesn't take too long) with the payload
return response(200);
});
# In a Django-like app:
import os
@require_POST
@csrf_exempt
def webhook(request):
secret_hash = os.getenv("BRANDSSL_SECRET_HASH")
signature = request.headers.get("x-brandssl-signature")
if signature == None or (signature != secret_hash):
# This request isn't from BrandSSL; discard
return HttpResponse(status=401)
payload = request.body
# It's a good idea to log all received events.
log(payload)
# Do something (that doesn't take too long) with the payload
return HttpResponse(status=200)
# In a Rails-like app:
class WebhookController < ApplicationController
protect_from_forgery except: :webhook
def webhook
secret_hash = ENV["BRANDSSL_SECRET_HASH"]
signature = request.headers["HTTP_X_BRANDSSL_SIGNATURE"]
if !signature || (signature != secret_hash)
# This request isn't from BrandSSL; discard
head :unauthorized
return
end
payload = params
# It's a good idea to log all received events.
Log.info payload
# Do something (that doesn't take too long) with the payload
head :ok
end
end
Types of events
Here are the events we currently raise. We would add more to this list as we hook into more actions in the future.
Event | Description |
---|---|
domain.created | A new domain name has been created |
domain.disabled | A domain name has been disabled |
domain.deleted | A domain name has been deleted |
Best practices
Use a secret hash
Remember, your webhook URL is public, and anyone can send a fake payload. We recommend using a secret hash so you can be sure the requests you get are from BrandSSL.
Respond quickly
Your webhook endpoint needs to respond within a certain time limit, or we'll consider it a failure and try again. Avoid doing long-running tasks or network calls in your webhook endpoint so you don't hit the timeout.
If your framework supports it, you can have your webhook endpoint immediately return a 200 status code, and then perform the rest of its duties; otherwise, you should dispatch any long-running tasks to a job queue, and then respond.
Be idempotent
Occasionally, we might send the same webhook event more than once. You should make your event processing idempotent (calling the webhook multiple times will have the same effect)
One way of doing this is recording the events you've processed, and then checking if the status has changed before processing the duplicate event: