Skip to main content

Delayed Start Pattern

Overview

The Delayed Start pattern enables Workflows to be created immediately but begin execution after a specified delay. The Workflow execution is registered in Temporal right away, but the first Workflow Task is scheduled to run only after the delay period expires, making it suitable for scheduled operations, grace periods, and deferred processing.

Problem

In business processes, you often need Workflows that start execution at a future time, are created immediately for tracking but execute later, avoid external scheduling systems or cron jobs for one-time delays, and maintain Workflow identity and queryability before execution begins.

Without delayed start, you must use external schedulers to trigger Workflow creation later, start Workflows immediately and sleep as the first operation (which wastes resources), implement complex queueing systems for deferred execution, or use Temporal Schedules for one-time delays (which is more than you need).

Solution

The Delayed Start uses a start delay option in WorkflowOptions to defer the first Workflow Task. The Workflow execution is created immediately with a firstWorkflowTaskBackoff set to the delay duration, but no Workflow code runs until the delay expires.

The following describes each step in the diagram:

  1. The client starts the Workflow with a 30-second delay. Temporal creates the execution immediately.
  2. The execution is visible and queryable, but no Workflow code runs during the delay.
  3. If the client sends a Signal-With-Start or Update-With-Start during the delay, the remaining delay is bypassed and a Workflow Task is dispatched immediately. Regular Signals do not interrupt the delay.
  4. After the delay expires, Temporal schedules the first Workflow Task and the Workflow begins execution.

The following example creates a Workflow with a 30-second start delay:

# client.py
from datetime import timedelta

handle = await client.start_workflow(
DelayedStartWorkflow.run,
id=WORKFLOW_ID,
task_queue=TASK_QUEUE,
start_delay=timedelta(seconds=30),
)
# Created now, executes in 30 seconds

The start delay option sets the firstWorkflowTaskBackoff on the execution. The Workflow is created and visible in the UI immediately, but the Worker does not receive a Task until the delay expires.

Implementation

Basic delayed notification

The following implementation sends a notification after a one-hour delay. The Workflow code runs only after the delay expires:

# workflows.py
from temporalio import workflow

@workflow.defn
class NotificationWorkflow:
@workflow.run
async def run(self, message: str) -> None:
workflow.logger.info(f"Sending notification: {message}")

# client.py
from datetime import timedelta

handle = await client.start_workflow(
NotificationWorkflow.run,
"Your trial expires soon",
task_queue=TASK_QUEUE,
start_delay=timedelta(hours=1),
)

The Workflow is created immediately, but the notification logic does not execute until one hour later.

Cancellable delayed execution

The following implementation adds Signal handlers for cancellation and a Query for status. You can cancel the Workflow before it runs or check its status during the delay:

# workflows.py
from temporalio import workflow

@workflow.defn
class DelayedOrderWorkflow:
def __init__(self) -> None:
self._cancelled = False
self._status = "SCHEDULED"

@workflow.run
async def run(self, order_id: str) -> None:
if self._cancelled:
self._status = "CANCELLED"
return

self._status = "PROCESSING"
# Process order logic
self._status = "COMPLETED"

@workflow.signal
async def cancel(self) -> None:
self._cancelled = True

@workflow.query
def get_status(self) -> str:
return self._status

The cancel Signal handler sets a flag that the Workflow checks when it starts executing. Note that Signal handlers and Query handlers only run after the delay expires and the first Workflow Task is dispatched. To cancel before execution, use Signal-With-Start to bypass the delay, or cancel the Workflow Execution directly.

When to use

The Delayed Start pattern is a good fit for scheduled one-time operations (send a reminder in 24 hours), grace periods before processing (cancel a subscription in 7 days), delayed notifications and alerts, deferred batch processing, and trial expiration Workflows.

It is not a good fit for recurring Schedules (use Temporal Schedules), immediate execution with internal delays (use Workflow sleep — Workflow.sleep() in Java, wf.sleep() in TypeScript, workflow.sleep() in Python, workflow.Sleep() in Go), complex scheduling logic (use Schedules with cron), or sub-second delays (minimal benefit).

Benefits and trade-offs

The Workflow is queryable before execution starts (immediate visibility). No Worker resources are consumed during the delay. You can cancel the Workflow Execution before it runs. A Signal-With-Start or Update-With-Start bypasses the remaining delay. Regular Signals sent during the delay do not interrupt it. The API is a single configuration option with no external schedulers needed. The delay is managed by Temporal, ensuring deterministic behavior.

The trade-offs to consider are that you cannot dynamically adjust the delay after creation (use the Updatable Timer pattern for that). The pattern is for one-time delays only — for recurring Schedules, use Temporal Schedules. Very short delays (milliseconds) provide minimal benefit — the minimum timer duration is 1 second. The delay is time-based only, not condition-based. Regular Signals sent during the delay are not delivered until the first Workflow Task fires, so Query and Signal handlers are not available until execution begins.

Comparison with alternatives

ApproachImmediate visibilityResource usageCancellableUse case
Delayed StartYesNone during delayYesOne-time future execution
Workflow sleepYesWorker resourcesYesInternal delays
Temporal SchedulesYesNoneYesRecurring Schedules
External SchedulerNoExternal systemDependsComplex scheduling

Best practices

  • Use for one-time delays. For recurring Schedules, use Temporal Schedules instead.
  • Set Workflow ID. Always set an explicit Workflow ID for tracking and cancellation.
  • Add Query methods. Expose status via Queries to check state during the delay.
  • Enable cancellation. Add Signal handlers to cancel before execution.
  • Validate delay duration. Ensure the delay is reasonable (not too short or too long).
  • Monitor backoff. Check firstWorkflowTaskBackoff in history for verification.
  • Consider time zones. Use absolute timestamps if the delay depends on a specific time.
  • Document behavior. Clearly indicate that the Workflow does not execute immediately.

Common pitfalls

  • Using Signals during the delay. Regular Signals do not interrupt the Start Delay. Only Signal-With-Start or Update-With-Start bypass the delay. Signals sent to a delayed Workflow are buffered but the Workflow code has not started, so there is no handler to process them until the delay expires.
  • Querying before the Workflow starts. Queries have no state to return during the delay because no Workflow code has executed yet. Clients may receive errors or empty results.
  • Setting delays shorter than 1 second. The minimum timer resolution is 1 second. Sub-second delays are not supported.
  • Forgetting that the Workflow ID is reserved. A delayed Workflow reserves its Workflow ID immediately. Starting another Workflow with the same ID will fail depending on the ID reuse policy.
  • Temporal Schedules: For recurring Workflow execution.
  • Updatable Timer: For dynamically adjustable delays within Workflows.
  • Signal with Start: Interacting with Workflows before execution.

Sample code