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

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
In order to create a Webhook you have to fill 3 required fields.

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 the endpoint doesn’t return a status 2xx
, an email with a message that the webhook failed will be sent to the space owner.
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
Story published
Story unpublished
Story deleted
Story saved
Story moved
published
: triggered when a story is published
{
"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
{
"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)
{
"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
}
saved
: triggered when a story is created or an existing story is saved
{
"text": "The user username@domain.com saved the Story Test (test)\nhttps://app.storyblok.com/#/me/spaces/123/stories/0/0/1234",
"action": "saved",
"space_id": 123,
"story_id": 1234
}
moved
: triggered when a story is moved from a folder or to a folder
{
"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
Entry saved
entries_updated
: triggered when a new datasource entry is saved or added.
{
"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).
{
"text": "The datasource entry red has been saved.",
"action": "datasource_entry_saved",
"space_id": 123,
"datasource_slug": "background-colors"
}
Asset
Asset created
Asset replaced
Asset deleted
Asset restored
created
: triggered when an asset is uploaded
{
"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
{
"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
{
"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
{
"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
User added
User removed
User’s roles updated
added
: triggered when a new user is added to the space
{
"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
{
"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
{
"text": "The user newuser@gmail.com was roles_updated by username@email.com",
"action": "roles_updated",
"user_id": 127771,
"space_id": 123
}
Workflow
Workflow changed
stage.changed
: triggered when the workflow stage of a story changes.
{
"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)
{
"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
To use Pipeline webhooks the Pipelines App needs to be installed first.
Pipeline deployed
deployed
: triggered, when a pipeline stage is deployed
{
"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)
{
"text": "The branch Staging has been deployed.",
"action": "branch_deployed",
"space_id": 123,
"branch_id": 123
}
Releases
To use Release webhooks, the Releases App needs to be installed first.
Release merged
merged
: triggered when a release is merged into the currently released content
{
"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)
{
"text": "The release Test has been merged.",
"action": "release_merged",
"space_id": 123,
"release_id": 123
}
Tasks
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

Create new Task in Task-Manager App
task_execution
: trigger a request from Storyblok
{
"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.
{
"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
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

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.
import { createHmac } from 'node: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 = createHmac('sha1', webhookSecret)
.update(JSON.stringify(body))
.digest('hex');
if (bodyHmac !== signature) {
throw new Error('Signature mismatch!');
}
}
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
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.
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

Webhook logs legacy
Logs show you which requests have been sent with which payload.

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.