Webhooks

What are Webhooks?

A webhook is a way for one application to send other applications immediate information. If you would like to react to events that happen within Storyblok in another service, you need a way to inform these services. Storyblok offers webhooks to call to the outside world. This mechanism is quite useful when you want to clear a cache, or start the build process, whenever new content is published.

Screenshot of Storyblok settings. Marker: 1 Story published & unpublished, 2 Datasource entry saved

There are different webhooks that can be configured independently. By default, Storyblok offers one webhook for “Story published & unpublished” {1} and another one for “Datasource entry saved” {2}. Furthermore, webhook events for Workflows, Pipelines, or Feature can be added by adding Apps Storyblok.  Once added,  a POST request will be sent with story_id as JSON payload to the URL configured. It expects to receive a 2xx status code and Content-Type: application/json as response.

Webhooks will not be retried if the service fails to process the event. Publish and/or save are single events and retries could have unwanted side effects. If the endpoint doesn’t return a status 2xx, an email with a message that the webhook failed will be sent to the owner of the space.

If you plan to execute a long-running task, it is recommended that a response is sent immediately (e.g., 202 - Accepted ) and to not wait until the task is finished. The webhook will time out and trigger an error message after 120 seconds.

Available Webhooks

Stories

Webhooks for stories are configured under “Settings” - “General” - “Stories published & unpublished”.

  • published: triggered, when a new or changed story is published

JSON payload
{
  "action": "published",
  "text": "The user test@domain.com published the Story XYZ (xyz)",
  "story_id": 123,
  "space_id": 123
}
  • unpublished: triggered, when a story is unpublished

JSON payload
{
  "action": "unpublished",
  "text": "The user test@domain.com unpublished the Story XYZ (xyz)",
  "story_id": 123,
  "space_id": 123
}
  • deleted: triggered, when a story is deleted (dropdown in the content overview)

JSON payload
{
  "action": "deleted",
  "text": "The user test@domain.com deleted the Story XYZ (xyz)",
  "story_id": 123,
  "space_id": 123
}

Datasources

Webhooks for data sources are configured under “Settings” - “General” - “Datasource entry saved”.

  • datasource_entry_saved: triggered, when a data source entry is saved or added

JSON payload
{
    "text": "The datasource entry value1 has been saved.",
    "action": "datasource_entry_saved",
    "space_id": 123,
    "datasource_slug": "datasource"
}

Workflows

Configuration for the workflow webhook is found under “Settings” - “Workflow” - “Workflow changed”.

  • workflow_stage_changed: triggered, when the workflow stage of a story changes

JSON payload
{
    "text": "The workflow stage of story Blog Overview has been changed to Drafting.",
    "action": "workflow_stage_changed",
    "space_id": 123,
    "story_id": 123,
    "workflow_stage_name": "Drafting"
}

Pipeline

Important:

