> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/docs/_mcp/server.

# Restate (tracing)

> Trace Restate Python Services, Virtual Objects, Workflows, replay context, and terminal failures with Respan.

[Restate](https://github.com/restatedev/sdk-python) is a durable execution system with Python Service, Virtual Object, and Workflow handlers. Respan uses Restate's `invocation_context_managers` extension point to create one span around each handler invocation attempt.

#### Set up Respan

Create an account at [platform.respan.ai](https://platform.respan.ai) and grab an [API key](https://platform.respan.ai/platform/gateway/api-keys).

Run `npx @respan/cli setup` to set up with your coding agent.

#### Example project

* [Service, Virtual Object, Workflow, and terminal-error example](https://github.com/respanai/respan-example-projects/tree/main/python/tracing/restate)

## Setup

#### Install packages

```bash
pip install respan-ai respan-instrumentation-restate restate-sdk hypercorn
```

#### Set your API key

```bash
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
```

#### Initialize and register handlers

Initialize Respan before decorating Restate handlers. The instrumentor adds its invocation context manager when handlers are registered.

**`Python`**

```python Python
import asyncio

import hypercorn
import hypercorn.asyncio
import restate
from respan import Respan
from respan_instrumentation_restate import RestateInstrumentor

respan = Respan(
    instrumentations=[RestateInstrumentor()],
)

greeter = restate.Service("Greeter")


@greeter.handler()
async def greet(ctx: restate.Context, name: str) -> str:
    return f"Hello, {name}!"


app = restate.app(services=[greeter])

config = hypercorn.Config()
config.bind = ["0.0.0.0:9080"]

try:
    asyncio.run(hypercorn.asyncio.serve(app, config))
finally:
    respan.shutdown()
```

#### Run Restate

Start a local Restate server, register the Python endpoint, and invoke the handler:

```bash
# Terminal 1
npx -y @restatedev/restate-server

# Terminal 2
python app.py

# Terminal 3
npx -y @restatedev/restate deployments register --force --yes http://localhost:9080
curl -X POST http://localhost:8080/Greeter/greet \
  -H "content-type: application/json" \
  -d '"Ada"'
```

You can also use the official Restate server and CLI binaries from [Restate releases](https://github.com/restatedev/restate/releases) if the npm launcher does not provide a binary for your platform.

#### View your trace

Open the [Traces page](https://platform.respan.ai/platform/traces) to inspect each invocation's handler and durable-execution context.

## What gets traced

| Restate handler           | Respan span | Captured Restate context                                          |
| ------------------------- | ----------- | ----------------------------------------------------------------- |
| `Service.handler()`       | `task`      | Service and handler names, invocation ID, replay state            |
| `VirtualObject.handler()` | `task`      | Object key, exclusive/shared kind, invocation ID, replay state    |
| `Workflow.main()`         | `workflow`  | Workflow key, invocation ID, replay state                         |
| `Workflow.handler()`      | `task`      | Workflow key, shared-handler context, invocation ID, replay state |

When Restate provides them, the adapter also records scope, limit key, idempotency key, service metadata, and handler metadata. The request is deserialized through the handler's configured Restate serde.

Restate invocation IDs are mapped to Respan trace groups. Virtual Object and Workflow keys are mapped to thread identifiers.

## Handler results

Restate's invocation context managers surround execution but do not receive the handler's serialized return value. Successful spans therefore record a completion status, not an invented response body. Failed spans record the real exception class, message, and status code exposed by Restate, including custom `TerminalError` codes.

This behavior intentionally differs from integrations whose native hooks expose a return value.

## Replay and content controls

The adapter records whether Restate reports the invocation as replaying; it traces the actual invocation attempt exposed to the context manager.

Disable deserialized request capture when request bodies may contain sensitive data:

```python
respan = Respan(
    instrumentations=[RestateInstrumentor(capture_content=False)]
)
```

Service, handler, invocation, replay, key, scope, idempotency, and status fields remain available.

## Lifecycle

The instrumentor patches handler registration on `Service`, `VirtualObject`, and `Workflow`. Handlers registered before activation are not retrofitted. Each service object receives at most one Respan context manager, and existing user-provided context managers are preserved.

`activate()` and `deactivate()` are reference-counted. Call `respan.shutdown()` during service shutdown to flush pending spans and restore the registration methods.