Canceling a job does not replace checking business state
Treat cancellation and receiver eligibility as separate controls, especially when product state changes near delivery time.
A customer can upgrade, an appointment can move and a workspace can be deleted while a reminder is waiting. Canceling the scheduled job is useful cleanup. It is not a transaction that also freezes your application's state and every downstream side effect.
Identify the two decisions
The scheduler decides whether a queued request can still be prevented from dispatching. The receiver decides whether the requested business action is appropriate now. Give both decisions an explicit place in the workflow.
Store the scheduled job's returned ID on your application's event record. When the event becomes obsolete, update that local state and request cancellation. A failed cleanup request should remain visible for retry or investigation; it should not silently reverse the product action that made the reminder obsolete.
Handle the result without guessing
The example creates a job and defines a function to cancel it later using the stored ID. The function throws when cancellation is not confirmed. Your application must decide how to inspect or reconcile that outcome.
Illustrative server-side example: this schedules delivery 24 hours from execution. Replace the environment variables and example record IDs, persist the returned job ID, and implement your own authenticated HTTPS receiver. The request follows the current API reference.
const apiKey = process.env.WEBHOOK_SCHEDULER_API_KEY;
const receiverUrl = process.env.REMINDER_WEBHOOK_URL;
if (!apiKey || !receiverUrl) throw new Error("Set the server-side API key and public HTTPS receiver URL.");
const response = await fetch("https://webhookscheduler.com/api/v1/schedule", {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({
url: receiverUrl,
method: "POST",
runAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
reference: "operation:example-123",
idempotencyKey: "operation-example-123-reminder-v1",
body: { resourceId: "example-123" },
}),
});
if (!response.ok) throw new Error(`Scheduling failed: ${response.status}`);
const job = await response.json();
if (!job.id) throw new Error("Missing scheduled job ID");
// Persist job.id with your business record before acknowledging this work.
console.log(job.id);
// Call with the stored ID when the reminder becomes obsolete.
async function cancelReminder(jobId) {
const result = await fetch(
`https://webhookscheduler.com/api/v1/jobs/${encodeURIComponent(jobId)}/cancel`,
{ method: "POST", headers: { Authorization: `Bearer ${apiKey}` } },
);
if (!result.ok) throw new Error(`Cancellation not confirmed: ${result.status}`);
}
Cancellation succeeds only while a job is PENDING or RETRYING. The API documents 400 for a job that is no longer cancelable, 404 for a job not found, and 409 when the state changes before cancellation is applied. These responses do not prove a business action succeeded. Inspect the job and keep the current-state check at the receiver: an HTTP request already in flight cannot be retracted.
Avoid treating every non-success response as either “nothing happened” or “the user was notified.” Those are business conclusions that a scheduling error cannot establish. Keep the job identifier and current application state available when investigating.
Re-evaluate eligibility at the receiver
Authenticate the incoming request before trusting its contents. Load the current business record and compare its version or state with the scheduled intention. If the record is missing or the action is obsolete, take an explicit skip path.
This final check narrows the window for stale work, but another state change can still happen immediately afterward. Decide which behavior your product can tolerate. If stronger coordination is required, design it within the system that owns the state rather than expecting cancellation of an external HTTP job to supply it.
Protect the business effect from duplicates
At-least-once delivery requires the receiver to handle repeated requests. Use a stable business event key and a durable claim, such as a unique constraint or transaction. Apply downstream provider idempotency when the provider supports it.
An external email or payment call cannot generally be rolled back by undoing a local database transaction. Document how you will investigate a timeout after the provider may have accepted the request. A scheduling cancellation is not a substitute for that recovery policy, and a plain check-then-send flag does not make the whole operation exactly-once.
Keep the operational boundary clear
WebhookScheduler is built by AllClearStack. It is appropriate when you want a service to handle delayed HTTP delivery. The tradeoff is an external dependency while business-state checks, receiver security and reconciliation remain yours.
An existing database worker may be a simpler choice if you already run one and its timing is sufficient. Whichever transport you choose, test a cancellation before dispatch, a cancellation racing with dispatch and an obsolete event received afterward. The webhook scheduling collection covers related design choices without turning the scheduler into the owner of your product's rules.
Disclosure · Built by the AllClearStack team
When ownership is the expensive part
Webhook Scheduler handles delayed HTTP delivery, automatic retries, per-attempt logs, and one-call cancellation. Try a real delivery without creating an account.