To use Pipeline webhooks the Pipelines App (https://www.storyblok.com/apps/branches) needs to be installed first.

You will then find the webhook configuration under “Settings” - “General” - “Branch deployed”.

  • branch_deployed: triggered, when a pipeline stage is deployed

JSON Payload
{
    "text": "The branch Staging has been deployed.",
    "action": "branch_deployed",
    "space_id": 123,
    "branch_id": 123
}

Releases

Important:

To use Release webhooks, the Releases App (https://www.storyblok.com/apps/releases_only) needs to be installed first.

After installing the app, you will find the configuration under “Settings” - “General” - “Release merged”.

  • release_merged: triggered, when a release is merged into the currently released content

JSON payload
{
    "text": "The release Test has been merged.",
    "action": "release_merged",
    "space_id": 123,
    "release_id": 123
}

Tasks

Important:

To use Task webhooks, the Tasks App (https://www.storyblok.com/apps/tasks) needs to be installed.

The Task App is used to create automation tasks which then execute a webhook. Tasks are triggered from the user interface by clicking the “Execute” button. To configure a webhook using the Task App, you need to create a new task {1}

Screenshot of new task dialog. Marker: 1 New Task button
  • task_execution: trigger a request from Storyblok

JSON Payload
{
    "task": {
        "id": 123,
        "name": "Trigger Webhook"
    },
    "text": "The user test@domain.com executed the task Trigger Webhook",
    "action": "task_execution",
    "space_id": 123,
    "dialog_values": null
}
  • task_execution with user dialog: the payload contains additional data in dialog_values. When setting up a task, it is possible to add a dialog. The provided user input will be sent to the endpoint as dialog_values.

JSON payload
{
    "task": {
        "id": 123,
        "name": "Trigger Webhook"
    },
    "text": "The user test@domain.com executed the task Trigger Webhook",
    "action": "task_execution",
    "space_id": 123,
    "dialog_values": {
        "name": "Test",
        "environment": "dev"
    }
}

Securing a Webhook

To protect your services, it is always a good idea to make sure that only requests from expected sources are processed. To protect your webhook endpoints, it is necessary to verify if the request is really coming from Storyblok. It is possible to verify the sender of the webhook by verifying the signature that is sent along with the payload and generated with a shared secret key (webhook secret).

Screenshot of Storyblok settings. Marker: 1 Webhook secret - Define/Edit

To enable signatures, the webhook secret needs to be set up. Go to the settings page of your space. Click “Define/Edit” {1} to manage the secret. We recommend using a random string with at least 20 characters (e.g. output of this command openssl rand -hex 20). Once the secret is inserted, press the save button or hit enter to permanently store the data.

Storyblok will add a signature to every webhook request once the webhook secret {1} is configured. The signature is sent in the webhook-signature header of the request. If the webhook secret is not set, the header remains empty.

In your webhook endpoint, you can now check if the webhook-signature was generated with the webhook secret. To use this example, you need to deine the environment variable STORYBLOK_WEBHOOK_SECRET with the value used as webhook secret.

Verify webhook signature - Ruby
class WebhookController < ApplicationController
  def create
    verify_signature(request.raw_post)
    # do stuff when verified
    render json: {success: true}
  end
 
  def verify_signature(payload_body)
    signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV[‘STORYBLOK_WEBHOOK_SECRET'], payload_body)
    raise StandardError, "Signature check failed!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_WEBHOOK_SIGNATURE'])
  end
end
Verfify webhook signature - JS
const crypto = require('crypto');
// signature from header 'webhook-signature'
const signature = '64c6153b5bff67......7d01f244bb7ad253e563'
// body of webhook request
const body = {
    "text": "The user ... published the Story ...",
    "action": "published",
    "space_id": 123,
    "story_id": 123
};

try {
  verifySignature(body, signature);
} catch(err) {
  console.log('Failure: ' + err.message);
}

function verifySignature(body, signature) {
  // webhookSecret configured in Storyblok settings
  const webhookSecret = process.env.STORYBLOK_WEBHOOK_SECRET; // 🙈 secrets shouldn't be in your code
  let bodyHmac = crypto.createHmac('sha1', webhookSecret)
    .update(JSON.stringify(body))
    .digest('hex');

  if (bodyHmac !== signature) {
    throw new Error('Signature mismatch!');
  }
}

Processing Webhooks

There are multiple ways of using the Storyblok webhooks.-- they can be sent to specific services to trigger build scripts (e.g.: Netlify, Vercel), they can send a notification (e.g.: Slack, Discord), or you can even use your own logic to process a webhook.

Depending on the tasks you would like to execute, there are different options for handling an incoming webhook. In this case, it heavily depends on what you would like to trigger with the webhook. There are a couple of ways to execute your logic:

  • By hosting a full-blown server. This depends heavily on what technology you are using.

  • By using serverless functions. Since webhooks logic are usually simple and concise programs, it's a perfect case for serverless functions. Some of the players are AWS Lambda, Google Cloud, Azure Functions, Webtask.io, Vercel, and Netlify Functions.

  • By using low- or no-code environments. Low- and no-code environments already provide predefined building blocks that can be used to create your own flows. Integrations pipedream, n8n, IFTTT, and Zapier.

learn:

Webhooks can, for example, be used to trigger build processes on Netlify or Vercel. Start by reading about how Netlify - Build Hooks or Vercel - Deploy Hooks work.

Distribute Events

Sometimes it is not enough to trigger only one endpoint. To send a single event to multiple environments, a service that distributes the event is required. An example of how such a service could look can be found in this GitHub project: https://github.com/DominikAngerer/webhook-router.

Debugging Webhooks

One thing that makes development much easier, is a sensible way of finding out what is going on when something doesn’t work as expected. To allow you to get this information quickly, we can offer two strategies to debug webhooks:

Webhook logs

Screenshot of Storyblok settings. Marker: 1 Webhook logs - View logs

Logs show you which requests have been sent with which payload. To see a list of all requests, open the link to “View logs” {1}

Debug Endpoint

Sometimes it is not clear if the correct payload arrives at the endpoint or if the event is really triggered as expected. In this case, you can use RequestBin to test the request information you get from webhooks. The tutorial “How to use RequestBin.com to work with Webhooks” covers this topic.