Schedule API Calls and Webhooks for Later
Schedule HTTP requests and webhooks for any future time. One API call to schedule, managed delivery with retries and observability. Works with any language — your endpoint handles the logic.
Last updated: March 24, 2026
You need to make an HTTP request at a specific future time. A reminder webhook in 24 hours. A payment retry next Tuesday at 10am. An invitation expiry check in 14 days. A status poll 5 minutes after kicking off an async process.
Posthook does exactly this. One API call to schedule a future HTTP POST or WebSocket delivery to your endpoint — specify when it should fire and what payload to include. Posthook persists the schedule, handles the timing, retries on failure, and gives you visibility into whether it was delivered. Your handler receives the delivery and runs whatever logic the moment requires — calling an external API, sending an email, updating a database. No infrastructure to set up, no workers to run, no cron to maintain.
When you need this
These are the most common reasons developers look for a way to schedule an API call:
- Reminders and follow-ups — send a trial expiry reminder 3 days before the deadline, nudge an inactive user after 48 hours, trigger a follow-up email if a form was abandoned
- Expirations and timeouts — expire a pending invitation in 7 days, release a payment hold after 3 business days, revoke a magic link after 15 minutes
- Payment workflows — retry a failed charge in 24 hours, send an invoice on the billing date, trigger a dunning sequence after a missed payment
- External integrations — trigger a handler at a scheduled time that calls a partner API, syncs data to a third-party system, or processes a settlement
- Status checks — poll an async process for completion after a delay, check whether a deployment succeeded 5 minutes after launch
- Cleanup and maintenance — purge temporary files after a TTL, archive stale records after a grace period
Each of these is a per-event timer — not a recurring cron cadence. The scheduling is dynamic, triggered by something that just happened in your application.
How developers typically solve this
Cron and database polling
Store tasks in a database with a run_at timestamp. A cron job polls every minute for due rows, processes them, and marks them complete.
This works at low volume. It breaks as load grows: the polling query scans every pending row on every tick, timing precision is limited to the cron interval (up to 59 seconds late), multiple instances processing the same batch create race conditions without careful locking, and failures are silent unless you build monitoring from scratch. At tens of thousands of pending records, this becomes a performance and coordination problem.
Cron is a good fit for simple, low-volume recurring tasks where the team already has database infrastructure and ops capacity. It becomes coordination work when every event creates its own timer.
Cloud provider schedulers
AWS EventBridge Scheduler is the most capable — it supports one-time and recurring schedules, configurable retries, and scales to millions of schedules. Google Cloud Scheduler handles recurring HTTP targets with cron expressions. Google Cloud Tasks supports delayed HTTP execution with a 30-day maximum delay.
These are powerful tools, but they come with platform lock-in and infrastructure complexity. EventBridge requires IAM roles, target configurations, and AWS-specific knowledge. Cloud Scheduler and Cloud Tasks are GCP-only. None offer per-schedule observability with attempt history, anomaly detection, or bulk incident response across schedules.
Cloud schedulers are a good fit for teams already deep in a provider’s ecosystem who need to target cloud-native services like Lambda or Cloud Functions.
Queue systems with delay
SQS supports a maximum 15-minute delay — not viable for most scheduling use cases. BullMQ (Redis-backed, Node.js) and Celery (Python) support arbitrary delays, but require Redis or RabbitMQ infrastructure. Memory usage scales linearly with the number of pending jobs. Timing precision depends on polling intervals and worker availability.
Queue systems are a good fit when the team is already running queue infrastructure for job processing and adds scheduling as a secondary concern. They are overhead for scheduling-only needs.
Workflow engines
Inngest, Trigger.dev, and Temporal run your code in their execution runtime. Scheduling is one feature within a broader platform for multi-step workflows, branching logic, and durable execution.
If the scheduled action is one step in a complex workflow, a workflow engine is the right tool. If you need to call a URL at a specific time, a full execution platform adds complexity that the problem does not require.
How Posthook works
The model is straightforward:
- Schedule — make an API call with the target path, payload, and when to deliver
- Persist — Posthook stores the schedule durably in PostgreSQL with synchronous replication
- Deliver — at the scheduled time, Posthook sends an HTTP POST (or WebSocket message) to your endpoint
- Retry — if delivery fails, Posthook retries with configurable backoff
- Track — every delivery attempt is logged with status, response, and timing
Three scheduling modes
Posthook supports three ways to express “when”:
import Posthook from "@posthook/node";
const posthook = new Posthook("phk_...");
// Exact UTC time
await posthook.hooks.schedule({
path: "/hooks/payment-retry",
postAt: "2026-04-01T14:30:00Z",
data: { paymentId: "pay_abc" },
});
// Relative delay from now
await posthook.hooks.schedule({
path: "/hooks/send-reminder",
postIn: "24h",
data: { userId: "user_123" },
});
// Local timezone with DST handling
await posthook.hooks.schedule({
path: "/hooks/trial-expiry",
postAtLocal: "2026-04-01T09:00:00",
timezone: "America/New_York",
data: { trialId: "trial_xyz" },
});
SDKs are available for Node.js, Python, and Go. The underlying API is plain HTTP and JSON — any language that can make an HTTP request can schedule a hook without an SDK.
The handler checks state and decides
When the hook fires, your endpoint receives the delivery as an HTTP POST. The handler checks the current state of the resource and decides what to do — act or skip:
export async function POST(req: Request) {
const { data } = await req.json();
const payment = await db.payments.findById(data.paymentId);
// Already succeeded or cancelled — nothing to do
if (!payment || payment.status !== "failed") {
return Response.json({ skipped: true });
}
await retryPaymentCharge(payment);
return Response.json({ retried: true });
}
This pattern makes the handler safe to run at any time. If the payment was already retried by another path, the handler skips. If the hook fires twice due to a retry, the second run is a no-op. The handler is the correctness mechanism — not cancellation, not exactly-once delivery.
What you get beyond basic scheduling
A raw HTTP scheduler fires a request at a time. Posthook adds the operational layer that matters when scheduling is part of your production system:
- Per-delivery observability — inspect every hook’s delivery status, attempt history, response body, and timing from the dashboard or API. No log digging required.
- Anomaly detection — Posthook tracks failure rates per endpoint against historical baselines and alerts via email, Slack, or webhook when something deviates. You find out about endpoint problems before users do.
- Bulk incident response — if a downstream outage caused a batch of deliveries to fail, retry all failed hooks in a time range with one API call. Filter by endpoint, time range, or hook IDs.
- Per-hook retry overrides — critical deliveries (payment retries, time-sensitive expirations) can get a more aggressive retry strategy without changing project defaults. Set
retryOverridewith custom backoff, jitter, and attempt limits at scheduling time. - Timezone-aware scheduling —
postAtLocal+ timezone schedules in the user’s local time with automatic DST handling. A hook scheduled for 9am fires at 9am year-round, even through spring-forward and fall-back transitions. - WebSocket delivery — for endpoints without public URLs (local development, internal services, private networks), Posthook delivers via WebSocket. No tunnels or port forwarding required. Use the CLI for interactive local testing.
- Sequences — recurring workflows with calendar-based scheduling, dependency graphs between steps, and config-as-code via
posthook.toml. Replaces cron for recurring delivery patterns.
Choosing the right approach
| Approach | Best for | Not ideal when |
|---|---|---|
| Posthook | Dynamic, per-event scheduling with delivery tracking and failure alerting | High-throughput queue workloads where volume economics dominate |
| EventBridge Scheduler | AWS-native targets (Lambda, SQS), very high volume within AWS | Multi-cloud setups, teams without AWS expertise, per-schedule observability |
| QStash | Serverless HTTP messaging within the Upstash ecosystem | Anomaly detection, observability depth, timezone-aware scheduling with DST |
| Cloud Tasks | Delayed HTTP execution within GCP, queue-based work | Scheduling beyond 30 days, platform independence |
| Cron + database | Low-volume recurring tasks, full infrastructure control | Per-event scheduling at scale, reliability requirements, delivery visibility |
| BullMQ / Celery | Teams already running Redis or RabbitMQ for job processing | Scheduling-only needs without existing queue infrastructure |
| Workflow engine | Complex multi-step orchestration with branching and compensation | Simple timed HTTP delivery where a full execution platform adds unnecessary complexity |
For deeper comparisons, see QStash vs Posthook, EventBridge vs Posthook, and cron vs durable scheduling.
Getting started
Install the SDK and schedule your first hook:
npm install @posthook/node
import Posthook from "@posthook/node";
const posthook = new Posthook("phk_...");
// Schedule a reminder 24 hours from now
const hook = await posthook.hooks.schedule({
path: "/hooks/follow-up-reminder",
postIn: "24h",
data: { userId: "user_123", action: "trial_follow_up" },
});
console.log(hook.id); // Hook ID for tracking
console.log(hook.postAt); // Resolved delivery time (UTC)
SDKs are available for Node.js, Python, and Go. To see scheduling and delivery working end-to-end, try the live Next.js demo or clone the starter repo. See the quickstart guide for full setup instructions including endpoint configuration and signature verification.
Frequently asked questions
Ready to get started?
Create your free account and start scheduling hooks in minutes. No credit card required.