⚠️Draft Content

Storyblok Raises $80M Series C - Read News

What’s the True Total Price of Enterprise CMS? Find out here.

Skip to main content

Webhooks

Warn:

If you are using Legacy Webhook we recommend you update it to the latest to take advantage of all the latest event triggers.

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 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 very useful when you want to clear a cache or start a build process, for example whenever new content is published or updated.

Screenshot of Storyblok Webhooks tab
1
2
3

Storyblok Webhooks tab

In Storyblok creating Webhook is pretty straightforward. You can follow the steps in the above image to create your first Webook.

Storyblok Create new Webhook
1
2
3

Storyblok Create new Webhook

In order to create a Webhook you have to fill 3 required fields.

Webhook with all the triggers enabled

Webhook with all the triggers enabled

Once added, a POST request will be sent with a JSON payload to the specified URL. It expects to receive a 2xx status code and Content-Type: application/json as a 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 you plan to execute a long-running task, it is recommended that a response is sent immediately (e.g., 202 - Accepted ) instead of waiting until the task is finished. The webhook will time out and trigger an error message after 120 seconds.

Available Triggers

Here is a list of all the available triggers with their respective JSON payload.

Story

  1. Story published
  2. Story unpublished
  3. Story deleted
  4. Story moved
  • published: triggered when a story is published
JSON payload
        
      {
  "text": "The user username@domain.com published the Story Test (test)\nhttps://app.storyblok.com/#/me/spaces/123/stories/0/0/1234",
  "action": "published",
  "space_id": 123,
  "story_id": 1234,
  "full_slug": "test"
}
    
  • unpublished: triggered, when a story is unpublished
JSON payload
        
      {
  "text": "The user username@domain.com unpublished the Story Test (test)\nhttps://app.storyblok.com/#/me/spaces/123/stories/0/0/1234",
  "action": "unpublished",
  "space_id": 123,
  "story_id": 1234,
  "full_slug": "test"
}
    
  • deleted: triggered, when a story is deleted (dropdown in the content overview)
JSON payload
        
      {
  "text": "The user username@domain.com deleted the Story Test (test)\nhttps://app.storyblok.com/#/me/spaces/123/stories/0/0/1234",
  "action": "deleted",
  "space_id": 123,
  "story_id": 1234
}
    

  • moved: triggered when a story is moved from a folder or to a folder
JSON payload
        
      {
  "text": "The user username@domain.com moved the Story Test (test)\nhttps://app.storyblok.com/#/me/spaces/123/stories/0/0/1234",
  "action": "moved",
  "space_id": 123,
  "story_id": 1234
}
    

Datasource

  1. Entry saved
  • entries_updated: triggered when a new datasource entry is saved or added.
JSON payload
        
      {
  "text": "The datasource entry red has been saved.",
  "action": "entries_updated",
  "space_id": 123,
  "datasource_slug": "background-colors"
}
    
  • datasource_entry_saved: triggered, when a data source entry is saved or added (legacy webhook). 
JSON payload
        
      {
  "text": "The datasource entry red has been saved.",
  "action": "datasource_entry_saved",
  "space_id": 123,
  "datasource_slug": "background-colors"
}
    

Asset

  1. Asset created
  2. Asset replaced
  3. Asset deleted
  4. Asset restored
  • created: triggered when an asset is uploaded
JSON payload
        
      {
    "text": "The user username@email.com created the Asset acm_uploadzzzzz.png\nhttps: //a.storyblok.com/f/132139/1440x840/23e366ce67/acm_uploadzzzzz.png",
    "action": "created",
    "asset_id": 10288746,
    "space_id": 123
}
    
  • replaced: triggered when an asset is replaced
JSON payload
        
      {
    "text": "The user username@email.com replaced the Asset acm_uploadzzzzz.png\nhttps: //a.storyblok.com/f/132139/1440x840/23e366ce67/acm_uploadzzzzz.png",
    "action": "replaced",
    "asset_id": 10288746,
    "space_id": 123
}
    
  • deleted: triggered when an asset is deleted
JSON payload
        
      {
    "text": "The user username@email.com deleted the Asset acm_uploadzzzzz.png\nhttps: //a.storyblok.com/f/132139/1440x840/23e366ce67/acm_uploadzzzzz.png",
    "action": "deleted",
    "asset_id": 10288746,
    "space_id": 123
}
    
  • restored: triggered when an asset is restored
JSON payload
        
      {
    "text": "The user username@email.com restored the Asset acm_uploadzzzzz.png\nhttps: //a.storyblok.com/f/132139/1440x840/23e366ce67/acm_uploadzzzzz.png",
    "action": "restored",
    "asset_id": 10288746,
    "space_id": 123
}
    

User management

  1. User added
  2. User removed
  3. User’s roles updated
  • added: triggered when a new user is added to the space
