--- title: 'A scheduled digest with a long wait' description: 'A digest workflow that starts itself every morning from a cron schedule and paces its sends with a durable sleep.' position: 3 --- # A scheduled digest with a long wait > This workflow runs every morning from its declarative `schedules` export, so > nothing outside it has to start a run. It also shows `sleep`, which parks the > run without holding a process open. The run stays parked until the timer > fires, then resumes from where it slept. The whole workflow is a single `workflows/daily-digest.workflow.ts` file. It needs no trigger of its own: the `schedules` export is the trigger. Stackbone reads every cron pattern in **UTC**, so `0 8 * * *` fires at 08:00 UTC, not at 08:00 local time. ```ts // workflows/daily-digest.workflow.ts import { z } from '@stackbone/sdk'; import { sleep } from '@stackbone/sdk/workflow'; export const schedules = [{ cron: '0 8 * * *', input: { scope: 'daily' } }]; export const inputSchema = z.object({ scope: z.enum(['daily', 'weekly']) }); export const outputSchema = z.object({ sent: z.number() }); export async function dailyDigestWorkflow(input: z.infer) { 'use workflow'; const recipients = await collectRecipients(input.scope); let sent = 0; for (const to of recipients) { await deliver(to, input.scope); sent += 1; await sleep('5s'); // gentle pacing between sends, durable across a restart } return { sent }; } async function collectRecipients(scope: string) { 'use step'; return scope === 'weekly' ? ['team@acme.com'] : ['sam@acme.com', 'lee@acme.com']; } async function deliver(to: string, scope: string) { 'use step'; // idempotent on (to, scope, day) return { to, scope, delivered: true }; } ``` ## Adding a schedule at run time The runtime reconciles the `schedules` export on every boot. That makes it the right home for a cadence that ships with the workspace. To add or drop one while the box is running, call the namespaced accessor from inside a workflow: ```ts import { stackbone } from '@stackbone/sdk'; await stackbone.workflows.schedule('daily-digest', { scope: 'weekly' }, '0 8 * * 1'); await stackbone.workflows.unschedule('daily-digest'); const active = await stackbone.workflows.listSchedules(); ``` These three live only on `stackbone.workflows`. There is no top-level `scheduleWorkflow` import to reach for. The runtime keeps dynamic schedules apart from the declarative ones, so a deploy never prunes them. ## What's next - **[An onboarding pipeline](/docs/examples/workflows/onboarding-pipeline)**: a linear run you trigger by hand. - **[A refund gated by a human approval](/docs/examples/workflows/refund-approval)**: park the run until a person decides. - **[What is a workflow](/docs/sdk/workflows/overview)**: the model behind this example. - **[Workflow + Agents examples](/docs/examples/workflow-agents/delegate-to-agent)**: let a step hand work to a conversational agent.