import { snippets } from "@/lib/generated/snippets";
import { Snippet } from "@/components/code";
import { Callout, Tabs } from "nextra/components";
import UniversalTabs from "@/components/UniversalTabs";
import HumanInLoopDiagram from "@/components/HumanInLoopDiagramWrapper";

# Durable Event Waits

Tasks can pause until an external event arrives before continuing. This is the foundation for human-in-the-loop workflows, webhook-driven pipelines, and any process that depends on signals from outside the task.

<HumanInLoopDiagram />

Events are delivered by pushing events into Hatchet either [using one of the SDKs](/v1/events#pushing-events-to-hatchet) or via an [incoming webhook](/v1/webhooks). The event key you push must match the key your task is waiting for.

Waiting for an event lets a durable task pause until an event arrives. Even if the task is interrupted and requeued while waiting, the event will still be processed. When it resumes, it reads the event from the durable event log and continues.

## Establishing an event wait

Your durable tasks can establish an event wait by using helper methods on the `DurableContext`.

<UniversalTabs items={["Python", "Typescript", "Go", "Ruby"]}>
  <Tabs.Tab title="Python">

<Snippet src={snippets.python.durable_event.worker.durable_event} />

</Tabs.Tab>
<Tabs.Tab title="Typescript">

<Snippet src={snippets.typescript.durable_event.workflow.durable_event} />

</Tabs.Tab>
<Tabs.Tab title="Go">

<Snippet src={snippets.go.durable.event.main.durable_event} />

</Tabs.Tab>
  <Tabs.Tab title="Ruby">
    <Snippet src={snippets.ruby.durable_event.worker.durable_event} />
  </Tabs.Tab>
</UniversalTabs>

## Matching CEL expressions

In general, you'll want to match the payload of the incoming event to some data that your task knows about to make sure the event you receive is the _right_ one. For instance, you might want to make sure that the event payload contains the correct user or organization id in order to consider the event wait as having been completed. To do this, you can provide a [CEL expression](https://celbyexample.com/) in addition to the event key when establishing the wait.

<UniversalTabs items={["Python", "Typescript", "Go", "Ruby"]} variant="hidden">
  <Tabs.Tab title="Python">

<Snippet src={snippets.python.durable_event.worker.durable_event_with_filter} />

</Tabs.Tab>
<Tabs.Tab title="Typescript">

<Snippet
  src={snippets.typescript.durable_event.workflow.durable_event_with_filter}
/>

</Tabs.Tab>
<Tabs.Tab title="Go">

<Snippet src={snippets.go.durable.event.main.durable_event_with_filter} />

</Tabs.Tab>
  <Tabs.Tab title="Ruby">
    <Snippet src={snippets.ruby.durable_event.worker.durable_event_with_filter} />
  </Tabs.Tab>
</UniversalTabs>

## Lookback Windows

Event waits can also optionally look back in time for recent events that have come in, which is often useful for preventing race conditions. An important caveat is that in order look up previous events, the wait must also be established alongside a **scope**, which must also be pushed with the event itself. The scope serves as a hint to help Hatchet narrow down the pool of candidate events.

As a simple example, if you're establishing an event wait with a CEL expression like `input.user_id == 1234`, then you know that `{"user_id": 1234}` is present on the event payload, so you might use the string `user_id:1234` as the scope, **both when establishing the wait and pushing the event.**

<UniversalTabs items={["Python", "Typescript", "Go", "Ruby"]} variant="hidden">
  <Tabs.Tab title="Python">

<Snippet src={snippets.python.durable.worker.wait_for_event_with_lookback} />

</Tabs.Tab>
<Tabs.Tab title="Typescript">

<Snippet
  src={snippets.typescript.durable.workflow.wait_for_event_with_lookback}
/>

</Tabs.Tab>
<Tabs.Tab title="Go">

{/* TODO-DOCS: Go example here */}

</Tabs.Tab>
  <Tabs.Tab title="Ruby">
    {/* TODO-DOCS: Ruby example here */}
  </Tabs.Tab>
</UniversalTabs>
