# How to Manage Flow Configurations Using the Flow CRUD Operations in OpenDeRisk

> Master OpenDeRisk flow configurations with CRUD operations. Learn to manage flow metadata and DAG structures using the derisk-client Python library for efficient data pipeline control.

- Repository: [derisk-ai/openderisk](https://github.com/derisk-ai/openderisk)
- Tags: how-to-guide
- Published: 2026-02-28

---

**You manage flow configurations in OpenDeRisk by using the `derisk-client` Python library to perform CRUD operations on `FlowPanel` objects, which encapsulate both flow metadata and the serialized DAG structure.**

OpenDeRisk represents every workflow as a **FlowPanel**, the canonical Pydantic model for an AWEL (Agent Workflow Engine Language) flow. This model lives in [`packages/derisk-core/src/derisk/core/awel/flow/flow_factory.py`](https://github.com/derisk-ai/openderisk/blob/main/packages/derisk-core/src/derisk/core/awel/flow/flow_factory.py) and stores everything from unique identifiers to the visual graph data. The `derisk-client` package exposes a complete CRUD API in [`packages/derisk-client/src/derisk_client/flow.py`](https://github.com/derisk-ai/openderisk/blob/main/packages/derisk-client/src/derisk_client/flow.py), allowing you to create, read, update, delete, and execute flows programmatically via REST endpoints.

## Understanding the FlowPanel Data Model

Before calling CRUD functions, you must understand the `FlowPanel` schema. According to the OpenDeRisk source code, a panel contains **metadata fields** (`uid`, `label`, `name`, `flow_category`) and a **nested `FlowData` object** (`flow_data`) that holds the node and edge arrays used by the visual editor.

In [`derisk/core/awel/flow/flow_factory.py`](https://github.com/derisk-ai/openderisk/blob/main/derisk/core/awel/flow/flow_factory.py), the model defines:
- **UID generation** and basic identity fields (lines 30‑55)
- **`flow_data`** containing `nodes`, `edges`, and viewport coordinates (lines 56‑63)
- **State handling** (initializing, developing, deployed) managed via the panel lifecycle (lines 122‑150)

When you create or update a flow, you construct a `FlowPanel` instance in Python, and the client library serializes it to JSON for transmission to the Derisk server.

## Creating Flow Configurations

To persist a new flow, use the `create_flow()` function. This asynchronous method sends a `POST` request to `/awel/flows` with your `FlowPanel` payload. The server validates the structure, generates a unique `uid`, and returns the persisted object.

The `create_flow()` implementation resides in [`packages/derisk-client/src/derisk_client/flow.py`](https://github.com/derisk-ai/openderisk/blob/main/packages/derisk-client/src/derisk_client/flow.py) (lines 13‑24). It accepts a `Client` instance—defined in [`packages/derisk-client/src/derisk_client/client.py`](https://github.com/derisk-ai/openderisk/blob/main/packages/derisk-client/src/derisk_client/client.py)—and a `FlowPanel` object, handling JSON conversion automatically.

```python
from derisk_client import Client
from derisk_client.flow import create_flow
from derisk.core.awel.flow.flow_factory import FlowPanel, FlowData, FlowNodeData, FlowEdgeData, FlowPositionData

client = Client(base_url="https://api.derisk.ai")

# Define a minimal DAG with one node and a self-referencing edge

node = FlowNodeData(
    id="dummy_node_0",
    width=300,
    height=200,
    position=FlowPositionData(x=100, y=100, zoom=1.0),
    position_absolute=FlowPositionData(x=100, y=100, zoom=1.0),
    data={"flow_type": "operator", "type": "dummy", "params": {}},
)
edge = FlowEdgeData(
    source="dummy_node_0",
    target="dummy_node_0",
    source_order=0,
    target_order=0,
    id="edge_0",
)
flow_data = FlowData(nodes=[node], edges=[edge], viewport=FlowPositionData(x=0, y=0, zoom=1.0))

panel = FlowPanel(label="Demo Flow", name="demo_flow", flow_data=flow_data)
created = await create_flow(client, panel)
print(f"Created flow with uid={created.uid}")

```

## Retrieving Flow Configurations

You can fetch flows individually by UID or list all flows for the current user. The **`get_flow()`** function (lines 76‑88) retrieves a single `FlowPanel` by its unique identifier, while **`list_flow()`** (lines 99‑119) returns a list of panels with optional filtering by `name` or `uid`.

Both methods deserialize the server’s JSON response back into `FlowPanel` objects, preserving the full `flow_data` DAG structure so you can inspect node configurations programmatically.

```python
from derisk_client.flow import list_flow, get_flow

# List all flows

flows = await list_flow(client)
print("Available flows:", [f.name for f in flows])

# Fetch a specific flow by UID

specific_flow = await get_flow(client, flows[0].uid)
print(f"Retrieved flow label: {specific_flow.label}")

```

## Updating and Deleting Flows

When modifying an existing flow, use **`update_flow()`** (lines 31‑42). This function sends a `PUT` request to `/awel/flows` with the modified `FlowPanel`. The server identifies the target flow via the panel’s `uid` field and overwrites the stored configuration with your new metadata and DAG data.

To remove a flow permanently, call **`delete_flow()`** (lines 53‑63), passing the flow’s `uid`. This operation is idempotent and returns immediately after the server confirms deletion.

```python
from derisk_client.flow import list_flow, update_flow, delete_flow

# Fetch, modify, and update

flows = await list_flow(client)
flow_to_update = flows[0]
flow_to_update.label = "Production Data Pipeline"

updated = await update_flow(client, flow_to_update)
print(f"New label persisted: {updated.label}")

# Clean up

await delete_flow(client, updated.uid)
print("Flow configuration removed")

```

## Executing Flows

Beyond CRUD, the client library supports flow execution via **`run_flow_cmd()`** (lines 124‑169). This function triggers a flow by `name` or `uid`, optionally passing input data and registering a streaming callback to handle real-time output chunks.

Execution is asynchronous and supports **streaming callbacks**, making it suitable for long-running workflows that emit progress updates or partial results.

```python
from derisk_client.flow import run_flow_cmd

def on_stream(chunk: str):
    print("STREAM:", chunk, end="")

await run_flow_cmd(
    client,
    name="demo_flow",               # or uid="..."

    data={"input_key": "value"},    # trigger payload

    streaming_callback=on_stream,
)

```

## Summary

- **FlowPanel** is the canonical model for AWEL flows, combining metadata and DAG serialization in [`derisk/core/awel/flow/flow_factory.py`](https://github.com/derisk-ai/openderisk/blob/main/derisk/core/awel/flow/flow_factory.py).
- **CRUD operations** live in [`packages/derisk-client/src/derisk_client/flow.py`](https://github.com/derisk-ai/openderisk/blob/main/packages/derisk-client/src/derisk_client/flow.py) and use async functions: `create_flow()`, `get_flow()`, `list_flow()`, `update_flow()`, and `delete_flow()`.
- The client communicates with **POST, PUT, and DELETE endpoints** under `/awel/flows`, automatically converting `FlowPanel` objects to JSON.
- **Execution** uses `run_flow_cmd()` with support for streaming output callbacks.
- All operations require a configured `Client` instance from `derisk_client.client` pointing to your Derisk server endpoint.

## Frequently Asked Questions

### How is the flow UID generated when creating a new flow?

The **UID is generated server-side** when `create_flow()` sends the `POST` request to `/awel/flows`. The server validates the incoming `FlowPanel`, assigns a unique identifier, and returns the populated object including the generated `uid` field. You should not set the `uid` manually before creation.

### Can I filter the list of flows returned by `list_flow()`?

Yes. The `list_flow()` function accepts optional `name` and `uid` parameters that are passed as query arguments to the server. When provided, the API returns only flows matching those criteria. Without filters, the function returns all flow configurations accessible to the authenticated user.

### What happens to the `flow_data` field during an update operation?

When you call `update_flow()`, the entire `FlowPanel`—including the nested `flow_data` object containing nodes and edges—is serialized to JSON and sent via `PUT /awel/flows`. The server performs a full overwrite of the existing flow record identified by the panel’s `uid`, so any changes to the DAG structure or metadata are persisted atomically.

### Is `run_flow_cmd` part of the CRUD operations?

**No**, `run_flow_cmd()` is an execution command rather than a CRUD operation. While create, read, update, and delete manage the static configuration of a flow, `run_flow_cmd()` triggers the actual runtime execution of the compiled DAG. It accepts a `name` or `uid` to identify the target flow and optional streaming callbacks to handle real-time output during execution.