Production & Scale/Production Operations
Intermediate16 min

Cron Jobs & Webhooks

Cron jobs schedule recurring agent runs on LangGraph Platform — the critical design choice is thread-bound (state accumulates across runs) vs stateless (fresh each time, requires cleanup). Webhooks notify your systems when runs complete. This article covers correct API usage, timezone handling, failure modes, and monitoring — the production gaps that documentation does not warn you about.

Quick Reference

  • Thread-bound crons use client.crons.create_for_thread(thread_id, assistant_id, ...) — a separate method from create()
  • Stateless crons use client.crons.create(assistant_id, schedule=..., on_run_completed='delete')
  • on_run_completed accepts 'delete' (not 'delete_thread') and 'keep'
  • All cron schedules default to UTC — set the timezone parameter explicitly
  • Webhooks fire on run completion (success or error) — there is no separate stream or cron webhook type
  • Webhook config in langgraph.json uses webhooks.headers and webhooks.url (not custom_headers / url_restrictions)
  • The most common production failure: cron interval shorter than agent run time — runs queue up indefinitely

When (and When Not) to Schedule Agent Runs

Not every recurring task needs a cron

If a run is triggered by an external event (new file uploaded, Slack message received, database row inserted), use a webhook trigger or API call instead. Crons are for time-based schedules. Using a cron to poll for events adds latency and wastes runs when nothing has changed.

Trigger patternUseExample
Fixed scheduleCron jobDaily report at 9 AM, hourly health check
External eventWebhook trigger → API callProcess file on S3 upload, react to Stripe webhook
User-initiatedDirect API call"Run analysis" button in your UI
After another run completesWebhook on run → trigger next runPost-processing pipeline

Once you know a cron is right, the next choice is thread-bound vs stateless. This shapes your data model, your token costs, and your cleanup strategy.

Thread-Boundcreate_for_thread()thread-abc (persistent)state grows with each runRun 1day 1Run 2day 2Run 3day 3✓ memory persists⚠ context grows dailyStatelesscrons.create() + on_run_completed="delete"thread-1xxauto-deleted×Run 1day 1thread-2xxauto-deleted×Run 2day 2thread-3xxauto-deleted×Run 3day 3× thread deleted after each run✓ no accumulated context× = deleted · dashed border = ephemeral thread · ⚠ = token cost grows over time

Thread-bound reuses one thread (state accumulates); stateless creates and deletes a thread per run

Start stateless

Start with stateless crons. Only switch to thread-bound when you genuinely need the agent to reference previous runs. Thread-bound crons accumulate state indefinitely — after 90 daily runs, you have 90 days of conversation history being passed on every execution, which means growing input token costs and eventual context window overflow.