Skip to main content
Think of <Timer> as a durable sleep. When the scheduler reaches this node, the workflow suspends — no polling, no busy-waiting. The Temporal runtime checkpoints the delay and resumes execution once the wall-clock condition is satisfied, even if the worker restarts in the meantime.

Import

import { Timer } from "smithers-orchestrator";

Props

PropTypeDefaultDescription
idstring(required)Unique node id within the workflow.
durationstringundefinedRelative delay as a human-readable string (e.g. "500ms", "30s", "2h", "7d"). Exactly one of duration or until is required.
untilstring | DateundefinedAbsolute fire time as an ISO 8601 timestamp string or a Date object. Exactly one of duration or until is required.
skipIfbooleanfalseSkip this node entirely. The node resolves immediately with no delay.
dependsOnstring[]undefinedTask IDs that must complete before the timer starts.
needsRecord<string, string>undefinedNamed deps. Keys become context keys, values are task IDs.
labelstringtimer:<id>Display label override.
metaRecord<string, unknown>undefinedExtra metadata attached to the node record.
Exactly one of duration or until must be provided. Providing both, or neither, throws at render time.
The every prop (recurring timer) is reserved for a future phase and is not supported. Passing it throws at render time.

Duration strings

The duration prop accepts a concise human-readable format.
StringMeaning
"500ms"500 milliseconds
"1s" / "30s"1 second / 30 seconds
"5m" / "30m"5 minutes / 30 minutes
"1h" / "2h"1 hour / 2 hours
"1d" / "7d"1 day / 7 days

Relative delay

Pause for 30 seconds before proceeding to the next step:
import { Sequence, Task, Timer, Workflow, createSmithers } from "smithers-orchestrator";
import { z } from "zod";

const { smithers, outputs } = createSmithers({
  report: z.object({ summary: z.string() }),
});

export default smithers(() => (
  <Workflow name="delayed-report">
    <Sequence>
      <Timer id="cooldown" duration="30s" />
      <Task id="report" output={outputs.report} agent={reportAgent}>
        Generate the daily summary report.
      </Task>
    </Sequence>
  </Workflow>
));

Absolute timestamp

Use until when the target time is computed at runtime — for example, a deadline stored in the workflow input:
export default smithers((ctx) => (
  <Workflow name="scheduled-reminder">
    <Sequence>
      <Timer id="wait-until-deadline" until={ctx.input.reminderAt} />
      <Task id="send-reminder" output={outputs.notification} agent={notifierAgent}>
        Send the reminder to the user.
      </Task>
    </Sequence>
  </Workflow>
));
ctx.input.reminderAt can be an ISO string ("2026-06-01T09:00:00Z") or a Date object — both are accepted. If the timestamp is already in the past when the node is reached, the timer fires immediately.

Timer inside a loop

Derive a duration from context at render time to implement a simple backoff:
export default smithers((ctx) => {
  const delay = ctx.iteration === 0 ? "5m" : "30m";

  return (
    <Workflow name="retry-with-backoff">
      <Loop
        until={ctx.outputMaybe(outputs.result)?.success === true}
        maxIterations={5}
      >
        <Sequence>
          <Timer id="backoff" duration={delay} />
          <Task id="attempt" output={outputs.result} agent={workerAgent}>
            Attempt the operation.
          </Task>
        </Sequence>
      </Loop>
    </Workflow>
  );
});

Behavior

  • When the scheduler reaches a <Timer> node, it enters waiting-timer status.
  • The engine records the timer target — a resolved UTC timestamp — durably in the workflow history.
  • The worker thread releases the execution slot. No resources are held during the wait.
  • When the target time arrives, Temporal wakes the workflow and the node transitions to completed. Downstream nodes are then eligible to run.
  • If skipIf is true, the node resolves immediately without any delay.
  • Worker restarts or redeployments during the wait do not reset the timer — the checkpoint is stored in the Temporal event history.
  • Timers in separate parallel branches wait independently.

Rendering

<Timer> renders as a smithers:timer host element. The scheduler treats it as a leaf node that blocks the sequence until the timer fires.

Notes

  • <Timer> produces no output. It has no output prop. It is a pure synchronization point.
  • Use dependsOn or needs when the timer should start only after specific upstream tasks, rather than relying on sequence position alone.
  • For event-driven delays (wait for an external signal rather than a fixed time), use <WaitForEvent> instead.
  • Timers inside a <Loop> body reset each iteration because each iteration is a fresh render of the tree.