Skip to main content
Restate Virtual Objects give your agents persistent, isolated sessions. Each session is identified by a unique key (like a user ID or conversation ID), maintains its own state, and has built-in concurrency control, so concurrent requests to the same session are automatically queued.

How it works

A Virtual Object is a Restate service type where each instance is identified by a key. State stored in a Virtual Object:
  • Survives crashes and restarts: No external database needed
  • Is isolated per key: Each session has its own state
  • Has concurrency control: Only one write handler runs at a time per key, preventing race conditions
Virtual Objects as sessions

Example: a chat session

To turn your agent into a stateful session, you need two changes compared to a regular durable agent:
  1. Define a Virtual Object: use restate.object() instead of restate.service(). This gives each session key its own isolated state.
  2. Manage conversation history using the object’s K/V store: use ctx.get() and ctx.set() to read and write the message history.
chat-agent.ts
Install Restate and launch it:
Get the example:
Export your OpenAI API key and run the agent:
Register the agents with Restate:
Ask the agent to do some task. Specify the Virtual Object ID in the URL, for example for session123:
Continue the conversation with the same session ID. The agent remembers previous context:
Send a message to a different session. It starts a completely separate conversation:
The state tab of the Restate UI lets you query the state of each session:
Conversation State Management
This pattern is complementary to AI memory solutions like mem0 or graffiti. You can use Virtual Objects to enforce session concurrency and queueing while storing the agent’s memory in specialized memory systems.
This pattern is implementable with any of our SDKs and any AI SDK. If you need help with a specific SDK, please reach out to us via Discord or Slack.

Built-in concurrency control

Restate queues concurrent requests to the same session key. They are processed sequentially, preventing race conditions on shared state. This works similar to a task queue per session, but without needing to set up any external queue infrastructure. Concurrency control Different session keys run in parallel with no interference.
Send several messages concurrently to different chat sessions:
The UI shows how Restate queues the requests per session to ensure consistency:
Concurrency control in action

Concurrently retrieving state

The state you store in Virtual Objects lives forever. To resume a session, simply send a new message to the same Virtual Object key.
To retrieve state, view the UI’s state tab or add a handler that reads it. Have a look at the getHistory handler in the example above.Call the handler to get the conversation history:
This is a shared handler, meaning it can only read state (not write). This allows it to run concurrently with the message handler.