Restate Skill Reference
Product summary
Restate is a lightweight runtime that makes AI agents, workflows, and backend services durable and resilient. It automatically handles failure recovery, state persistence, and reliable communication between services without requiring you to write retry logic or manage external state stores.
Key files and commands:
- Services run on any platform (Kubernetes, Lambda, Vercel, Docker, etc.) and embed the Restate SDK
- Restate Server is a single binary (
restate-server) that sits in front of your services - CLI:
restate deployments register <endpoint>to register services - UI: Available at
http://localhost:9070for monitoring and testing - Ingress endpoint:
http://localhost:8080for invoking handlers - SDKs: TypeScript, Java/Kotlin, Python, Go, Rust
Primary docs: https://docs.restate.dev
When to use
Reach for Restate when you need to:
- Build AI agents that survive crashes, API rate limits, and network failures without losing progress
- Orchestrate workflows with multi-step processes, approvals, human input, or external events
- Coordinate microservices with automatic retries, exactly-once semantics, and resilient communication
- Process events from Kafka with exactly-once guarantees and built-in state management
- Manage stateful entities (user accounts, shopping carts, chat sessions, state machines) with strong consistency
- Run long-running operations on serverless platforms (Lambda, Vercel) without paying for idle time
Do NOT use Restate for:
- Simple CRUD APIs without failure recovery needs
- Stateless request-response services that don't need durability
- Real-time systems requiring sub-millisecond latency
Quick reference
Service types
| Type | Use case | State | Concurrency |
|---|---|---|---|
| Basic Service | Stateless handlers, ETL, sagas, background jobs | None | Unlimited parallel |
| Virtual Object | User accounts, shopping carts, agents, state machines | K/V store per key | Single writer per key + concurrent readers |
| Workflow | Multi-step processes, approvals, onboarding | K/V store per ID | Single run handler + concurrent signals |
Context actions (available in handlers)
| Action | Purpose | Example |
|---|---|---|
ctx.run() | Wrap non-deterministic operations (API calls, DB writes) | await ctx.run("fetch", () => fetchData()) |
ctx.get() / ctx.set() | Read/write persistent state (Objects/Workflows only) | await ctx.get("cart") |
ctx.serviceClient() | Call another service synchronously | await ctx.serviceClient(UserService).getProfile() |
ctx.serviceSendClient() | Fire-and-forget call to another service | ctx.serviceSendClient(NotificationService).sendEmail(...) |
ctx.sleep() | Pause execution durably | await ctx.sleep({minutes: 5}) |
ctx.promise() | Wait for external event (Workflows only) | await ctx.promise("payment-completed").value() |
CLI commands
# Register a service endpoint
restate deployments register http://localhost:9080
restate deployments register --force http://localhost:9080 # During development
# List registered deployments
restate deployments list
# Manage invocations
restate invocations list
restate invocations cancel <INVOCATION_ID>
restate invocations kill <INVOCATION_ID>
# Describe a service
restate deployment describe <DEPLOYMENT_ID>HTTP invocation patterns
# Invoke a Basic Service handler
curl localhost:8080/MyService/myHandler --json '{"key": "value"}'
# Invoke a Virtual Object handler
curl localhost:8080/MyObject/objectKey/myHandler --json '{"key": "value"}'
# Invoke a Workflow
curl localhost:8080/MyWorkflow/workflowId/run --json '{"key": "value"}'
# Send without waiting for response
curl localhost:8080/MyService/myHandler/send --json '{"key": "value"}'
# With idempotency key
curl localhost:8080/MyService/myHandler \
-H 'idempotency-key: unique-key-123' \
--json '{"key": "value"}'
# With delay
curl "localhost:8080/MyService/myHandler/send?delay=10s" --json '{"key": "value"}'Decision guidance
When to use each service type
| Scenario | Service Type | Why |
|---|---|---|
| API endpoint, background job, ETL pipeline | Basic Service | Stateless, unlimited concurrency, simple |
| User account, shopping cart, chat session | Virtual Object | Needs persistent state, single-writer consistency |
| Multi-step approval flow, onboarding | Workflow | Needs interaction, signals, exactly-once per ID |
| Long-running agent with tool calls | Virtual Object or Workflow | State for context/memory, durable execution |
When to use request-response vs fire-and-forget
| Scenario | Pattern | Why |
|---|---|---|
| Need the result immediately | ctx.serviceClient() | Synchronous, waits for response |
| Background task, notification | ctx.serviceSendClient() | Async, returns immediately, retried automatically |
| Scheduled task | ctx.serviceSendClient(..., sendOpts({delay:...})) | Delayed execution, survives restarts |
When to use state in Restate vs external database
| Scenario | Restate State | External DB |
|---|---|---|
| Session state, agent context, temporary data | ✓ | |
| Frequently accessed, small (<1MB) | ✓ | |
| Shared across multiple services | ✓ | |
| Complex queries, analytics | ✓ | |
| Long-term archival | ✓ |
Workflow
Typical task: Build a durable service
- Understand the requirements
- Identify if you need state (Virtual Object/Workflow) or stateless execution (Basic Service) - Determine failure modes: API timeouts, crashes, duplicate requests - Check if you need human interaction or external events
- Check existing services
- Search the codebase for similar patterns - Review registered services in the UI (http://localhost:9070) - Verify service names don't conflict
- Write the handler
- Wrap all non-deterministic operations in ctx.run() - Use ctx.get()/ctx.set() for state in Objects/Workflows - Use ctx.serviceClient() for synchronous calls, ctx.serviceSendClient() for async - Use ctx.sleep() for delays, ctx.promise() for external events (Workflows only)
- Deploy and register
- Start your service on a port (e.g., 9080) - Register with Restate: restate deployments register http://localhost:9080 - During development, use --force flag to re-register after code changes
- Test and monitor
- Use the Restate UI playground to invoke handlers - Monitor invocations in the UI for failures and retries - Check logs for non-determinism errors during development
- Verify before shipping
- Confirm all external operations are wrapped in ctx.run() - Check state access is only in Objects/Workflows - Verify retry policy is appropriate for your use case - Test failure scenarios (service crash, API timeout)
Common gotchas
- Non-determinism errors: All non-deterministic operations (API calls, random numbers, timestamps, DB writes) must be wrapped in
ctx.run(). Restate replays these on retry and expects the same result. - State only in Objects/Workflows: Basic Services cannot use
ctx.get()/ctx.set(). Use Virtual Objects or Workflows if you need persistent state. - Workflow
runhandler executes once per ID: You cannot re-invoke the same workflow ID. Use signals or shared handlers to interact with a running workflow. - Idempotency key retention: Idempotency keys are retained for 24 hours by default. After that, duplicate requests will re-execute. Adjust with
idempotencyRetentionconfig. - Service registration is immutable: Once registered, a deployment is immutable. Deploy new code to a new endpoint and register it separately. Restate routes new requests to the latest version.
- Lazy state on Lambda: By default, state is eagerly loaded. On Lambda, enable lazy state (
enableLazyState: true) to avoid large payloads, but be aware it may cause replays. - Timeout configuration: Default inactivity timeout is 1 minute. For long-running operations (LLM calls, external APIs), increase
inactivityTimeoutandabortTimeout. - Kafka key must be UTF-8: When invoking Virtual Objects or Workflows via Kafka, the message key determines the object key/workflow ID and must be valid UTF-8.
- Promises are workflow-only:
ctx.promise()is only available in Workflows. Use awakeables in Basic Services/Objects for similar patterns. - State cleared after workflow retention: Workflow state is cleared after the retention period (default 24 hours). Increase
workflowRetentionif you need longer access.
Verification checklist
Before submitting work:
- All external operations (API calls, DB writes, random values) are wrapped in
ctx.run() - State access (
ctx.get(),ctx.set()) is only in Virtual Objects or Workflows - Service is registered with Restate:
restate deployments register <endpoint> - Handlers have appropriate timeout configuration for long-running operations
- Retry policy is configured (default infinite retries with exponential backoff)
- Idempotency keys are used for critical operations
- Service-to-service calls use typed clients or HTTP with proper error handling
- Workflow signals/promises are used correctly for external events
- No hardcoded timestamps, random values, or non-deterministic logic outside
ctx.run() - Tested failure scenarios: service crash, API timeout, duplicate requests
Resources
Comprehensive navigation: https://docs.restate.dev/llms.txt
Critical documentation:
- Key Concepts — Durable execution, state, communication
- Services — Service types and when to use each
- Actions — Context methods for handlers
For additional documentation and navigation, see: https://docs.restate.dev/llms.txt