Skip to content

Commit 4b75fc4

Browse files
committed
chore(webapp): add webhook delivery seed script and design onboarding doc
1 parent 8c3eeb6 commit 4b75fc4

3 files changed

Lines changed: 817 additions & 0 deletions

File tree

ONBOARDING.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# Onboarding: taking over the hosted webhooks PR (#4344) for design
2+
3+
You are picking up **PR #4344 "hosted webhooks, agent channels, and human-in-the-loop"** to own the UX and front-end. This doc gets you from a clean machine to a running dashboard with realistic webhook data you can screenshot, restyle, and iterate on.
4+
5+
The feature is built and green (all backend plumbing, all four dashboard surfaces, the in-app test console). Your job is the visual and interaction design of the dashboard surfaces, not the backend. Everything below is oriented around that.
6+
7+
---
8+
9+
## 1. What you are designing
10+
11+
Hosted webhooks let a Trigger.dev user receive and verify a provider's webhooks (Stripe, GitHub, and so on) as a task, with no ingress or verification code of their own. A `webhook()` handler in their project gets a hosted URL; deliveries to that URL are verified, recorded, and routed to their `onEvent` handler.
12+
13+
The dashboard has **four surfaces you own**, all under the "Webhooks" nav section (teal icon):
14+
15+
| Surface | Route (under `/orgs/:org/projects/:project/env/:env`) | What it shows |
16+
| --- | --- | --- |
17+
| **Deliveries list** | `/webhooks` | Every delivery across all endpoints in the environment. Runs-style filter bar (Status, Webhook, Created, plus a More-filters menu for Delivery ID / Run ID), applied-filter pills, a Webhook column linking to the handler. This is the main screen. |
18+
| **Delivery detail** | `/webhooks/deliveries/:deliveryParam` | One delivery. Main panel is a tabbed view (Event payload / Request headers) rendered as JSON. Sidebar property table (status badge, webhook + run links, external delivery id, idempotency key, timestamps, computed duration, error). Also has a friendly "not available / retained for N days" empty state for expired or bogus links. |
19+
| **Handler detail + Console** | `/webhooks/:webhookParam` | The handler (the `webhook()` in the user's code). Tabs: Deliveries, Runs, Endpoints. This page also hosts the **Webhook Console / Composer** (see section 5), the tool you will lean on for data. |
20+
| **Endpoint detail** | `/webhooks/endpoints/:endpointParam` | One endpoint. Left: scoped deliveries. Right: a **Connect** card (webhook URL, signing secret set/rotate/generate, provider setup rendered from the verifier config), Routing, Scope, Metadata. |
21+
22+
The status vocabulary, badges, and colors live in `components/webhookDeliveries/v1/DeliveryStatus.tsx` and `components/webhookEndpoints/v1/EndpointStatus.tsx`. The nav accent color is a Tailwind token `--color-webhooks` (teal), used via `text-webhooks`.
23+
24+
---
25+
26+
## 2. Get the code
27+
28+
You need the PR branch, `feat/hosted-webhook-ingress`.
29+
30+
```bash
31+
git clone https://github.com/triggerdotdev/trigger.dev.git
32+
cd trigger.dev
33+
gh pr checkout 4344 # lands you on feat/hosted-webhook-ingress
34+
```
35+
36+
If you plan to push design changes back to this branch, coordinate with Eric first: the branch is rebased and force-pushed periodically, so agree on timing or work on a child branch and open a follow-up.
37+
38+
Toolchain: pnpm 10.33.2 via corepack, Node 22+. Use `corepack pnpm` (a bare `pnpm` can be an old global that wipes `node_modules`).
39+
40+
```bash
41+
corepack enable
42+
corepack pnpm install
43+
```
44+
45+
---
46+
47+
## 3. Bring the stack up
48+
49+
Four services and the webapp. Run from the repo root.
50+
51+
```bash
52+
# 1. Core dev services: Postgres, Redis, Electric, MinIO, ClickHouse, s2-lite
53+
corepack pnpm run docker
54+
55+
# 2. Config
56+
cp .env.example .env
57+
```
58+
59+
Now edit `.env` and add the two webhook-delivery replication lines (they are NOT in `.env.example`, and without them the Deliveries list looks empty even after you send webhooks, see section 5):
60+
61+
```bash
62+
# webhook deliveries replication (required for the Deliveries list/detail to populate)
63+
WEBHOOK_DELIVERIES_REPLICATION_CLICKHOUSE_URL=http://default:password@localhost:8123
64+
WEBHOOK_DELIVERIES_REPLICATION_ENABLED=1
65+
```
66+
67+
Then migrate, seed, build, and run:
68+
69+
```bash
70+
corepack pnpm run db:migrate
71+
corepack pnpm run db:seed # creates the References org + hello-world project
72+
73+
# Build the pieces you will run (do these sequentially, not with db:seed running)
74+
corepack pnpm run build --filter webapp --filter trigger.dev --filter "@trigger.dev/sdk"
75+
76+
# Run the webapp (http://localhost:3030)
77+
corepack pnpm run dev --filter webapp
78+
curl -s http://localhost:3030/healthcheck # verify
79+
```
80+
81+
**Log in (dev):** open http://localhost:3030, submit the email `local@trigger.dev`. Dev auto-verifies the magic link (watch the webapp log for `/magic?token=`). That seeded user is an org admin, which matters for the next step.
82+
83+
---
84+
85+
## 4. Turn the feature on
86+
87+
The dashboard is gated by a feature flag, `hasWebhooksAccess` (default off).
88+
89+
- The seeded dev user `local@trigger.dev` is an **admin**, and admins bypass the flag, so on a fresh seed the Webhooks nav section is already visible to you. Nothing to do.
90+
- If you use a non-admin user, flip the flag on the org: set `featureFlags.hasWebhooksAccess = true` on the `Organization` row, or add a global `FeatureFlag` row with key `hasWebhooksAccess`.
91+
92+
If the "Webhooks" section is missing from the left nav, this flag is why.
93+
94+
---
95+
96+
## 5. Get nice data (the part that matters)
97+
98+
Delivery rows are what make these screens interesting: a spread of providers, statuses, payloads, timestamps. Here is how the data flows and how to produce it.
99+
100+
### The pipeline (why an empty list is usually a setup issue, not a bug)
101+
102+
`ingest -> engine (verify, filter, route) -> Postgres WebhookDelivery rows -> replication -> ClickHouse`. The Deliveries **list orders and paginates from ClickHouse**, then hydrates every visible field from Postgres. So if replication is off (section 3), you can create deliveries and still see an empty list. Enable the two replication env vars and restart the webapp.
103+
104+
One caveat baked into the design: replication starts streaming from the moment it is enabled, so deliveries written **before** you turned it on will not appear. Turn replication on first, then generate data.
105+
106+
### Fastest path: the seed script
107+
108+
There is a seed script that inserts a full, stable dataset directly into both stores (Postgres and ClickHouse), so you get realistic screens on a fresh DB with no workers, no `trigger dev`, and no signing secrets to set:
109+
110+
```bash
111+
corepack pnpm --filter webapp run db:seed:webhooks
112+
# optional: deliveries per endpoint (default 45)
113+
corepack pnpm --filter webapp run db:seed:webhooks -- 60
114+
```
115+
116+
It creates six endpoints across different providers and verifier schemes (Stripe, GitHub, Slack, Svix, Discord, and a custom shared-secret one, with a mix of active/inactive and secret-set/not-set), then a spread of deliveries over the last two weeks covering **every** delivery status (SUCCEEDED, FAILED, FILTERED, PENDING, PROCESSING), realistic per-provider payloads and headers, and a mix of test and live. It attaches to the first DEVELOPMENT environment your local user can see (set `WEBHOOK_SEED_PROJECT="<project name>"` to target a specific one), and prints the exact Deliveries URL when it finishes. Re-running clears and reseeds that environment, so you always get the same clean dataset. The script is `apps/webapp/seed-webhook-deliveries.ts`; edit the `ENDPOINTS` array or the status weights to shape the data to whatever you are designing.
117+
118+
Because it writes the ClickHouse rows directly, seeded data shows up **without** the replication setup in section 3. That replication env is only needed for the live and Composer paths below. (The seed uses `WEBHOOK_DELIVERIES_REPLICATION_CLICKHOUSE_URL` if set, otherwise `CLICKHOUSE_URL`, which is already in `.env.example`.)
119+
120+
This is the recommended way to get data. The interactive paths below are for exercising the live pipeline (real verification, real routed runs) or the in-app test console.
121+
122+
### Interactive: create an endpoint (one-time)
123+
124+
The Composer sends to an endpoint, and endpoints only exist once a Trigger project that declares a `webhook()` has been dev-run or deployed. Quickest path: a tiny demo project.
125+
126+
```ts
127+
// demo/src/trigger/demo-webhook.ts
128+
import { webhook, webhooks } from "@trigger.dev/sdk";
129+
130+
export const demoWebhook = webhook({
131+
id: "demo-webhook",
132+
source: webhooks.custom<{ message: string }>({ /* generic HMAC */ }),
133+
onEvent: async ({ event, headers, ctx }) => {
134+
// event is the parsed body, headers is a Web Headers object
135+
},
136+
});
137+
138+
// A real provider, for realistic payloads:
139+
export const stripeWebhook = webhook({
140+
id: "stripe-webhook",
141+
source: webhooks.stripe(),
142+
onEvent: async ({ event }) => {},
143+
});
144+
```
145+
146+
Link that demo project to your local build and run `trigger dev` (see `AGENTS.md` "Testing with the hello-world Reference Project" for linking; the `triggerdotdev/references` repo has ready-made projects). Running `trigger dev` registers the `webhook()` handlers, which creates their endpoints. Set each endpoint's signing secret from the **endpoint detail Connect card** (Generate or paste).
147+
148+
### Interactive: fire deliveries with the Webhook Console
149+
150+
Open the handler detail page (`/webhooks/:webhookParam`). It hosts the **Composer** (`components/webhookConsole/WebhookComposer.tsx`). It has four source tabs and four signature modes, and it injects the delivery straight through the engine in-process, so it is fast and does not consume any real rate budget:
151+
152+
- **Sample tab**: pick a real provider event from the built-in catalog (`@internal/webhook-sources`, six first-class providers plus a large sample manifest). This is the fastest way to get realistic Stripe / GitHub / Svix / Square / Discord payloads with correct-looking headers.
153+
- **Body tab**: hand-write any JSON.
154+
- **Replay tab**: re-send a prior delivery.
155+
- **AI tab**: generate a payload with a prompt.
156+
- **Signature modes** `signed | unsigned | tampered | simulate`: this is how you produce a **spread of delivery statuses**. `signed` (with a secret set) verifies and routes to a SUCCEEDED delivery; `unsigned` and `tampered` produce failed/rejected deliveries. Send a mix to populate every status badge you need to design.
157+
158+
To get SUCCEEDED deliveries whose **runs** also complete (nicest end-to-end data), keep the demo project's `trigger dev` running so the routed task actually executes.
159+
160+
### Interactive: a real provider (most realistic)
161+
162+
For genuine payloads and headers, point the Stripe CLI at an endpoint: `stripe listen --forward-to http://localhost:3030/webhooks/v1/ingest/<opaqueId>`, set that endpoint's `whsec` via the Connect card, then `stripe trigger payment_intent.succeeded`.
163+
164+
---
165+
166+
## 6. Where the front-end code lives
167+
168+
| Area | Path |
169+
| --- | --- |
170+
| Routes (pages) | `apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.webhooks*` |
171+
| Deliveries list / detail components | `apps/webapp/app/components/webhookDeliveries/v1/` (`DeliveriesTable`, `DeliveryStatus`, `WebhookDeliveryFilters`, `DeliveryTimeline`, `useDeliveriesLiveReload`) |
172+
| Endpoint components | `apps/webapp/app/components/webhookEndpoints/v1/` (`EndpointsTable`, `EndpointStatus`) |
173+
| Console / Composer | `apps/webapp/app/components/webhookConsole/` (`WebhookComposer`, `SampleSourcePicker`, `ReplaySourcePicker`) |
174+
| Data (presenters, read-only from your side) | `apps/webapp/app/presenters/v3/WebhookDeliveriesListPresenter.server.ts`, `WebhookDeliveryDetailPresenter.server.ts`, `WebhookDetailPresenter.server.ts`, `webhookComposerEndpoints.server.ts` |
175+
| Nav entry | `apps/webapp/app/components/navigation/SideMenu.tsx` (the `staticSections` "webhooks" push) |
176+
| Path builders | `apps/webapp/app/utils/pathBuilder.ts` (`v3WebhooksPath`, `v3WebhookDeliveryPath`, `v3WebhookEndpointPath`, `v3WebhookTaskPath`) |
177+
| Accent color token | `apps/webapp/app/tailwind.css` (`--color-webhooks`, used as `text-webhooks`) |
178+
| Data seed script | `apps/webapp/seed-webhook-deliveries.ts` (run via `db:seed:webhooks`) |
179+
180+
**Styling:** the webapp is on Tailwind v4 (CSS-first `@theme` in `apps/webapp/app/tailwind.css`, there is no `tailwind.config.js`). Add or change design tokens there.
181+
182+
**Design language to match:** these screens deliberately reuse the Runs page primitives (the filter bar is built from `RunFilters` / `SharedFilters`, the tables mirror the Runs table cells). Match the Runs and Sessions pages, not a new visual system.
183+
184+
---
185+
186+
## 7. Iterating
187+
188+
- **HMR vs restart:** editing a component (`.tsx`) hot-reloads. Editing a `.server.ts` file makes the Remix dev server restart the app (a brief connection refused, then it comes back). Editing Tailwind tokens hot-reloads.
189+
- **Screenshots:** capture from the running dashboard at http://localhost:3030. Save shots outside the repo or to a scratch folder so they do not get committed.
190+
- **Typecheck after non-trivial changes:** `corepack pnpm run typecheck --filter webapp` (about 1 to 2 minutes). For small style tweaks, trust it and let CI catch anything.
191+
- **One boundary gotcha that the dev server will NOT catch:** route files must not leak server-only imports into the client bundle. The dev server tolerates it, but the production build fails. If you touch a route file and import anything server-only, run `corepack pnpm --filter webapp run build:remix` before pushing. Pure component and style edits are unaffected.
192+
193+
---
194+
195+
## 8. Shipping your changes
196+
197+
Follow the repo PR workflow:
198+
199+
- Format and lint before committing: `corepack pnpm run format` (oxfmt) and `corepack pnpm run lint:fix` (oxlint). CI enforces both.
200+
- Commit style is Conventional Commits, for example `feat(webapp): redesign webhook deliveries table`. No emoji, no attribution footer.
201+
- The PR is a **draft** awaiting an AI review pass, then a human review, before it flips to ready. Do not flip it to ready yourself; push your commits and let Eric coordinate the review and any rebase onto `main`.
202+
- CI to expect: `code-quality` (oxfmt + oxlint), `typecheck`, webapp unit shards, and the Playwright `e2e-webapp` job. Style-only changes usually only risk `code-quality`.
203+
204+
---
205+
206+
## 9. Quick reference
207+
208+
- **Webapp:** http://localhost:3030 (port comes from `REMIX_APP_PORT`, falling back to `PORT`/3030).
209+
- **Default docker services:** Postgres 5432, Redis 6379, ClickHouse HTTP 8123 (`default:password`), MinIO, Electric, s2-lite.
210+
- **Feature flag:** `hasWebhooksAccess` (admins bypass).
211+
- **Seed data:** `corepack pnpm --filter webapp run db:seed:webhooks` (append `-- <n>` for deliveries per endpoint).
212+
- **Must-set env for data to show:** `WEBHOOK_DELIVERIES_REPLICATION_ENABLED=1` and `WEBHOOK_DELIVERIES_REPLICATION_CLICKHOUSE_URL=http://default:password@localhost:8123`.
213+
- **Login:** `local@trigger.dev`, magic link auto-verifies in dev.
214+
- **Feature docs:** `docs/webhooks/` (overview, sources, connect, deliveries, channels, human-in-the-loop). Read `overview.mdx` and `deliveries.mdx` first for the mental model behind the screens.
215+
- **PR:** https://github.com/triggerdotdev/trigger.dev/pull/4344
216+
217+
---
218+
219+
## 10. Mental model in one paragraph
220+
221+
A user writes a `webhook()` in their project. On deploy (or `trigger dev`) that handler gets one or more hosted endpoints, each with a signing secret. A provider POSTs to the endpoint's URL; the engine verifies the signature, optionally filters, records a `WebhookDelivery`, and triggers the routed task run. The dashboard reads those deliveries: the list orders them out of ClickHouse and hydrates the rest from Postgres, the detail page reads Postgres directly (it holds the only copy of the event payload and headers). Everything you design sits on top of that delivery record and the endpoint that produced it.

apps/webapp/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"guard:runops-legacy": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" tsx ./scripts/runOpsLegacyGuard.ts",
2020
"db:seed": "tsx seed.ts",
2121
"db:seed:ai-spans": "tsx seed-ai-spans.mts",
22+
"db:seed:webhooks": "tsx seed-webhook-deliveries.ts",
2223
"upload:sourcemaps": "bash ./upload-sourcemaps.sh",
2324
"test": "vitest --no-file-parallelism",
2425
"eval:dev": "evalite watch"

0 commit comments

Comments
 (0)