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.
- Python
- .NET
- JavaScript
- Java
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:
- Python
- .NET
- JavaScript
- Java
- macOS/Linux
- Windows
cd catalyst-quickstarts/workflow/python
cd catalyst-quickstarts\workflow\python
- macOS/Linux
- Windows
cd catalyst-quickstarts/workflow/csharp
cd catalyst-quickstarts\workflow\csharp
- macOS/Linux
- Windows
cd catalyst-quickstarts/workflow/javascript
cd catalyst-quickstarts\workflow\javascript
- macOS/Linux
- Windows
cd catalyst-quickstarts/workflow/java
cd catalyst-quickstarts\workflow\java
4. Install Dependencies
Install the dependencies for the workflow application.
- Python
- .NET
- JavaScript
- Java
Install Python dependencies:
uv sync --all-packages
Install .NET dependencies:
dotnet build
Install Node dependencies:
npm install
Install Maven dependencies:
mvn clean install
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.
- Python
- .NET
- JavaScript
- Java
Run the application:
uv run diagrid dev run -f workflow-quickstart.yaml --project workflow-quickstart --approve
Run the application:
diagrid dev run -f workflow-quickstart.yaml --project workflow-quickstart --approve
Run the application:
diagrid dev run -f workflow-quickstart.yaml --project workflow-quickstart --approve
Run the application:
diagrid dev run --project workflow-quickstart --id order-workflow --approve -- mvn spring-boot:run
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:
- macOS/Linux
- Windows
curl -i -X POST http://localhost:5001/workflow/start -H "Content-Type: application/json" -d '{"name":"Car", "quantity":2}'
Invoke-RestMethod -Method Post -Uri "http://localhost:5001/workflow/start" -ContentType "application/json" -Body '{"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:
- macOS/Linux
- Windows
export INSTANCE_ID=<YOUR_INSTANCE_ID>
$env: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.
- macOS/Linux
- Windows
curl -i -X GET http://localhost:5001/workflow/status/$INSTANCE_ID
Invoke-RestMethod -Method Get -Uri "http://localhost:5001/workflow/status/$env:INSTANCE_ID" | ConvertTo-Json -Depth 3
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.

7. Recover from a crash
- Python
- .NET
- JavaScript
- Java
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.
Durable execution earns its name when a process dies mid-run. This quickstart ships a second workflow for exactly that: CrashRecoveryWorkflow 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.
The JavaScript quickstart does not yet ship the crash-recovery workflow, so this step has no JavaScript walkthrough. Switch to the Python, .NET, or Java tab to try it, or continue to Clean Up.
Durable execution earns its name when a process dies mid-run. This quickstart ships a second workflow for exactly that: CrashRecoveryWorkflow 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.
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.
- macOS/Linux
- Windows
curl -i -X POST http://localhost:5001/crash/run -H "Content-Type: application/json" -d '{"id":"trip-42", "reference":"ABC123", "kill_after_seconds": 8}'
Invoke-RestMethod -Method Post -Uri "http://localhost:5001/crash/run" -ContentType "application/json" -Body '{"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:
- Python
- .NET
- JavaScript
- Java
== 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
== APP - order-workflow == Reservation trip-42 received for ABC123
== APP - order-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 == >>> crash: killing this process 8s into the run, as asked by kill_after_seconds
Not available for the JavaScript quickstart. See the note at the start of step 7.
== APP - order-workflow == Notification: Reservation trip-42 received for ABC123
== APP - order-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 == >>> crash: halting the JVM 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.
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.
- macOS/Linux
- Windows
curl -i -X POST http://localhost:5001/crash/kill
Invoke-RestMethod -Method Post -Uri "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:
- Python
- .NET
- JavaScript
- Java
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
diagrid dev run -f workflow-quickstart.yaml --project workflow-quickstart --approve
== APP - order-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 == Committed reservation ABC123. Confirmation code: BK-E0BEBD22
== APP - order-workflow == Reservation trip-42 has completed! Reservation ABC123 confirmed. Confirmation code: BK-E0BEBD22
Not available for the JavaScript quickstart. See the note at the start of step 7.
diagrid dev run --project workflow-quickstart --id order-workflow --approve -- mvn spring-boot:run
== APP - order-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 == Committed reservation ABC123. Confirmation code: BK-E0BEBD22
== APP - order-workflow == Notification: 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:
- macOS/Linux
- Windows
curl -i -X POST http://localhost:5001/crash/run -H "Content-Type: application/json" -d '{"id":"trip-42", "reference":"ABC123"}'
Invoke-RestMethod -Method Post -Uri "http://localhost:5001/crash/run" -ContentType "application/json" -Body '{"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.
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
statusendpoint 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
- Explore the Workflow SDK guides for building workflows from scratch, understanding workflow patterns, and resiliency.
- Try the Workflow Composer to scaffold workflow projects based on diagrams or try our Claude skills for Dapr to build entire Dapr workflow applications.