JSON payload
        
      {
    "text": "The user newuser@gmail.com was added by username@email.com",
    "action": "added",
    "user_id": 127771,
    "space_id": 123
}
    
  • removed: triggered when a user is removed from the space
JSON payload
        
      {
    "text": "The user newuser@gmail.com was removed by username@email.com",
    "action": "removed",
    "user_id": 127771,
    "space_id": 123
}
    
  • roles_updated: triggered when a user role is updated
JSON payload
        
      {
    "text": "The user newuser@gmail.com was roles_updated by username@email.com",
    "action": "roles_updated",
    "user_id": 127771,
    "space_id": 123
}
    

Workflow

  1. Workflow changed
  • stage.changed: triggered when the workflow stage of a story changes.
JSON payload
        
      {
  "text": "The workflow stage of story Test has been changed to Reviewing.",
  "action": "stage.changed",
  "space_id": 123,
  "story_id": 1234,
  "workflow_name": "Default",
  "workflow_stage_name": "Reviewing"
}
    
  • workflow_stage_changed: triggered, when the workflow stage of a story changes (legacy webhook)
JSON payload
        
      {
  "text": "The workflow stage of story Test has been changed to Reviewing.",
  "action": "workflow_stage_changed",
  "space_id": 123,
  "story_id": 1234,
  "workflow_name": "Default",
  "workflow_stage_name": "Reviewing"
}
    

Pipeline

Important:

To use Pipeline webhooks the Pipelines App needs to be installed first.

  1. Pipeline deployed
  • deployed: triggered, when a pipeline stage is deployed
JSON Payload
        
      {
  "text": "The branch Staging has been deployed.",
  "action": "deployed",
  "space_id": 123,
  "branch_id": 9218
}
    
  • branch_deployed: triggered, when a pipeline stage is deployed(legacy webhook)
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 needs to be installed first.

  1. Release merged
  • merged: triggered when a release is merged into the currently released content
JSON payload
        
      {
  "text": "The release Test has been merged.",
  "action": "merged",
  "space_id": 123,
  "release_id": 109114
}
    
  • release_merged: triggered, when a release is merged into the currently released content(legacy webhook)
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 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-Manager App, you need to create a new task {1}

Task-Manager App
1

Task-Manager App

Create new Task in Task-Manager App
1
2
3

Create new Task in Task-Manager App

  • 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

Note:

In order to set the webhook secret you require an Entry plan or above.

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).

Define Webhook secret
1

Define Webhook secret

Define Webhook secret legacy
1

Define Webhook secret legacy

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 define the environment variable STORYBLOK_WEBHOOK_SECRET with the value used as webhook secret.

learn:

Most JavaScript frameworks parse incoming data into JSON automatically, Storyblok's payload is meant to be used as raw text without parsing.

To ensure seamless integration, handle the data without JSON parsing. Check your framework's documentation or explore methods to directly use the raw text data provided by Storyblok.

Verfify webhook signature - Nodejs + Express
        
      import express from 'express'
import { createHmac } from 'node:crypto'

const app = express()
const port = 4000

//Adding a new property in the request called rawBody with unparsed data
app.use(function (req, res, next) {
  req.rawBody = ''
  req.setEncoding('utf8')
  req.on('data', function (chunk) {
    req.rawBody += chunk
  })
  req.on('end', function () {
    next()
  })
})

//If you are using something like body parser for your project use it here

app.get('/', (req, res) => {
  res.send('Hello World!')
})
app.post('/', async (req, res) => {
  const payload = req.rawBody
  const signature = req.headers['webhook-signature']
  const webhookSecret = 'hello' //What you used in Storyblok
  const generatedSignature = createHmac('sha1', webhookSecret)
    .update(payload)
    .digest('hex')
  const isValid = generatedSignature === signature
  if (isValid) {
    //you logic goes here
  }
  res.json({ isValid, generatedSignature, signature, payload })
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
    
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
    
Verify webhook signature - NextJS App Router
        
      import { createHmac } from 'crypto'

export async function POST(request) {
  const payload = await request.text()
  const signature = await request.headers.get('webhook-signature')
  const generateSignature = getSignature(payload)
  const isValid = generateSignature === signature
  if (isValid) {
    //You code goes here
  }
  return Response.json({
    isValid,
    payload,
  })
}
const webhookSecret = 'hello' //What you used in Storyblok
const getSignature = (body) =>
  createHmac('sha1', webhookSecret).update(body).digest('hex')
    

Processing Webhooks

There are multiple ways to use the Storyblok webhooks. For instance, 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 handler 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 usually triggers a concise program, 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. Example providers are 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 happening 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

Webhook logs
1

Webhook logs

Webhook logs legacy
1

Webhook logs legacy

Logs show you which requests have been sent with which payload.

Webhook logs list

Webhook logs list

To see the JSON payload you can click on a log item.

Debug Endpoint

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