Webhook Scheduler
Log inStart free
DocsSchedule Webhooks API

API reference

Schedule a webhook with one API call

Create delayed HTTPS jobs from your backend, retry failed deliveries, and inspect every scheduled webhook from the dashboard.

Base URL

https://webhookscheduler.com

Authentication

Public API calls require an API key created from the dashboard. Send it as eitherAuthorization: Bearer YOUR_KEY or x-api-key: YOUR_KEY.

Headers
Authorization: Bearer wh_live_xxxxxxxxx
Content-Type: application/json

TypeScript SDK

The zero-dependency Node.js SDK exposes typed helpers for scheduling, inspection, listing, cancellation, and signature verification. Keep it server-side because it uses your API key.

Install
npm install @webhookscheduler/sdk
TypeScript
import { WebhookScheduler } from "@webhookscheduler/sdk";

const scheduler = new WebhookScheduler({
  apiKey: process.env.WEBHOOK_SCHEDULER_API_KEY!,
});

const job = await scheduler.schedule({
  url: "https://example.com/webhooks/trial-reminder",
  runAt: new Date(Date.now() + 86_400_000),
  body: { userId: "usr_4821" },
  idempotencyKey: "trial-reminder:usr_4821",
});

// If the user upgrades before delivery starts:
await scheduler.cancel(job.id);

Package: @webhookscheduler/sdk on npm.

Schedule a webhook

POST/api/v1/schedule

Creates a future delivery. The target must be HTTPS and public; private networks, localhost, metadata endpoints, and unsafe redirects are blocked.

Request body

urlstringrequired

Destination HTTPS URL.

methodstring

One of GET, POST, PUT, PATCH, DELETE. Default is POST.

bodyjson

JSON payload sent to the destination. Alias: payload.

headersobject

Custom user headers. Unsafe hop-by-hop or proxy headers are stripped before dispatch.

runAtISO date stringrequired

Future execution time.

idempotencyKeystring

Optional 8-80 character key scoped to the organization. Reusing it returns the original job, even when the new request body differs. The key remains reserved while that job is retained.

referencestring

Optional business identifier for reconciliation, for example customer:usr_4821. Exact matches can be queried later without reading payloads.

criticalitystring

STANDARD, IMPORTANT, or CRITICAL. Delivery alert thresholds use this value. Default is STANDARD.

cURL
curl https://webhookscheduler.com/api/v1/schedule \
  -H "Authorization: Bearer wh_live_xxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/webhook",
    "method": "POST",
    "body": { "event": "user.created" },
    "runAt": "{{ tomorrow_at_09_utc }}",
    "idempotencyKey": "user-created-123",
    "reference": "customer:usr_4821",
    "criticality": "IMPORTANT"
  }'

Response

201 Created
{
  "id": "job_...",
  "status": "PENDING",
  "scheduledFor": "{{ tomorrow_at_09_utc }}",
  "idempotencyKey": "user-created-123",
  "reference": "customer:usr_4821",
  "criticality": "IMPORTANT"
}

List jobs

GET/api/v1/jobs

Lists the workspace's scheduled jobs, most recent first. Filter by status to find pending work or failures.

Query parameters

pageinteger

Page number, starting at 1.

limitinteger

Results per page, between 1 and 100. Default is 20.

statusstring

Optional filter: PENDING, PROCESSING, SUCCESS, FAILED, RETRYING, or CANCELED.

referencestring

Exact business reference match. Use this to compare scheduled work with your own database.

statestring

open returns pending, processing, and retrying jobs. terminal returns successful, failed, and canceled jobs. A status filter takes precedence.

cURL
curl "https://webhookscheduler.com/api/v1/jobs?status=PENDING&limit=20" \
  -H "Authorization: Bearer wh_live_xxxxxxxxx"

Response

200 OK
{
  "jobs": [
    {
      "id": "job_...",
      "status": "PENDING",
      "url": "https://api.example.com/webhook",
      "method": "POST",
      "scheduledFor": "2026-07-15T10:00:00.000Z",
      "idempotencyKey": "user-created-123",
      "reference": "customer:usr_4821",
      "criticality": "IMPORTANT",
      "createdAt": "2026-07-02T08:41:00.000Z"
    }
  ],
  "totalCount": 1,
  "totalPages": 1,
  "page": 1
}

Get a job

GET/api/v1/jobs/{jobId}

Returns one job with its payload, headers, and the full delivery attempt history: HTTP status codes, response bodies, latency, and errors.

cURL
curl https://webhookscheduler.com/api/v1/jobs/job_xxx \
  -H "Authorization: Bearer wh_live_xxxxxxxxx"

Response

200 OK
{
  "id": "job_...",
  "status": "SUCCESS",
  "url": "https://api.example.com/webhook",
  "method": "POST",
  "payload": { "event": "user.created" },
  "headers": {},
  "scheduledFor": "2026-07-15T10:00:00.000Z",
  "idempotencyKey": "user-created-123",
  "reference": "customer:usr_4821",
  "criticality": "IMPORTANT",
  "createdAt": "2026-07-02T08:41:00.000Z",
  "updatedAt": "2026-07-15T10:00:02.000Z",
  "attempts": [
    {
      "id": "att_...",
      "statusCode": 200,
      "responseBody": "{\"ok\":true}",
      "error": null,
      "durationMs": 143,
      "createdAt": "2026-07-15T10:00:01.000Z"
    }
  ]
}

Cancel a job

POST/api/v1/jobs/{jobId}/cancel

Cancels a PENDING or RETRYING job before dispatch. Use this to drop reminders or follow-ups once the user has already acted. Jobs that have delivered cannot be canceled.

