Skip to main content

Actions & Scheduling

Everything in CallMeLater is an action -- something scheduled to happen in the future. Actions operate in one of two modes: webhook or approval.

Webhook mode

Webhook mode is the default. A webhook action delivers an HTTP request to your endpoint at a scheduled time.

{
"schedule": { "preset": "tomorrow" },
"request": {
"method": "POST",
"url": "https://api.example.com/webhook",
"headers": { "X-Custom-Header": "value" },
"body": { "event": "trial_expired", "user_id": 42 }
}
}

Use webhook mode for trial expirations, delayed notifications, scheduled API calls, cleanup tasks, and follow-up triggers.

Approval mode

An approval action sends an interactive message to one or more recipients and collects their response (confirm, decline, or snooze).

{
"mode": "approval",
"schedule": { "wait": "2h" },
"gate": {
"message": "Please confirm the deployment",
"recipients": ["ops@example.com", "+1234567890"],
"confirmation_mode": "first_response"
},
"callback_url": "https://api.example.com/webhooks/response"
}
info

Approvals do not automatically execute anything. When someone responds, CallMeLater sends the response to your callback_url. Your system decides what to do next.

Use approval mode for approval workflows, human confirmations, check-ins, and escalation chains.

Scheduling

Every action needs a schedule that defines when it should fire. There are three ways to set it.

Presets

Named time references, resolved relative to now (or to a timezone if provided):

PresetWhen
tomorrowTomorrow at the current time
next_mondayNext Monday at the current time
next_tuesdayNext Tuesday at the current time
next_wednesdayNext Wednesday at the current time
next_thursdayNext Thursday at the current time
next_fridayNext Friday at the current time
next_saturdayNext Saturday at the current time
next_sundayNext Sunday at the current time
next_weekNext Monday at the current time
1h, 2h, 4h1, 2, or 4 hours from now
1d, 3d1 or 3 days from now
1w1 week from now
{
"schedule": {
"preset": "next_monday",
"timezone": "America/New_York"
}
}

Relative wait

A duration from now using schedule.wait:

{ "schedule": { "wait": "30m" } }
FormatMeaningExample
NmN minutes5m = 5 minutes
NhN hours2h = 2 hours
NdN days1d = 1 day
NwN weeks1w = 1 week

Exact time

An ISO 8601 UTC timestamp using scheduled_for:

{
"scheduled_for": "2026-04-01T14:30:00Z"
}

Timezone

Optional. Defaults to UTC. Provide a timezone when using presets so that times like tomorrow and next_monday resolve to the correct local time.

{
"schedule": {
"preset": "tomorrow",
"timezone": "Europe/Paris"
}
}

Lifecycle states

Actions move through a series of states from creation to completion.

Webhook mode:

scheduled → resolved → executing → executed
↘ failed

Approval mode:

scheduled → resolved → executing → awaiting_response → executed
↘ failed
↘ expired

Any non-terminal action can also be cancelled.

StateDescriptionNext states
scheduledSchedule is being resolved to an exact timestampresolved, cancelled
resolvedWaiting for the scheduled time to arriveexecuting, cancelled
executingHTTP request or reminder is being deliveredexecuted, failed, awaiting_response, resolved (retry)
awaiting_responseReminder sent, waiting for a human replyexecuted, failed, expired, cancelled, scheduled (snooze)
executedCompleted successfully (terminal)--
failedFailed permanently (terminal)resolved (manual retry)
expiredApproval timed out without response (terminal)--
cancelledCancelled before completion (terminal)--

Idempotency keys

Use idempotency_key to prevent duplicate actions when your system retries requests or a user double-clicks.

{
"idempotency_key": "trial-end-user-42",
"schedule": { "wait": "14d" },
"request": { "url": "https://api.example.com/expire" }
}

Key format patterns:

trial:user:123
deploy:v2.1
invoice:1234:reminder
weekly-report:2026-W07

Keys are scoped per account, up to 255 characters, and case-sensitive.

Behavior when a matching key already exists:

Existing action stateBehavior
scheduledReturns existing action
resolvedReturns existing action
awaiting_responseReturns existing action
executedCreates new action
failedCreates new action
cancelledCreates new action

Non-terminal states return the existing action to prevent duplicates. Terminal states allow key reuse since the original action is already complete.

Dedup keys

Dedup keys group related actions together and control how they interact. Unlike idempotency keys (which prevent duplicates of the same request), dedup keys coordinate behavior across different actions that share a logical group.

{
"dedup_keys": ["deploy:api-service"],
"coordination": {
"on_create": "cancel_and_replace"
},
"schedule": { "wait": "1h" },
"request": { "url": "https://ci.example.com/deploy" }
}

on_create behaviors

Controls what happens when you create a new action with the same dedup key as an existing non-terminal action.

BehaviorDescription
skip_if_existsReturn the existing action instead of creating a new one (response includes meta.skipped: true)
cancel_and_replaceCancel all existing non-terminal actions with matching keys, then create the new action

on_execute behaviors

Controls what happens at execution time based on other actions sharing the same dedup key. This is a nested object with condition-based logic:

{
"dedup_keys": ["deploy:api-service"],
"coordination": {
"on_execute": {
"condition": "skip_if_previous_pending",
"on_condition_not_met": "cancel",
"reschedule_delay": 300,
"max_reschedules": 10
}
}
}
FieldDescription
conditionskip_if_previous_pending, execute_if_previous_failed, execute_if_previous_succeeded, or wait_for_previous
on_condition_not_metcancel, reschedule, or fail
reschedule_delaySeconds to wait before retrying when rescheduled (used with reschedule)
max_reschedulesMaximum number of reschedule attempts (used with reschedule)

Format rules

  • Alphanumeric characters plus _, :, ., -
  • No spaces, slashes, or special characters
  • Case-sensitive (Deploy:API and deploy:api are different keys)
  • Maximum 10 keys per action
  • Scoped to your account

Valid: deploy:production, user:42:notifications, workflow.onboarding.step-1

Invalid: key with spaces, key/with/slashes, key@email.com