> ## Documentation Index
> Fetch the complete documentation index at: https://docs.restate.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# External Events

> Handle external events and human-in-the-loop patterns with durable waiting primitives.

Sometimes your handlers need to pause and wait for external processes to complete. This is common in:

* **Human-in-the-loop workflows** (approvals, reviews, manual steps)
* **External system integration** (waiting for webhooks, async APIs)
* **AI agent patterns** (tool execution, human oversight)

This pattern is also known as the **callback** or **task token** pattern.

## Two Approaches

Restate provides two primitives for handling external events:

| Primitive            | Use Case                   | Key Feature                          |
| -------------------- | -------------------------- | ------------------------------------ |
| **Awakeables**       | Services & Virtual Objects | Unique ID-based completion           |
| **Durable Promises** | Workflows only             | Named promises for simpler signaling |

## How it works

Implementing this pattern in a distributed system is tricky, since you need to ensure that the handler can recover from failures and resume waiting for the external event.

Restate promises are durable and distributed. They survive crashes and can be resolved or rejected by any handler in the workflow.

To save costs on FaaS deployments, Restate lets the handler [suspend](/foundations/key-concepts#suspensions-on-faas) while awaiting the promise, and invokes it again when the result is available.

## Awakeables

**Best for:** Services and Virtual Objects where you need to coordinate with external systems.

### Creating and waiting for awakeables

1. **Create an awakeable** - Get a unique ID and Future
2. **Send the ID externally** - Pass the awakeable ID to your external system
3. **Wait for result** - Your handler [suspends](/foundations/key-concepts#suspensions-on-faas) until the external system responds

<CodeGroup>
  ```java Java {"CODE_LOAD::java/src/main/java/develop/Awakeables.java#here"}  theme={null}
  // Create awakeable and get unique ID
  Awakeable<String> awakeable = ctx.awakeable(String.class);
  String awakeableId = awakeable.id();

  // Send ID to external system (email, queue, webhook, etc.)
  ctx.run(() -> requestHumanReview(name, awakeableId));

  // Handler suspends here until external completion
  String review = awakeable.await();
  ```

  ```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#here"}  theme={null}
  // Create awakeable and get unique ID
  val awakeable = ctx.awakeable<String>()
  val awakeableId = awakeable.id

  // Send ID to external system (email, queue, webhook, etc.)
  ctx.runBlock { requestHumanReview(awakeableId) }

  // Handler suspends here until external completion
  val review = awakeable.await()
  ```
</CodeGroup>

<Accordion title="Serialization">
  To customize serialization, visit the [docs](/develop/java/serialization).
</Accordion>

<Info>
  Note that if you wait for an awakeable in an [exclusive handler](/foundations/handlers#handler-behavior) in a Virtual Object, all other calls to this object will be queued.
</Info>

### Resolving/rejecting Awakeables

External processes complete awakeables in two ways:

* **Resolve** with success data → handler continues normally
* **Reject** with error reason → throws a [terminal error](/develop/java/error-handling) in the waiting handler

#### Via SDK (from other handlers)

**Resolve:**

<CodeGroup>
  ```java Java {"CODE_LOAD::java/src/main/java/develop/Awakeables.java#resolve"}  theme={null}
  // Complete with success data
  ctx.awakeableHandle(awakeableId).resolve(String.class, "Looks good!");
  ```

  ```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#resolve"}  theme={null}
  // Complete with success data
  ctx.awakeableHandle(awakeableId).resolve("Looks good!")
  ```
</CodeGroup>

**Reject:**

<CodeGroup>
  ```java Java {"CODE_LOAD::java/src/main/java/develop/Awakeables.java#reject"}  theme={null}
  // Complete with error
  ctx.awakeableHandle(awakeableId).reject("This cannot be reviewed.");
  ```

  ```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#reject"}  theme={null}
  // Complete with error
  ctx.awakeableHandle(awakeableId).reject("This cannot be reviewed.")
  ```
</CodeGroup>

#### Via HTTP API

External systems can complete awakeables using Restate's HTTP API:

**Resolve with data:**

```shell theme={null}
curl localhost:8080/restate/awakeables/sign_1PePOqp/resolve \
--json '"Looks good!"'
```

**Reject with error:**

```shell theme={null}
curl localhost:8080/restate/awakeables/sign_1PePOqp/reject \
-H 'content-type: text/plain' \
-d 'Review rejected: insufficient documentation'
```

## Durable Promises

**Best for:** Workflows where you need to signal between different workflow handlers.

**Key differences from awakeables:**

* No ID management - use logical names instead
* Scoped to workflow execution lifetime

Use this for:

* Sending data to the run handler
* Have handlers wait for events emitted by the run handler

<Info>
  After a workflow's run handler completes, other handlers can still be called for up to 24 hours (default).
  The results of resolved Durable Promises remain available during this time.
  Update the retention time via the [service configuration](/services/configuration).
</Info>

### Creating and waiting for promises

Create a promise key:

<CodeGroup>
  ```java Java {"CODE_LOAD::java/src/main/java/develop/ReviewWorkflow.java#promise_key"}  theme={null}
  private static final DurablePromiseKey<String> REVIEW_PROMISE =
      DurablePromiseKey.of("review", String.class);
  ```

  ```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/ReviewWorkflow.kt#promise_key"}  theme={null}
  val REVIEW_PROMISE = durablePromiseKey<String>("review")
  ```
</CodeGroup>

Wait for a promise by name:

<CodeGroup>
  ```java Java {"CODE_LOAD::java/src/main/java/develop/ReviewWorkflow.java#promise"}  theme={null}
  String review = ctx.promise(REVIEW_PROMISE).future().await();
  ```

  ```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/ReviewWorkflow.kt#promise"}  theme={null}
  val review: String = ctx.promise(REVIEW_PROMISE).future().await()
  ```
</CodeGroup>

Your workflow can [suspend](/foundations/key-concepts#suspensions-on-faas) until the promise is resolved or rejected.

### Resolving/rejecting promises

Resolve/reject from any workflow handler:

<CodeGroup>
  ```java Java {"CODE_LOAD::java/src/main/java/develop/ReviewWorkflow.java#resolve_promise"}  theme={null}
  ctx.promiseHandle(REVIEW_PROMISE).resolve(review);
  ```

  ```kotlin Kotlin {"CODE_LOAD::kotlin/src/main/kotlin/develop/ReviewWorkflow.kt#resolve_promise"}  theme={null}
  ctx.promiseHandle(REVIEW_PROMISE).resolve(review)
  ```
</CodeGroup>

### Complete workflow example

<CodeGroup>
  ```java Java expandable {"CODE_LOAD::java/src/main/java/develop/ReviewWorkflow.java#here"}  theme={null}
  @Workflow
  public class ReviewWorkflow {
    private static final DurablePromiseKey<String> REVIEW_PROMISE =
        DurablePromiseKey.of("review", String.class);


    @Workflow
    public String run(WorkflowContext ctx, String documentId) {
      // Send document for review
      ctx.run(() -> askReview(documentId));

      // Wait for external review submission
      String review = ctx.promise(REVIEW_PROMISE).future().await();

      // Process the review result
      return processReview(documentId, review);
    }

    @Shared
    public void submitReview(SharedWorkflowContext ctx, String review) {
      // Signal the waiting run handler
      ctx.promiseHandle(REVIEW_PROMISE).resolve(review);
    }
  }
  ```

  ```kotlin Kotlin expandable {"CODE_LOAD::kotlin/src/main/kotlin/develop/ReviewWorkflow.kt#here"}  theme={null}
  @Workflow
  class ReviewWorkflow {

    companion object {
      val REVIEW_PROMISE = durablePromiseKey<String>("review")
    }

    @Workflow
    suspend fun run(ctx: WorkflowContext, documentId: String): String {
      // Send document for review
      ctx.runBlock { askReview(documentId) }

      // Wait for external review submission
      val review: String = ctx.promise(REVIEW_PROMISE).future().await()

      // Process the review result
      return processReview(documentId, review)
    }

    @Shared
    suspend fun submitReview(ctx: SharedWorkflowContext, review: String) {
      // Signal the waiting run handler
      ctx.promiseHandle(REVIEW_PROMISE).resolve(review)
    }
  }
  ```
</CodeGroup>

### Two signaling patterns

**External → Workflow** (shown above): External handlers signal the run handler

* Use for human approvals, external API responses, manual interventions
* External handlers call the handler which resolves the promise

**Workflow → External**: Run handler signals other handlers waiting for workflow events

* Use for step completion notifications, status updates, result broadcasting
* Run handler resolves promises that external handlers are awaiting

## Best Practices

* **Use awakeables** for services/objects coordinating with external systems
* **Use durable promises** for workflow signaling
* **Always handle rejections** to gracefully manage failures
* **Include timeouts** for long-running external processes