cURL
curl -X POST https://webhookscheduler.com/api/v1/jobs/job_xxx/cancel \
  -H "Authorization: Bearer wh_live_xxxxxxxxx"

Response

200 OK
{
  "id": "job_...",
  "status": "CANCELED",
  "url": "https://api.example.com/webhook",
  "scheduledFor": "2026-07-15T10:00:00.000Z"
}

Returns 400 if the job already reached a final state, 404 if the job does not belong to your workspace, and 409 if the job state changed while the cancel was being applied.

Cancellation can race with delivery

Cancellation is not a transaction with your application state. If dispatch has already started when the cancel request arrives, your receiver may still be called. Re-read the current business state and enforce idempotency in the receiving handler before applying side effects.

Recipe: a cancelable reminder

The core lifecycle pattern is two calls. Schedule the reminder when a trial or workflow starts, store the returned id on the record, then cancel it if the user acts first. Full walkthrough: cancelable trial reminders.

1. Schedule when the trial starts

cURL
curl https://webhookscheduler.com/api/v1/schedule \
  -H "Authorization: Bearer wh_live_xxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/hooks/trial-reminder",
    "runAt": "2026-07-12T09:00:00.000Z",
    "body": { "userId": "usr_4821" },
    "idempotencyKey": "trial-reminder-usr_4821"
  }'

2. Cancel if the user upgrades first

cURL
curl -X POST https://webhookscheduler.com/api/v1/jobs/job_xxx/cancel \
  -H "Authorization: Bearer wh_live_xxxxxxxxx"

The idempotencyKey keeps the schedule call safe to retry. Verify the delivery in your receiver with the Webhook-Signature header using the signature format documented below. The receiver must also re-check whether the reminder is still relevant because cancellation and dispatch can race.

Execute immediately

POST/api/v1/execute-now

Dispatches a webhook immediately using the same SSRF policy and header sanitization as scheduled jobs.

urlstringrequired

Destination HTTPS URL.

methodstring

One of GET, POST, PUT, PATCH, DELETE. Default is POST.

headersobject

Optional headers after sanitization.

bodyjson

Optional JSON request body.

cURL
curl https://webhookscheduler.com/api/v1/execute-now \
  -H "Authorization: Bearer wh_live_xxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/webhook",
    "body": { "event": "manual.test" }
  }'

Response

200 OK
{
  "success": true,
  "status": 200,
  "data": { "ok": true },
  "durationMs": 143
}

Delivery semantics

Webhook Scheduler provides at-least-once delivery. Duplicate delivery is uncommon but possible during infrastructure failures and recovery. Receivers must be idempotent.

Cancellation succeeds only while a job is PENDING or RETRYING. A concurrent transition can return 409; after processing starts, cancellation cannot retract an HTTP request that is already in flight.

An idempotencyKey is organization-scoped. A repeated schedule request returns the original job and does not compare payloads. The key becomes reusable only after the retained job is deleted.

Webhook signatures

New workspaces receive a signing secret automatically, and outgoing deliveries include a Webhook-Signature header. If an older workspace has no secret, generate one in Settings. Store the secret server-side and verify both the timestamp and HMAC before trusting the payload.

Header
Webhook-Signature: t=1780732800,v1=hex_hmac_sha256
Node.js verification
import crypto from "node:crypto";

const header = req.headers["webhook-signature"];
const parts = Object.fromEntries(
  header.split(",").map(part => part.split("="))
);

const timestamp = Number(parts.t);
if (
  !Number.isFinite(timestamp) ||
  Math.abs(Date.now() / 1000 - timestamp) > 300
) {
  throw new Error("Webhook timestamp is outside the 5-minute tolerance");
}

const payloadToSign = `${parts.t}.${rawBody}`;
const expectedHex = crypto
  .createHmac("sha256", process.env.WEBHOOK_SCHEDULER_SECRET)
  .update(payloadToSign)
  .digest("hex");

if (!/^[a-f0-9]{64}$/i.test(parts.v1)) {
  throw new Error("Invalid webhook signature");
}

const provided = Buffer.from(parts.v1, "hex");
const expected = Buffer.from(expectedHex, "hex");
if (
  provided.length !== expected.length ||
  !crypto.timingSafeEqual(provided, expected)
) {
  throw new Error("Invalid webhook signature");
}

Job statuses

PENDING

Scheduled and waiting for delivery.

PROCESSING

The delivery is currently being sent.

SUCCESS

Delivered successfully.

FAILED

Final failure after all retries or unrecoverable scheduling failure.

RETRYING

Failed once and scheduled for the next recovery attempt.

CANCELED

Canceled before delivery.

Errors

400

Validation failed or unsafe target URL. SSRF failures return code UNSAFE_TARGET_URL.

401

Missing or invalid API key.

403

Plan quota exceeded.

405

Unsupported HTTP method.

409

Job state changed before cancellation was applied.

429

Rate limit exceeded.

503

Delivery could not be queued. Retry with backoff.

500

Internal error. Retry later with backoff.

Security model

Outbound webhooks are HTTPS-only and revalidated immediately before dispatch.

Redirects are not followed automatically. A 3xx response from your endpoint is recorded as the webhook result.

DNS lookups are pinned to the validated public IP to reduce DNS rebinding risk.

New workspaces receive a signing secret automatically. Outbound deliveries include Webhook-Signature whenever that secret is configured.

Each scheduled job is claimed atomically before delivery to reduce duplicate sends. This is an at-least-once system, not an exactly-once guarantee.

For the complete target validation, retry, timeout, retention, and abuse-control model, read Security and reliability.