Skip to main content

Quickstart: Workflow

In this quickstart, you'll run an order processing workflow on Catalyst Cloud. You will learn how to:

  • Provision a Catalyst project with a managed workflow engine using the Diagrid CLI.
  • Run a stateful, multi-step order workflow that chains inventory checking, payment processing, and notification activities.
  • Start, monitor, and inspect workflow executions using both the API and the Catalyst web console.
  • Recover a workflow after the process dies mid-run, resuming the same instance under an ID you choose.

1. Prerequisites

Before you proceed, ensure you have the following prerequisites installed.

2. Log in to Catalyst

Authenticate to Diagrid Catalyst using the following command:

diagrid login

This command opens a new browser window where where you'll be shown a confirmation code that should match the code in your terminal. Confirm the code, and if you're not logged into Catalyst, you'll be redirected to login.

Confirm your user details are correct using the following command:

diagrid whoami

The expected output contains the name of the organization, your user name, and the Catalyst API endpoint.

3. Clone Quickstart Code

Clone the quickstart code from GitHub:

git clone https://github.com/diagridio/catalyst-quickstarts

Navigate to the quickstart directory:

cd catalyst-quickstarts/workflow/python

4. Install Dependencies

Install the dependencies for the workflow application.

Install Python dependencies:

uv sync --all-packages

5. Run the application with Catalyst Cloud

The diagrid dev run command creates your Catalyst project, provisions resources (apps, workflow engine, managed state store), configures environment variables, and launches your application connected to Catalyst Cloud.

Run the application:

uv run diagrid dev run -f workflow-quickstart.yaml --project workflow-quickstart --approve
tip

Wait a few seconds until you see application logs in the terminal to ensure the application is up and running and connected to Catalyst.

6. Start and inspect a workflow instance

The Order Processing workflow chains notification, inventory, payment, and shipping activities, see the diagram for more details.

6.1 Start workflow

Open a new terminal and start a new workflow by making a POST request to the start endpoint:

curl -i -X POST http://localhost:5001/workflow/start -H "Content-Type: application/json" -d '{"name":"Car", "quantity":2}'

This returns a workflow instance ID. Copy the value from the response and save it as an environment variable for subsequent calls:

export INSTANCE_ID=<YOUR_INSTANCE_ID>

6.2 Get workflow status

Get the workflow status by making a GET request to the status endpoint and providing the instance ID.

curl -i -X GET http://localhost:5001/workflow/status/$INSTANCE_ID

The response is a JSON structure that is similar to this:

{
"exists":true,
"isWorkflowRunning":false,
"isWorkflowCompleted":true,
"createdAt":"<DATE_TIME>",
"lastUpdatedAt":"<DATE_TIME>",
"runtimeStatus":1,
"failureDetails":null
}

Leave the application from step 5 running.

6.3 View in the Catalyst web console

Open the Workflow viewer in the Catalyst Cloud web console and select the workflow instance that you just started to see a visual execution trace. The viewer displays each activity in sequence, its completion status, and the total workflow duration — useful for debugging long-running or failed executions.

Catalyst Workflow viewer showing the completed order-workflow execution with activity statuses and timing

7. Recover from a crash

Durable execution earns its name when a process dies mid-run. This quickstart ships a second workflow for exactly that: crash_recovery_workflow runs a fast activity, then a slow one that takes about 30 seconds. The app kills itself partway through that slow activity, at a point you choose, and you restart it and watch the run finish without redoing the work it had already recorded.

Two things make the demo legible. You define the instance ID, so you can find the same run again. And the confirmation code is derived from the instance ID, so the answer after the restart is visibly the same answer.

7.1 Start a run with a known instance ID

Open a new terminal. This time the request you'll use contains a kill_after_seconds parameter which causes to app to crash after ~8 seconds. The request blocks until that happens and then reports a connection reset, the process is gone before it can answer, which is exactly what a real crash would look like.

Pick an ID you have not used before

Re-issuing an ID attaches to the run it already names instead of starting a new one, so an ID left over from a finished run answers instantly with a correct-looking confirmation and you never see a crash at all. Every language tab here shares the same project (workflow-quickstart) and the same documented ID (trip-42), so if you have already walked through another language, substitute a distinct ID such as trip-42-python wherever the rest of step 7 writes trip-42.

curl -i -X POST http://localhost:5001/crash/run -H "Content-Type: application/json" -d '{"id":"trip-42", "reference":"ABC123", "kill_after_seconds": 8}'

In the terminal running diagrid dev run, the fast activity completes, the slow one announces the window, and the app then ends itself:

== APP - order-workflow == INFO:workflow:Reservation trip-42 received for ABC123
== APP - order-workflow == INFO:workflow:Committing reservation ABC123 over ~30s, but this process kills itself 8s into the run, as asked by kill_after_seconds. It resumes on restart.
== APP - order-workflow == WARNING:main:>>> crash: killing this process 8s into the run, as asked by kill_after_seconds

