> ## Documentation Index
> Fetch the complete documentation index at: https://trigger.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Trigger tasks from Hookdeck webhooks

> Forward webhooks from a Hookdeck connection to a hosted Trigger.dev endpoint, verified with the Hookdeck project's signing secret, and run a task for each event.

[Hookdeck](https://hookdeck.com) receives webhooks from external services, then queues, logs and forwards them to a destination. Point a Hookdeck connection at a hosted webhook endpoint and Trigger.dev verifies each forwarded request with the Hookdeck project's signing secret, deduplicates Hookdeck's retries, and runs your task with the original provider's payload.

## Task code

Declare the endpoint with `webhooks.hookdeck()` and subscribe a task to it. Hookdeck forwards the original provider's body unchanged, so pass that provider's event type. This example relays Stripe events:

```ts /trigger/stripe-via-hookdeck.ts theme={"theme":"css-variables"}
import type Stripe from "stripe";
import { webhook, webhooks } from "@trigger.dev/sdk";

export const stripeViaHookdeck = webhooks.endpoint.define({
  id: "stripe-via-hookdeck",
  source: webhooks.hookdeck<Stripe.Event>(),
});

export const paymentSucceeded = webhook({
  id: "payment-succeeded",
  endpoint: stripeViaHookdeck,
  filter: "event.type == 'payment_intent.succeeded'",
  onEvent: async ({ event }) => {
    console.log("Payment succeeded", event.id);
  },
});
```

Run `trigger dev` or deploy, then copy the endpoint's URL from the Webhooks page in the dashboard.

## Set up Hookdeck

**1. Create a connection**

In the [Hookdeck dashboard](https://dashboard.hookdeck.com), create a [connection](https://hookdeck.com/docs/connections) from your source (for example a Stripe source) to an HTTP destination whose URL is the endpoint's URL. Keep the destination's authentication on **Hookdeck Signature**, the default for new destinations: Hookdeck only signs forwarded requests with it set.

**2. Store the signing secret**

Copy the signing secret from **Settings > Secrets** in Hookdeck and set it as the endpoint's signing secret in the Trigger.dev dashboard. It's one secret for the whole Hookdeck project.

## Test it

Send a webhook to your Hookdeck source URL, or trigger a real event in the upstream provider. In Hookdeck, the event shows a `200` response from the destination. In Trigger.dev, the endpoint shows a succeeded delivery and the `payment-succeeded` task has a completed run with the Stripe event as its payload.

A request signed with a different secret gets a `400`, which Hookdeck reports as a failed attempt.

## Rotate the signing secret

Roll the signing secret under **Settings > Secrets** in Hookdeck with an expiry instead of expiring it immediately. Until the old secret expires, Hookdeck signs each request with both secrets, sending the previous secret's signature in `X-Hookdeck-Signature-2`. The endpoint accepts either, so deliveries keep verifying while you copy the new secret to Trigger.dev. Set a grace period when you store it to keep accepting the old secret for a while.

## Deduplication

Hookdeck retries and manual replays of the same event carry the same `X-Hookdeck-Eventid`, and the endpoint uses it as the [deduplication](https://trigger.dev/docs/webhooks/deliveries.md#deduplication) key, so a retried event runs your task once.

If you changed the header prefix in the Hookdeck project's settings, the event id arrives under the new prefix. Pass the prefix so deduplication keeps working:

```ts /trigger/stripe-via-hookdeck.ts theme={"theme":"css-variables"}
import type Stripe from "stripe";
import { webhooks } from "@trigger.dev/sdk";

export const stripeViaHookdeck = webhooks.endpoint.define({
  id: "stripe-via-hookdeck",
  source: webhooks.hookdeck<Stripe.Event>({ headerPrefix: "x-acme" }),
});
```

The signature headers keep the `X-Hookdeck-` name whatever the prefix.

## Trigger a task through the API instead

You can also point a Hookdeck destination at the [trigger task API](https://trigger.dev/docs/management/tasks/trigger.md) without a hosted endpoint. Hookdeck authenticates with your API key instead of a signature, and a transformation wraps the body in the `payload` field the API expects.

1. Create a destination with the URL `https://api.trigger.dev/api/v1/tasks/<task-id>/trigger`, method `POST`, and **Bearer token** authentication using a **Trigger only** environment API key.

2. Add a [transformation](https://hookdeck.com/docs/transformations):

   ```javascript theme={"theme":"css-variables"}
   addHandler("transform", (request, context) => {
     request.body = { payload: { ...request.body } };
     return request;
   });
   ```

3. Create a connection from your source to that destination and transformation.

```ts /trigger/webhook-handler.ts theme={"theme":"css-variables"}
import { task } from "@trigger.dev/sdk";

export const webhookHandler = task({
  id: "webhook-handler",
  run: async (payload: Record<string, unknown>) => {
    console.log("Received webhook:", payload);
  },
});
```

This path has no deduplication of Hookdeck retries; each forwarded request triggers a run.

For more on Hookdeck, see the [Hookdeck documentation](https://hookdeck.com/docs).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.