Webhooks staging checklist
This checklist is for validating the organization webhook feature in a real staging environment before broader rollout.
Scope
Covered events:
client.createdprocess_assignment.accepted
Supported consumers:
- n8n
- Make
1. Deploy prerequisites
Before deploying to staging, confirm:
- Prisma migrations are applied, including the webhook migration.
WEBHOOK_SECRET_ENCRYPTION_KEYis set in the staging environment.WEBHOOK_RETRY_TOKENis set in the staging environment.CRON_SECRETis set in the staging or production environment if Vercel Cron is used.- The SaaS project on Vercel has Queues enabled.
- The queue consumer route is deployed with the
queue/v2betatrigger configuration. - A Vercel Cron job is configured for
/api/internal/webhooks/retry.
Current repository default:
- On Vercel Hobby, the repair cron runs once per day at
03:00UTC. - If faster recovery is needed, use manual calls to the repair endpoint or upgrade the Vercel plan.
Important:
- Vercel Cron Jobs run only on
Productiondeployments. - Preview deployments do not execute Vercel Cron Jobs automatically.
- If your staging environment is a Preview deployment, the repair path must be tested manually.
- For preview or non-production validation, you can still call
POST /api/internal/webhooks/retrymanually withAuthorization: Bearer <WEBHOOK_RETRY_TOKEN>.
2. Staging app setup
In the staging app:
- Open an organization where your test user is
owneroradmin. - Go to the Webhooks settings page.
- Create one endpoint for n8n or Make.
- Subscribe the endpoint to at least one event.
- Copy the secret shown after creation and store it outside the app. It is not recoverable later.
Recommended staging setup:
- One endpoint subscribed only to
client.created - One endpoint subscribed only to
process_assignment.accepted
This makes it easier to verify event routing and avoid ambiguity during testing.
3. n8n setup
For n8n:
- Create a
Webhooktrigger node. - Use
POST. - Copy the production or test webhook URL from n8n into the organization webhook endpoint.
- Add downstream logging or persistence so you can inspect headers and body.
Recommended validation in n8n:
- Store
headers["webhook-id"] - Store
headers["webhook-timestamp"] - Store
headers["webhook-signature"] - Store
body.id - Store
body.type
Important:
- n8n should deduplicate on
webhook-idorbody.id. - Queue delivery is at-least-once, so duplicate deliveries are possible.
4. Make setup
For Make:
- Create a
Custom webhook. - Copy the generated webhook URL into the organization webhook endpoint.
- Add modules that log the raw body and headers.
Recommended validation in Make:
- Persist the event id
- Persist the event type
- Persist the webhook signature headers
- Add a deduplication step keyed by event id
5. Functional test cases
Run these in staging.
A. Client created
Steps:
- Create a new client in the organization.
- Confirm a webhook delivery row appears in the app.
- Confirm the consumer receives one event with
type = "client.created". - Confirm the payload contains:
organization.idorganization.nameorganization.slugdata.client.iddata.client.namedata.client.emaildata.client.organizationId
Expected result:
- Delivery status becomes
succeeded. - Consumer receives the expected payload and headers.
B. Accepted submission
Steps:
- Create or reuse a process assignment for a client.
- Submit the public form.
- Accept the submission from the protected review flow.
- Confirm a webhook delivery row appears in the app.
- Confirm the consumer receives one event with
type = "process_assignment.accepted". - Confirm the payload contains:
data.clientdata.processdata.assignmentdata.submittedSubmissiondata.acceptedSubmission
Expected result:
- No webhook is sent on public submit alone.
- Webhook is sent only when the submission is accepted.
6. Signature checks
Every outbound webhook should include:
webhook-idwebhook-timestampwebhook-signature
The JSON body should contain:
idtypecreatedAtorganizationdata
Minimum verification:
webhook-idshould matchbody.id.body.typeshould match the subscribed event.webhook-signatureshould be present on every request.
7. Retry behavior
Validate both success and failure behavior.
Success path
- Configure the consumer to return
200. - Confirm the app marks the delivery as
succeeded.
Retryable failure path
- Temporarily configure the consumer to return
500. - Trigger an event.
- Confirm the app marks the delivery as
retrying. - Confirm
attemptCountincreases. - Confirm a later retry is attempted.
Terminal failure path
- Temporarily configure the consumer to return
400. - Trigger an event.
- Confirm the app marks the delivery as
failed. - Confirm no further retry is scheduled.
8. UI checks
Confirm the staging UI shows:
- endpoint list
- subscribed events
- active or paused state
secretPreview- recent deliveries
- last error for failed deliveries
- attempt count and timestamps
Also verify:
- non-admin users cannot manage webhook endpoints
- rotating a secret shows the new secret once
- the full secret is not shown again after refresh
9. Known operational constraints
- The queue worker is the primary execution path.
- The internal retry route is a repair path and should stay enabled.
- Vercel Cron secures its
GETcall withCRON_SECRET. - Delivery is at-least-once, not exactly-once.
- Consumers must be idempotent.
- Localhost webhook URLs are allowed only for local development, not for staging.
10. Go or no-go
Ready for broader testing if all are true:
- staging deploy succeeds
- migrations are applied
- queue consumer is active
- both events are delivered
500retries work400terminal failure works- secrets are shown only once
- ciphertext is never returned to the browser
- n8n or Make flow deduplicates by event id
Do not proceed if any of these fail:
- queue trigger is not active
- deliveries remain stuck in
pendingwith no queue activity - accepted submission fires on public submit instead of accept
- consumer cannot reliably distinguish duplicate events