The workflow instance trip-42 is unaffected. It lives in Catalyst, not in the process that just died.

Crashing the app yourself instead

Leave kill_after_seconds out of the request and nothing is armed: the call blocks for the length of the slow activity, and you crash the app from a second terminal whenever you like. This is the flow the quickstart READMEs lead with, and it is worth trying once — aiming a kill at a running activity is what the armed timer spares you.

curl -i -X POST http://localhost:5001/crash/kill

POST /crash/kill is demo scaffolding — do not copy it into a real service. It is an unauthenticated endpoint that lets any caller who can reach the port terminate the process, and it exists only to make a crash reproducible on demand. Nothing else in this quickstart depends on it.

7.2 Restart the app

Start the application again with the same command as step 5. That is the whole recovery. You do not have to send anything. The run is not waiting on you: Catalyst has been retrying the interrupted activity the entire time the app was down, and it hands the pending work back the moment the restarted app's worker reconnects. The log below is usually scrolling before you can type.

Read the app log carefully, because this is the whole proof:

uv run diagrid dev run -f workflow-quickstart.yaml --project workflow-quickstart --approve
== APP - order-workflow == INFO:workflow:Committing reservation ABC123 over ~30s. KILL THE APP NOW to test crash recovery (POST /crash/kill, or kill -9). It resumes on restart.
== APP - order-workflow == INFO:workflow:Committed reservation ABC123. Confirmation code: BK-E0BEBD22
== APP - order-workflow == INFO:workflow:Reservation trip-42 has completed! Reservation ABC123 confirmed. Confirmation code: BK-E0BEBD22

The commit line reads differently this time: nothing is armed in the restarted process, so it prints its un-armed prompt to kill the app. Ignore it — the run is already finishing on its own.

Reservation trip-42 received for ABC123 does not appear again. That activity had already completed and Catalyst had recorded its result, so the replay took the recorded value instead of re-running it. Only the activity that was interrupted runs a second time.

7.3 Collect the answer

The run recovered on its own, but the crash took the connection that was waiting for its result: the blocked request from step 7.1 died with the process, and its answer had nowhere to go. Send the request from step 7.1 once more, without kill_after_seconds, to open a new connection to the run that already finished:

curl -i -X POST http://localhost:5001/crash/run -H "Content-Type: application/json" -d '{"id":"trip-42", "reference":"ABC123"}'

Because the instance already exists, this call attaches to it instead of booking a second time, and the app logs Attaching to existing crash-recovery workflow trip-42 to say so. It resumes nothing, because nothing was waiting: it reads back the confirmation code the recovered run recorded in step 7.2.

{"id":"trip-42","result":"Reservation ABC123 confirmed. Confirmation code: BK-E0BEBD22","message":null}

Send it while the slow activity is still re-running and it simply blocks until the run finishes. If the wait budget elapses first, the response is a 202 carrying the instance ID. That is not a failure either: send the same request again to attach again.

7.4 View the recovered run in the Catalyst web console

Open the Workflow viewer and select the instance named trip-42. The trace shows one execution, not two, with the interrupted activity attempted twice and every other activity once.

The instance ID is a handle you own

A durable activity is at-least-once, so make side-effecting work idempotent by keying off a business value, as the commit-reservation activity keys its confirmation code off the booking reference. To run the demo again, pick a new ID: this one now names a finished run, and re-issuing it would only attach to that.

The slow activity's length is configurable through the CRASH_DELAY_SECONDS environment variable, which defaults to 30. Set it lower to shorten the window, or higher if you want a longer run before the self-kill fires, but stay under the wait budget /crash/run allows: a delay above that makes the first call return a 202 instead of the blocking 200 step 7.1 describes. That budget is CRASH_WAIT_SECONDS, which defaults to 120, so raise it too if you want a longer delay than that.

8. Clean Up

Press CTRL+C in the terminal that runs diagrid dev run to stop the application and disconnect from Catalyst Cloud.

To delete the entire project and all provisioned resources:

diagrid project delete workflow-quickstart

Summary

In this quickstart you:

  • Logged in to Catalyst and provisioned a managed workflow project with a single CLI command (diagrid dev run).
  • Ran an order processing workflow that's using task chaining.
  • Inspected workflow execution state using the status endpoint and the Catalyst web console.
  • Recovered a workflow after the app crashed mid-run, resuming the same instance under an ID you chose.

Catalyst handled workflow state durability, activity orchestration, and retries automatically — no infrastructure to manage.

Next steps

Dapr & AI University · Learn this hands-onDapr Workflow: Use durable execution to build reliable distributed applicationsBuild reliable distributed applications with durable execution: task chaining, fan-out/fan-in, and error handling.Intermediate50 min.NET · Java · PythonStart the track