# Guaranteeing webhook delivery in Strapi using QStash

> **Source:** https://upstash.com/blog/strapi-webhooks-qstash
> **Date:** 2022-08-31
> **Author(s):** Andreas Thomas
> **Reading time:** 4 min read
> **Tags:** qstash, strapi, webhook
> **Format:** text/markdown — machine-readable content for agents and LLMs

---

Today we will look at how to automatically retry your
[Strapi](https://strapi.io) webhooks using [QStash](https://upstash.com/qstash).

Strapi is a very popular open-source content management system in the javascript
ecosystem. It allows you to customize almost everything about your CMS and comes
with a lot of features to make your work as easy as possible. Today we will be
looking at Strapi's integrated webhooks, to send a message on certain conditions
A webhook is just an HTTP request to another server that gets
automatically sent whenever some data in Strapi changes. For example, you might
want to call another API, when an editor publishes a new page, or when an entry is
updated.

However there is one shortcoming, that could cause some issues: Strapi could
send a webhook to your API, but it crashes or fails to perform the action
successfully. There is no retry mechanism, it simply sends the webhook once and
forgets about it.

This is where Upstash QStash comes in: QStash is a serverless message queue,
capable of handling retries and guaranteeing successful delivery to your API.
The best part is, that you can use serverless functions to process your
messages, because QStash also sends a webhook to your API, just like Strapi
would do.

You don't need to pay anything, the free tier includes 100 requests per day!

## Let's build something

In order to focus on the interesting parts, we will use a mock for the API,
there are various free services for this and we will be using
[requestcatcher.com](https://requestcatcher.com/) today. It allows us to easily
see what payloads are sent. For production, we have a dedicated
[section](/docs/qstash/features/security) about how you
should secure your webhook API and
[quickstarts](/docs/qstash/quickstarts/vercel-nextjs) for
popular frameworks.

### API

Head over to [requestcatcher.com](https://requestcatcher.com) and create a new
unique endpoint, I chose `https://strapi-qstash.requestcatcher.com/test`, and
keep the tab open.

### Strapi

If you don't have a Strapi project yet, you can create one using
`npx create-strapi-app@latest`

```bash
npx create-strapi-app@latest
? What would you like to name your project? my-strapi-project
? Choose your installation type Quickstart (recommended)
Creating a quickstart project.

// logs omitted

One more thing...
Create your first administrator 💻 by going to the administration panel at:

┌─────────────────────────────┐
│ http://localhost:1337/admin │
└─────────────────────────────┘
```

After you have created your admin account, go to
[http://localhost:1337/admin/settings/webhooks/create](localhost:1337/admin/settings/webhooks/create)
and fill in the form to create a webhook.

![](/blog/strapi-webhooks-qstash/create-webhook.png)

Click `Save` and then `Trigger` to test it.

On the request-catcher tab, you should see a new event with the following body:

```json
{ "event": "trigger-test", "createdAt": "2022-08-29T08:46:44.400Z" }
```

Everything is working now, but as I mentioned in the beginning, there is no
error handling or retrying if the API goes down. Let's fix that in the next
step:

## QStash

Head over to [console.upstash.com/qstash](http://console.upstash.com/qstash) and
create an account if you haven't yet.

1. Make a note of the `QSTASH_TOKEN` here, you will need it later.

![](/blog/strapi-webhooks-qstash/qstash-token.png)

2. Click on `Topics` and create a new one by giving it a descriptive name.

![](/blog/strapi-webhooks-qstash/create-topic.png)

3. After the topic is created, add your API endpoint like this:

![](/blog/strapi-webhooks-qstash/create-endpoint.png)

If at any point in the future you want to send the same event to another API,
you can just add a second endpoint and we will deliver all events to both
endpoints.

4. Now go back to Strapi to update our webhook to use QStash.

5. Replace the URL with
   [https://qstash.upstash.io/v1/publish/strapi.create](https://qstash.upstash.io/v1/publish/strapi.create)
6. Add the `QSTASH_TOKEN` from earlier as `Authorization` header with `Bearer`
   prefix.
7. Click `Save`

![](/blog/strapi-webhooks-qstash/update-webhook.png)

8. Create a new entry of any content type

After a second or two you should see the event logged in requestcatcher:

```json
{
  "event": "entry.create",
  "createdAt": "2022-08-29T11:36:10.463Z",
  "model": "my-model",
  "entry": {
    "id": 1,
    "my-field": "abc",
    "createdAt": "2022-08-29T11:36:10.457Z",
    "updatedAt": "2022-08-29T11:36:10.457Z",
    "publishedAt": null
  }
}
```

## What is happening behind the scenes?

Strapi is now sending the webhook to QStash and using the `QSTASH_TOKEN` to
authenticate the request. QStash will then send the webhook to all endpoints,
that are subscribed to the created topic and retry delivery in case the endpoint
does not return a `2XX` HTTP status code.

From now you no longer have to worry about losing webhooks in case your API is
temporarily down for updates, not reachable over the network or some unforeseen
error occured. By default we are retrying failed messages a couple of times with
increasing exponential backoff. You can check out the details
[here](/pricing/qstash).

## Next steps

In production you should verify the authenticity of incoming webhooks using the
signature, we generate for each message. You can use our typescript sdk
@upstash/qstash or check out
[/docs/qstash/features/security](/docs/qstash/features/security)
to learn how to verify messages.

Let us know your feedback on [Twitter](https://twitter.com/upstash) or
[Discord](https://upstash.com/discord).