Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 103 additions & 24 deletions src/content/docs/integrate/webhooks/about-webhooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ sidebar:
relatedArticles:
- 7fe91aba-930c-4a63-8996-85af6bb605a7
- 95bf5e04-3415-4dc8-9ff6-953171df3f8b
prev: false
tableOfContents:
maxHeadingLevel: 3
topics:
- integrate
- webhooks
Expand All @@ -27,23 +30,23 @@ keywords:
- security
- retry policy
- endpoints
updated: 2026-01-30
updated: 2026-02-04
featured: false
deprecated: false
ai_summary: Comprehensive guide to Kinde webhooks including event monitoring, security best practices, JWT handling, and retry policies for real-time notifications.
---

<Aside type="warning">

If you are experiencing issues with webhooks, it could be because the webhooks request content-type header has changed from `application/json` to `application/jwt`. Read the full details [here](https://kinde.notion.site/Important-notice-for-webhooks-users-7fcf184faece41368603e513267dffc6). We apologize for any inconvenience.
If you are experiencing issues with webhooks, it could be because the webhook request content-type header has changed from `application/json` to `application/jwt`. Read the full details [here](https://kinde.notion.site/Important-notice-for-webhooks-users-7fcf184faece41368603e513267dffc6). We apologize for any inconvenience.

</Aside>

Webhooks are a way of monitoring events in Kinde, so that you can be notified and take action when something you’re monitoring happens.

<Aside type="upgrade">

Depending which Kinde plan you’re on, the number of webhooks you can create might be limited. [View plans](https://kinde.com/pricing/).
Depending on which Kinde plan you’re on, the number of webhooks you can create might be limited. [View plans](https://kinde.com/pricing/).

</Aside>

Expand All @@ -53,55 +56,138 @@ When you create a webhook, you register your own URL endpoints in Kinde and asso

A simple example would be setting up a webhook for when a new user is created in Kinde. This event can be set up to trigger an update to your database, enabling your system to automatically send a welcome email.

## Webhooks terminology

You’ll come across these terms when you start using webhooks.

**Endpoint** - the URL called when an event occurs.

**Event type** - something that happens in Kinde, e.g. a user is created.

**Event** - an instance of an event type that happens in Kinde, e.g. Jane Doe just signed up.

**Request** - the request object sent to your endpoint that contains a JSON Web Token (JWT) with the event data and details about the event.

**Attempt** - an instance of an HTTP request attempt. Multiple request attempts may be made if Kinde does not receive a 200 response.


## Webhooks flow

1. Webhook endpoints are registered in Kinde.
2. An event occurs in your account, e.g. `user.created`.
3. Your endpoint will be called with a JWT containing the data object about that event, and details about the webhook.
1. If a `200` response is received, the workflow is complete.
2. If it fails to receive a response, it will retry using a back-off policy (see **Webhooks responses and retry policy** below).
2. If Kinde fails to receive a response, it will retry using a back-off policy (see **Webhooks responses and retry policy** below).
4. In your application you should verify the authenticity of the webhook request, and decode the JWT (see **Webhooks security** below).
5. Logic is triggered in your application using the event data.

## Webhook request content-type

Kinde uses the `application/jwt` content-type for webhook requests.

The body of the request is a JWT token that is signed by Kinde.

```text
eyJhbGciOiJSUzI1NiIsImtpZCI6IjJhOmUwOjRmO...
```

### Example decoded payload

Here is an example decoded payload.

You can use the [Kinde webhook package](https://www.npmjs.com/package/@kinde/webhooks) to decode and verify the JWT. You can also use the [Kinde Online JWT decoder](https://www.kinde.com/tools/online-jwt-decoder/) to view it online.

```json
{
"data": {
"user": {
"email": "user@example.com",
"first_name": "Test",
"id": "kp_1234567890",
"is_password_reset_requested": false,
"is_suspended": false,
"last_name": "",
"organizations": [
{
"code": "org_1234567890",
"permissions": null,
"roles": null
}
],
"phone": null,
"username": null
}
},
"event_id": "event_1234567890",
"event_timestamp": "2026-02-03T12:00:00.000Z",
"source": "admin",
"timestamp": "2026-02-03T12:00:00.000Z",
"type": "user.created"
}
```

## Webhooks identifiers

There are a number of unique IDs you will come across with webhooks.

- **Webhook ID**: prefixed with `webhook_`, this refers to the webhooks that you create in Kinde. Webhook IDs are exposed via the Kinde Management API webhook endpoints. You can get a list of webhooks via the endpoint `GET /api/v1/webhooks`
- **Webhook ID**: prefixed with `webhook_`, this refers to a webhook that you create in Kinde. Webhook IDs are exposed via the Kinde Management API webhook endpoints. You can get a list of webhooks via the endpoint `GET /api/v1/webhooks`
- **Event ID**: prefixed with `event_`, this is the ID of an event that has occurred in your account, and is included as `event_id` in webhook request payloads. You can use this ID to retrieve an event from Kinde’s API using `GET /api/v1/events/{event_id}`
- **Webhook Request ID**: found in the webhook headers as `webhook-id`, this is unique to a webhook and event, and will not change between attempts. You can use this as the idempotency key (see Webhooks security below).
- **Webhook Request ID**: found in webhook request headers as `webhook-id`, this is unique to a webhook and event, and will not change between attempts. You can use this as the idempotency key (see Webhooks security below).

## Webhooks decoder

Kinde provides a [webhooks decoder](https://github.com/kinde-oss/webhook) to help you decode and validate webhook tokens, for an easier implementation.
Kinde provides a [webhooks decoder](https://www.npmjs.com/package/@kinde/webhooks) to help you decode and validate webhook tokens for easier implementation.

Here's how to use it in a JavaScript application:

```javascript
import { decodeWebhook } from "@kinde/webhooks";

const decodedToken = await decodeWebhook(
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjJhOmUwOjRmO...",
"https://your-kinde-subdomain.kinde.com"); // KINDE_ISSUER_URL

if(!decodedToken) {
// return early if the webhook is not valid
// send a 400 response to Kinde
return;
}

if(decodedToken.type === "user.created") {
// send a 200 response to Kinde
// handle user created event
}
```

If you are using a different programming language, you will need the JWKS URL to verify the JWT. You can find the JWKS URL at`https://<your_subdomain>.kinde.com/.well-known/jwks`.

## Webhooks security

### Idempotency key

Each webhook has a webhook-id header that can be used to avoid reprocessing the same webhook request. This is referred to as the idempotency key meaning that multiple requests will result in the same outcome.
Each webhook request has a `webhook-id` header that can be used to avoid reprocessing the same webhook request. This is referred to as the **idempotency key**, meaning that multiple requests will result in the same outcome.

### Prevent Replay Attacks

A replay attack occurs when a payload is intercepted, potentially modified and re-transmitted. Kinde webhooks contain a timestamp attribute in the payload, and the payload is a signed JSON Web Token (JWT). This allows you to verify that the request came from Kinde, and check the timestamp to prevent replay attacks.
A replay attack occurs when a payload is intercepted, potentially modified, and re-transmitted. Kinde webhooks contain a `timestamp` attribute in the payload, and the payload is a signed JSON Web Token (JWT). This allows you to verify that the request came from Kinde, and check the `timestamp` to prevent replay attacks.

### Verify an event

You can request events by their event_id using the Kinde management api via `/api/v1/events/{event_id}`. The event_id is provided in all webhook requests.
You can request events by their `event_id` using the Kinde Management API via `/api/v1/events/{event_id}`. The `event_id` is provided in all webhook requests.

## Webhooks best practices

Ensure webhooks are secure and optimized.

- Handle duplicate request attempts by checking that the webhook-id header has not already been received and processed, making your processing idempotent
- Verify the JWT to ensure that the request came from Kinde using your public JWKS file
- Only add the events to your endpoint that your implementation requires, listening for all events creates unnecessary load on your server
- Only subscribe to the events that your implementation requires; listening for all events creates unnecessary load on your server
- Endpoints must use HTTPS
- Return a 200 from your endpoint to indicate it has received the request

## Webhooks responses and retry policy

If we fail to receive a 200 response from your endpoint, Kinde operates a back-off retry policy, this roughly equates to the following attempts:
If Kinde fails to receive a 200 response from your endpoint, Kinde operates a back-off retry policy, which roughly equates to the following attempts:

- Immediately
- After 5 seconds
Expand All @@ -114,18 +200,11 @@ If we fail to receive a 200 response from your endpoint, Kinde operates a back-o

## Webhooks auto-disable policy

Webhooks that fail consistently for 72 hours are now automatically disabled, and you’ll receive an email notification when this happens. This prevents runaway failures from impacting your system and keeps you informed so you can investigate and re-enable when ready.

## Webhooks terminology
Webhooks that fail consistently for 72 hours are automatically disabled, and you’ll receive an email notification when this happens. This prevents runaway failures from impacting your system and keeps you informed so you can investigate and re-enable when ready.

You’ll come across these terms when you start using webhooks.
## Next steps

**Endpoint** - the URL called when an event occurs.

**Event type** - something that happens in Kinde, e.g. a user is created.

**Event** - an instance of an event type that happens in Kinde, e.g. Jane Doe just signed up.

**Request** - the request object sent to your endpoint that contains a JSON Web Token (JWT) with the event data and details about the event.
Now that you know about webhooks, your next steps are to:

**Attempt** - an instance of a request making an http request, multiple request attempts may be made if we do not receive a 200 response.
- [Add and manage webhooks](/integrate/webhooks/add-manage-webhooks)
- [Set up webhooks using Next.js](/integrate/webhooks/webhooks-nextjs)
28 changes: 18 additions & 10 deletions src/content/docs/integrate/webhooks/add-manage-webhooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ sidebar:
relatedArticles:
- 84581694-59d6-4a02-ab8b-c7a2889713d5
- 95bf5e04-3415-4dc8-9ff6-953171df3f8b
tableOfContents:
maxHeadingLevel: 3
app_context:
- m: settings
s: webhooks
Expand All @@ -31,7 +33,7 @@ keywords:
- local testing
- ngrok
- localtunnel
updated: 2024-01-15
updated: 2026-02-03
featured: false
deprecated: false
ai_summary: Guide to adding and managing webhooks in Kinde including security setup, trigger configuration, API management, and local testing with proxy tools.
Expand All @@ -43,7 +45,7 @@ If you are experiencing issues with webhooks, it could be because the webhook re

</Aside>

Webhooks are outbound calls that are sent from Kinde when a specified events occurs. Calls are sent using HTTPS REST calls to a verified external URL you specify.
Webhooks are outbound calls that are sent from Kinde when a specified event occurs. Calls are sent using HTTPS REST calls to a verified external URL you specify.

Use Kinde’s webhooks to create triggers and push event data to your other systems and applications.

Expand All @@ -57,29 +59,29 @@ Webhook limits apply if you are on the Kinde free plan. [See all plans](https://

Webhook request payloads are signed and sent as a JSON Web Token (JWT) from Kinde.

You’re probably using a library to validate your JWTs and they will require the url for your public JSON Web Key (also known as a `jwks` file).
If you’re using a library to validate your JWTs, it will require the URL for your public JSON Web Key (also known as a `JWKS` file). The JWKS file can be found at `https://<your_subdomain>.kinde.com/.well-known/jwks`

The jwks file can be found at `https://<your_subdomain>.kinde.com/.well-known/jwks`
If you are using a JavaScript backend, you can use the [Kinde webhooks decoder](https://www.npmjs.com/package/@kinde/webhooks) to decode and verify the JWT, outlined in the [webhooks decoder](/integrate/webhooks/about-webhooks#webhooks-decoder) section.

## Add a webhook

1. Go to **Settings > Environment > Webhooks**.
1. Go to **Settings > Webhooks**.
2. Select **Add webhook**.
3. Give the webhook a name and enter a description so it’s clear what it’s for.
4. Enter the external URL.
5. Select the event triggers. You can choose from a number of triggers and categories. See below for definitions.

## Edit a webhook

1. Go to **Settings > Environment > Webhooks**.
1. Go to **Settings > Webhooks**.
2. Select the webhook name to open the details window.
3. Update events and triggers.
4. Select **Save**.

## Delete a webhook

1. Go to **Settings > Environment > Webhooks**.
2. Select the three dots menu next to the webhook you want to delete.
1. Go to **Settings > Webhooks**.
2. Select the three-dot menu next to the webhook you want to delete.
3. Select **Delete webhook**. A confirmation message appears.
4. Confirm you want to delete the webhook.

Expand Down Expand Up @@ -151,7 +153,13 @@ In order to use webhooks, you need a publicly available URL. To test locally you

Here are a couple of free tools to help you to do this.

- [localtunnel](https://theboroer.github.io/localtunnel-www/) - fast and easy, we recommend using the `subdomain` attribute to fix the issued subdomain.
- [localtunnel](https://theboroer.github.io/localtunnel-www/) - fast and easy, we recommend using the `--subdomain` attribute to fix the issued subdomain.
- [Ngrok](https://ngrok.com/) - for intermediate users, requires some dev experience to get set up.

Each of these services exposes a local port though a public URL that can be set as your web socket.
Each of these services exposes a local port through a public URL that can be set as your webhook endpoint.

You can also use the [webhook.site](https://webhook.site/) to check the webhook request payload and headers.

## Next steps

- [Set up webhooks using Next.js](/integrate/webhooks/webhooks-nextjs)
Loading