Skip to content
All insights
AllClearStack editorial·Reliability··3 min read

Choose idempotency keys that survive retries without hiding new work

Separate a retry of the same scheduling request from a genuinely new business event, and give each a stable identity.

A timeout creates uncertainty. The scheduler may have accepted your request even though your application never received the response. An idempotency key helps you retry that same intention without deliberately creating another scheduled job.

Identify the business event

Start with the thing that should happen once from the product's point of view. A customer ID alone may be too broad: the same customer can have several invoices, trials or reminders. Build the key from the event identity and the action or version that distinguishes this work.

Keep the key stable across retries of that intention. A timestamp or random value created inside each retry defeats this purpose. Store the chosen key with the business record so another process can recover it after a crash. Follow the current API's length and character constraints instead of assuming arbitrary identifiers will be accepted.

Understand what the scheduler remembers

WebhookScheduler's documented scheduling key is organization-scoped. Reusing a key returns the retained original job and does not compare the new request's payload. The key becomes reusable after the retained job is deleted; it is not a permanent uniqueness constraint for your business data.

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: "event:example-123",
    idempotencyKey: "event-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);

The illustrative key includes an event, reminder and version. Replace those placeholders with your own stable identity. If the response disappears, retry with that same key and persist the returned id. Changing the payload while reusing the key does not edit the retained job.

Make replacements explicit

When the underlying intention changes, decide whether the old job should be canceled and a new one scheduled. Use a new versioned key for genuinely new work and retain the relationship between the old and new records. Cancellation can race with delivery, so a receiver must still reject obsolete versions using current application state.

For example, moving an appointment may invalidate an earlier reminder. The application should record the new appointment version before treating a replacement reminder as current. At delivery time, comparing the payload's stable reminder identity with that current version can reveal obsolete work.

Deduplicate the effect separately

Scheduling idempotency and receiver idempotency are different responsibilities. The former limits duplicate job creation for a retained key. It does not make an HTTP receiver or an external notification service exactly-once.

Claim business effects with a unique event identifier or suitable transaction. When a downstream provider supports idempotency, use its mechanism as well. Define what happens if the provider accepts a request but the response times out before your local state is updated. A plain “already sent” check followed by a separate send is not an atomic operation.

Review the failure cases before launch

WebhookScheduler is built by AllClearStack. Its scheduling API is useful when your missing component is delayed HTTP work. The application still owns durable business identity, replacement policy and reconciliation.

Test a lost schedule response, two concurrent requests for the same event, a changed payload with the old key, and an obsolete delivery after a replacement. A database worker may be sufficient when it already provides these operations within infrastructure your team maintains. Use the retry guide to examine timing separately from the identity of the work.

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.

Related articles