Skip to main content
Some agent actions need human approvals for dangerous actions: deploying code, sending money, modifying user accounts. With Restate, your agent can pause execution, wait for an external signal, and resume exactly where it left off, even if the process restarts in the meantime.

How it works

Restate provides durable promises (awakeables) that survive crashes and restarts:
  1. The agent creates a durable promise and gets a unique ID
  2. The agent sends this ID to whoever needs to approve (Slack, email, dashboard)
  3. The agent suspends, freeing compute resources (no idle billing on serverless)
  4. When the approver responds, they resolve the promise via HTTP
  5. The agent resumes from the exact point it paused

Example: approval tool for an agent

human-approval-agent.ts
const run = async (ctx: restate.Context, { prompt }: ClaimPrompt) => {
  const model = wrapLanguageModel({
    model: openai("gpt-5.4"),
    middleware: durableCalls(ctx, { maxRetryAttempts: 3 }),
  });

  const { text } = await generateText({
    model,
    system:
      "You are an insurance claim evaluation agent. Use these rules: " +
      "* if the amount is more than 1000, ask for human approval, " +
      "* if the amount is less than 1000, decide by yourself",
    prompt,
    tools: {
      humanApproval: tool({
        description: "Ask for human approval for high-value claims.",
        inputSchema: InsuranceClaimSchema,
        execute: async (claim: InsuranceClaim): Promise<boolean> => {
          const approval = ctx.awakeable<boolean>();
          await ctx.run("request-review", () =>
            requestHumanReview(claim, approval.id),
          );
          return approval.promise;
        },
      }),
    },
    stopWhen: [stepCountIs(5)],
    providerOptions: { openai: { parallelToolCalls: false } },
  });
  return text;
};
Install Restate and launch it:
npm install --global @restatedev/restate-server@latest @restatedev/restate@latest
restate-server
Get the example:
restate example typescript-vercel-ai-tour-of-agents && cd typescript-vercel-ai-tour-of-agents
npm install
Export your OpenAI API key and run the agent:
export OPENAI_API_KEY=sk-...
npx tsx ./src/human-approval-agent.ts
Register the agents with Restate:
restate deployments register http://localhost:9080 --force --yes # dev only: overrides previous registrations
Use curl with the send verb to start the claim asynchronously, without waiting for the result:
curl localhost:8080/restate/send/HumanClaimApprovalAgent/run \
--json '{"prompt": "Process my hospital bill of 3000USD for a broken leg."}'
You can restart the service to see how Restate continues waiting for the approval.If you wait for more than a minute, the invocation will get suspended.
Invocation overview
Simulate approving the claim by executing the curl request that was printed in the service logs, similar to:
curl localhost:8080/restate/awakeables/sign_1M28aqY6ZfuwBmRnmyP/resolve --json 'true'
See in the UI how the workflow resumes and finishes after the approval.
Invocation overview

Why this matters for agents

  • No idle resources: The agent suspends while waiting. On serverless infrastructure, you pay nothing during the wait.
  • Survives restarts: Even if the process or infrastructure changes, the agent resumes when the approval arrives.
  • Composable: Combine with tool calls, multi-step workflows, and other patterns. The approval is just another durable step.

Adding timeouts

Add a timeout to prevent approval steps from hanging indefinitely. Restate persists both the timer and the approval promise, so if the service crashes or is restarted, it will continue waiting with the correct remaining time.
human-approval-agent-with-timeout.ts
const approval = ctx.awakeable<boolean>();
await ctx.run("request-review", () =>
  requestHumanReview(claim, approval.id),
);
try {
  // At most 3 hours, to reach our SLA
  const approved = await approval.promise.orTimeout({ hours: 3 });
  return { approved };
} catch (e) {
  if (e instanceof TimeoutError) {
    return {
      approved: false,
      reason: "Approval timed out - Evaluate with AI",
    };
  }
  throw e;
}
Start the timeout agent (stop any previously running agent first):
npx tsx ./src/human-approval-agent-with-timeout.ts
Register the agents with Restate:
restate deployments register http://localhost:9080 --force --yes # dev only: overrides previous registrations
Send a request to the service:
curl localhost:8080/restate/send/HumanClaimApprovalWithTimeoutsAgent/run \
--json '{"prompt": "Process my hospital bill of 3000USD for a broken leg."}'
Restart the service and check in the UI how the process will block for the remaining time without starting over.You can also lower the timeout to a few seconds to see how the timeout path is taken.