import { Callout, Card, Cards, Steps, Tabs } from "nextra/components";
import UniversalTabs from "@/components/UniversalTabs";

# Webhooks

Webhooks allow external systems to trigger Hatchet workflows by sending HTTP requests to dedicated endpoints. This enables real-time integration with third-party services like GitHub, Stripe, Slack, or any system that can send webhook events.

## Guides

We have step-by-step guides for the most common webhook integrations:

- [**Stripe**](/cookbooks/webhooks-stripe) — payments, subscriptions, invoices
- [**GitHub**](/cookbooks/webhooks-github) — pull requests, issues, pushes
- [**Slack**](/cookbooks/webhooks-slack) — slash commands, interactive components, event subscriptions

## Creating a webhook

To create a webhook, you'll need to fill out some fields that tell Hatchet how to determine which workflows to trigger from your webhook, and how to validate it when it arrives from the sender. In particular, you'll need to provide the following fields:

#### Name

The **Webhook Name** is tenant-unique (meaning a single tenant can only use each name once), and is used to create the URL for where the incoming webhook request should be sent. For instance, if your tenant id was `d60181b7-da6c-4d4c-92ec-8aa0fc74b3e5` and your webhook name was `my-webhook`, then the URL might look like `https://cloud.onhatchet.run/api/v1/stable/tenants/d60181b7-da6c-4d4c-92ec-8aa0fc74b3e5/webhooks/my-webhook`. Note that you can copy this URL in the dashboard.

#### Source

The **Source** indicates the source of the webhook, which can be a pre-provided one for easy setup like Stripe or Github, or a "generic" one, which lets you configure all of the necessary fields for your webhook integration based on what the webhook sender provides.

#### Event Key Expression

The **Event Key Expression** is a [CEL](https://cel.dev/) expression that you can use to create a dynamic event key from the payload and headers of the incoming webhook. You can either set this to a constant value, like `webhook`, or you could set it to something dynamic using those two options. Some examples:

1. `'stripe:' + input.type` would create event keys where `'stripe:'` is a prefix for all keys indicating the webhook came from Stripe, and `input.type` selects the `type` field off of the webhook payload and uses it to create the final event key. The result might look something like `stripe:payment_intent.created`.
2. `'github:' + headers['x-github-event'] + ':' + input.action` could create a key like `github:star:created`

<Callout type="info">
  The result of the event key expression is what Hatchet will use as the event
  key, so you'd need to set a matching event key as a trigger on your workflows
  in order to trigger them from the webhooks you create. For instance, you might
  add `on_events=["stripe:payment_intent.created"]` to listen for payment intent
  created events in the previous example.
</Callout>

#### Scope Expression (Optional)

The **Scope Expression** is an optional [CEL](https://cel.dev/) expression that evaluates to a string used to filter which workflows to trigger. This is useful when you have multiple workflows listening to the same event key but want to route to specific workflows based on the webhook content.

Like the event key expression, you have access to `input` (the webhook payload) and `headers` (the request headers). Some examples:

1. `input.customer_id` would use the customer ID from the payload as the scope
2. `headers['x-organization-id']` would use a header value as the scope
3. `input.metadata.environment` could route to different workflows based on environment

#### Static Payload (Optional)

The **Static Payload** is an optional JSON object that gets merged with the incoming webhook payload before it's passed to your workflows. This is useful for:

- Adding constant metadata to all events from this webhook
- Injecting configuration values that aren't in the original payload
- Overriding specific fields from the incoming payload

<Callout type="info">
  When there's a key collision between the incoming webhook payload and the
  static payload, the static payload values take precedence.
</Callout>

For example, if you set a static payload of `{"source": "stripe", "environment": "production"}` and receive a webhook with `{"type": "payment_intent.created", "source": "api"}`, the final payload passed to your workflow would be `{"type": "payment_intent.created", "source": "stripe", "environment": "production"}`.

#### Authentication

Finally, you'll need to specify how Hatchet should authenticate incoming webhook requests. For non-generic sources like Stripe and Github, Hatchet has presets for most of the fields, so in most cases you'd only need to provide a secret.

If you're using a generic source, then you'll need to specify an authentication method (either basic auth, an API key, HMAC-based auth), and provide the required fields (such as a username and password in the basic auth case).

<Callout type="warning">
  Hatchet encrypts any secrets you provide for validating incoming webhooks.
</Callout>

The different authentication methods require different fields to be provided:

- **Pre-configured sources** (Stripe, GitHub, Slack): Only require a webhook secret
- **Generic sources** require different fields depending on the selected authentication method:
  - **Basic Auth**: Requires a username and password
  - **API Key**: Requires header name containing the key on incoming requests, and secret key itself
  - **HMAC**: Requires a header name containing the secret on incoming requests, the secret itself, an encoding method (e.g. hex, base64), and an algorithm (e.g. `SHA256`, `SHA1`, etc.).

## Usage

While you're creating your webhook (and also after you've created it), you can copy the webhook URL, which is what you'll provide to the webhook _sender_.

Once you've done that, the last thing to do is register the event keys you want your workers to listen for so that they can be triggered by incoming webhooks.

For examples on how to do this, see the [documentation on event triggers](/v1/events#declaring-event-triggers).
