Schedule webhooks that fire at the right time — no cron, no queues, no workflow engines. Built with Posthook for durable per-event scheduling.
- Create a task — schedules a reminder webhook via Posthook
- Countdown ticks — the dashboard shows when the hook will fire
- Hook fires — Posthook delivers the webhook, task status changes to "reminded"
- Mark done early — the pending hook no-ops on delivery (state verification pattern)
In a real app, the webhook handler would also send a notification, trigger a workflow, or call an external API.
- Click the Deploy with Vercel button above (provisions a Neon Postgres database)
- Add your
POSTHOOK_API_KEYandPOSTHOOK_SIGNING_KEYfrom posthook.io - Set your deployment domain in your Posthook project settings (e.g.,
my-app.vercel.app) - Set up the cleanup sequence:
npx posthook init # connects your project, keeps the sequence config npx posthook apply # deploys the sequence to Posthook
git clone https://github.com/posthook/nextjs-posthook-scheduling.git
cd nextjs-posthook-scheduling
npm install
docker compose up -d db
cp .env.example .env.local # add your Posthook API key + signing key
npm run db:push
npm run devIn another terminal, forward Posthook deliveries to your local server:
npx posthook listen --forward http://localhost:3000Open http://localhost:3000, create a task with a 30s delay, and watch it fire.
See PATTERNS.md for detailed explanations and copy-pasteable code:
- Schedule hooks before committing state — if scheduling fails, no broken task
- State verification on delivery — mark done before the hook fires, it no-ops
- Handler as extension point — add your notification/email/API call logic
This starter includes a daily cleanup sequence defined in posthook.toml using Posthook Sequences. It deletes resolved tasks older than 24 hours. See the Deploy to Production section for setup.
app/
page.tsx # Dashboard UI
create-form.tsx # Task creation form
mark-done-button.tsx # Complete task action
api/
tasks/route.ts # POST (create), GET (list)
tasks/[id]/route.ts # GET (detail), PATCH (complete)
webhooks/
remind/route.ts # Posthook reminder callback
cleanup/route.ts # Posthook sequence: daily cleanup
lib/
posthook.ts # Posthook client (lazy singleton)
tasks.ts # Task logic + Posthook scheduling
store.ts # Data access layer (Drizzle)
types.ts # TypeScript types
db/schema.ts # Drizzle schema
db/index.ts # Database connection
posthook.toml # Sequences config (config-as-code)
- Change
REMINDER_DELAYenv var to set the default delay (e.g.,30s,5m,1h,1d) - Add new webhook handlers for different hook types
- Add notification logic (email, Slack, push) inside the remind handler
- See PATTERNS.md for advanced patterns
- For production, replace the
postbuildscript with a proper migration workflow (drizzle-kit pushcan drop columns)
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Postgres connection string |
POSTHOOK_API_KEY |
Yes | Posthook API key |
POSTHOOK_SIGNING_KEY |
Yes | Posthook signing key |
REMINDER_DELAY |
No | Default reminder delay (default: 1h) |
- Next.js 16 (App Router)
- Posthook for durable per-event scheduling
- Drizzle ORM + PostgreSQL
- Tailwind CSS
MIT
