Tutorial

This comprehensive tutorial guides you through creating HTTP-based workflows in Elsa, covering all aspects of HTTP endpoint development from basic concepts to advanced patterns.

Overview

In this tutorial, you will learn how to:

  • Create HTTP endpoints that respond to GET, POST, PUT, and DELETE requests

  • Handle query parameters, headers, and request bodies

  • Parse and validate incoming data

  • Return appropriate HTTP responses with proper status codes

  • Implement error handling strategies

  • Test and debug HTTP workflows

By the end of this tutorial, you'll have a complete understanding of building production-ready HTTP workflows for RESTful API development.

Prerequisites

Before you start, ensure you have:

  • An Elsa Server project up and running

  • Elsa Studio installed and connected to your Elsa Server

  • Basic understanding of HTTP methods and REST principles

  • A tool for testing HTTP endpoints (Postman, curl, or similar)

New to Elsa?

If you haven't set up Elsa yet, check out our Getting Started guide and Docker Quickstart for the fastest way to get up and running.

Tutorial Scenario

We'll build a simple Task Management API with the following endpoints:

  • GET /workflows/tasks - List all tasks (with query parameters for filtering)

  • GET /workflows/tasks/{id} - Get a specific task by ID

  • POST /workflows/tasks - Create a new task

  • PUT /workflows/tasks/{id} - Update an existing task

  • DELETE /workflows/tasks/{id} - Delete a task

This scenario will demonstrate real-world patterns you'll use when building HTTP workflows.

Part 1: Creating a GET Endpoint with Query Parameters

Let's start by creating a workflow that lists tasks with optional filtering via query parameters.

1

Create the List Tasks Workflow

  1. Open Elsa Studio and navigate to Workflows

  2. Click Create Workflow

  3. Name it ListTasks

  4. Set the workflow as Published when ready

2

Add Required Activities

Add the following activities to your workflow:

  1. HTTP Endpoint - To receive the request

  2. Set Variable - To extract query parameters

  3. Set Variable - To create a filtered task list

  4. Write HTTP Response - To return the results

3

Create Workflow Variables

Create the following variables:

Name
Type
Storage

QueryData

ObjectDictionary

Workflow Instance

StatusFilter

string

Workflow Instance

Tasks

Object

Workflow Instance

4

Configure HTTP Endpoint

Configure the HTTP Endpoint activity:

Property
Value
Syntax

Path

tasks

Default

Supported Methods

GET

Default

5

Extract Query Parameters

Configure the first Set Variable activity to extract the status filter:

Property
Value
Syntax

Variable

StatusFilter

Default

Value

{{ Variables.QueryData.status ?? "all" }}

Liquid

This extracts the status query parameter (e.g., ?status=active) or defaults to "all".

6

Create Mock Task Data

Configure the second Set Variable activity to create sample task data:

Property
Value
Syntax

Variable

Tasks

Default

Value

See code below

C#

C# Expression:

7

Return Response

Configure the Write HTTP Response activity:

Property
Value
Syntax

Status Code

OK

Default

Content

Variables.Tasks

JavaScript

Content Type

application/json

Default

8

Test the Workflow

  1. Publish the workflow

  2. Test with different query parameters:

    • GET https://localhost:5001/workflows/tasks - Returns all tasks

    • GET https://localhost:5001/workflows/tasks?status=active - Returns only active tasks

    • GET https://localhost:5001/workflows/tasks?status=completed - Returns completed tasks

Part 2: Creating a GET Endpoint with Route Parameters

Now let's create a workflow that retrieves a specific task by ID using route parameters.

1

Create the Get Task Workflow

  1. Create a new workflow named GetTask

  2. This workflow will handle requests like GET /workflows/tasks/1

2

Create Variables

Name
Type
Storage

RouteData

ObjectDictionary

Workflow Instance

TaskId

string

Workflow Instance

Task

Object

Workflow Instance

3

Configure HTTP Endpoint

Property
Value
Syntax

Path

tasks/{id}

Default

Supported Methods

GET

Default

4

Extract Task ID

Add a Set Variable activity:

Property
Value
Syntax

Variable

TaskId

Default

Value

{{ Variables.RouteData.id }}

Liquid

5

Find Task

Add another Set Variable activity with branching logic:

Property
Value
Syntax

Variable

Task

Default

Value

See code below

C#

C# Expression:

6

Add Conditional Response

Add a Decision activity to check if the task was found:

Property
Value
Syntax

Condition

Variables.Task != null

C#

Connect two Write HTTP Response activities to the Decision outcomes:

For "True" outcome (Task Found):

Property
Value
Syntax

Status Code

OK

Default

Content

Variables.Task

JavaScript

Content Type

application/json

Default

For "False" outcome (Task Not Found):

Property
Value
Syntax

Status Code

NotFound

Default

Content

{"error": "Task not found", "taskId": "{{Variables.TaskId}}"}

Liquid

Content Type

application/json

Default

7

Test the Workflow

Test with different task IDs:

  • GET https://localhost:5001/workflows/tasks/1 - Returns task details (200 OK)

  • GET https://localhost:5001/workflows/tasks/999 - Returns error message (404 Not Found)

Part 3: Creating a POST Endpoint for Creating Resources

Let's create a workflow that handles POST requests to create new tasks.

1

Create the Create Task Workflow

Create a new workflow named CreateTask

2

Create Variables

Name
Type
Storage

RequestBody

Object

Workflow Instance

NewTask

Object

Workflow Instance

ValidationErrors

Object

Workflow Instance

3

Configure HTTP Endpoint

Property
Value
Syntax

Path

tasks

Default

Supported Methods

POST

Default

The HTTP Endpoint automatically parses JSON request bodies into the Parsed Content output.

4

Validate Request Body

Add a Set Variable activity to validate the input:

Property
Value
Syntax

Variable

ValidationErrors

Default

Value

See code below

C#

C# Expression:

5

Add Validation Decision

Add a Decision activity:

Property
Value
Syntax

Condition

Variables.ValidationErrors == null

C#

6

Create Task (Valid Input)

For the "True" outcome, add a Set Variable activity:

Property
Value
Syntax

Variable

NewTask

Default

Value

See code below

C#

C# Expression:

Then add a Write HTTP Response activity:

Property
Value
Syntax

Status Code

Created

Default

Content

Variables.NewTask

JavaScript

Content Type

application/json

Default

7

Return Validation Errors (Invalid Input)

For the "False" outcome, add a Write HTTP Response activity:

Property
Value
Syntax

Status Code

BadRequest

Default

Content

Variables.ValidationErrors

JavaScript

Content Type

application/json

Default

8

Test the Workflow

Test with valid data:

Test with invalid data:

Part 4: Creating a PUT Endpoint for Updates

Let's create a workflow that handles PUT requests to update existing tasks.

1

Create the Update Task Workflow

Create a new workflow named UpdateTask

2

Create Variables

Name
Type
Storage

RouteData

ObjectDictionary

Workflow Instance

RequestBody

Object

Workflow Instance

TaskId

string

Workflow Instance

ExistingTask

Object

Workflow Instance

UpdatedTask

Object

Workflow Instance

3

Configure HTTP Endpoint

Property
Value
Syntax

Path

tasks/{id}

Default

Supported Methods

PUT

Default

4

Extract Task ID

Add a Set Variable activity:

Property
Value
Syntax

Variable

TaskId

Default

Value

{{ Variables.RouteData.id }}

Liquid

5

Find Existing Task

Add a Set Variable activity:

Property
Value
Syntax

Variable

ExistingTask

Default

Value

See code below

C#

C# Expression:

6

Add Decision for Task Existence

Add a Decision activity:

Property
Value
Syntax

Condition

Variables.ExistingTask != null

C#

7

Update Task (If Found)

For the "True" outcome, add a Set Variable activity:

Property
Value
Syntax

Variable

UpdatedTask

Default

Value

See code below

C#

C# Expression:

Then add a Write HTTP Response activity:

Property
Value
Syntax

Status Code

OK

Default

Content

Variables.UpdatedTask

JavaScript

Content Type

application/json

Default

8

Return Not Found (If Task Doesn't Exist)

For the "False" outcome, add a Write HTTP Response activity:

Property
Value
Syntax

Status Code

NotFound

Default

Content

{"error": "Task not found", "taskId": "{{Variables.TaskId}}"}

Liquid

Content Type

application/json

Default

9

Test the Workflow

Test updating an existing task:

Test updating a non-existent task:

Part 5: Creating a DELETE Endpoint

Let's complete our CRUD operations with a DELETE endpoint.

1

Create the Delete Task Workflow

Create a new workflow named DeleteTask

2

Create Variables

Name
Type
Storage

RouteData

ObjectDictionary

Workflow Instance

TaskId

string

Workflow Instance

TaskExists

bool

Workflow Instance

3

Configure HTTP Endpoint

Property
Value
Syntax

Path

tasks/{id}

Default

Supported Methods

DELETE

Default

4

Extract and Validate Task ID

Add a Set Variable activity to extract the ID:

Property
Value
Syntax

Variable

TaskId

Default

Value

{{ Variables.RouteData.id }}

Liquid

Then add another Set Variable activity to check if task exists:

Property
Value
Syntax

Variable

TaskExists

Default

Value

See code below

C#

C# Expression:

5

Add Decision

Add a Decision activity:

Property
Value
Syntax

Condition

Variables.TaskExists

C#

6

Return Success (If Deleted)

For the "True" outcome, add a Write HTTP Response activity:

Property
Value
Syntax

Status Code

NoContent

Default

HTTP 204 No Content

The 204 status code indicates successful deletion without returning any content in the response body. This is the standard practice for DELETE operations.

7

Return Not Found (If Task Doesn't Exist)

For the "False" outcome, add a Write HTTP Response activity:

Property
Value
Syntax

Status Code

NotFound

Default

Content

{"error": "Task not found", "taskId": "{{Variables.TaskId}}"}

Liquid

Content Type

application/json

Default

8

Test the Workflow

Test deleting an existing task:

Test deleting a non-existent task:

Part 6: Working with Headers

Learn how to read and set HTTP headers in your workflows.

Reading Request Headers

To access request headers, use the HTTP Endpoint activity's Headers output:

1

Create Variables

Name
Type
Storage

Headers

ObjectDictionary

Workflow Instance

AuthToken

string

Workflow Instance

UserAgent

string

Workflow Instance

2

Configure HTTP Endpoint

Property
Value

Headers

Headers

3

Extract Header Values

Add Set Variable activities to extract specific headers:

For Authorization Header:

Property
Value
Syntax

Variable

AuthToken

Default

Value

{{ Variables.Headers.Authorization ?? "No token provided" }}

Liquid

For User-Agent Header:

Property
Value
Syntax

Variable

UserAgent

Default

Value

{{ Variables.Headers["User-Agent"] ?? "Unknown" }}

Liquid

Setting Response Headers

To set custom response headers, configure the Write HTTP Response activity:

Add custom headers:

Name
Value
Syntax

X-Request-Id

{{guid()}}

Liquid

X-Response-Time

`{{now

date: "%Y-%m-%d %H:%M:%S"}}`

Cache-Control

no-cache, no-store, must-revalidate

Default

X-Api-Version

v3

Default

Part 7: Error Handling Strategies

Implement robust error handling to make your workflows production-ready.

Pattern 1: Try-Catch with Fault Activity

Create a workflow that handles exceptions gracefully:

1

Use Fault Activity

Wrap risky operations in a Fault activity to catch exceptions:

  1. Add a Fault activity

  2. Inside the Fault activity, add activities that might fail (e.g., HTTP Request to external API)

  3. Connect the Faulted outcome to error handling logic

2

Handle Fault

Create a Set Variable activity to process the error:

Property
Value
Syntax

Variable

ErrorResponse

Default

Value

See code below

C#

C# Expression:

3

Return Error Response

Add a Write HTTP Response activity:

Property
Value
Syntax

Status Code

InternalServerError

Default

Content

Variables.ErrorResponse

JavaScript

Content Type

application/json

Default

Pattern 2: Validation and Early Returns

Validate input early and return appropriate error responses:

Pattern 3: Custom Error Status Codes

Use appropriate HTTP status codes for different error scenarios:

Status Code
Use Case
Example

400 Bad Request

Invalid input data

Missing required fields, invalid format

401 Unauthorized

Missing or invalid authentication

No auth token provided

403 Forbidden

Insufficient permissions

User not allowed to perform action

404 Not Found

Resource doesn't exist

Task ID not found

409 Conflict

Resource state conflict

Task already exists

422 Unprocessable Entity

Semantic validation errors

Valid format but business rule violation

429 Too Many Requests

Rate limit exceeded

Too many API calls

500 Internal Server Error

Unexpected server errors

Database connection failure

503 Service Unavailable

Temporary service issues

Downstream service unavailable

Part 8: Advanced Request/Response Patterns

Content Negotiation

Handle different content types based on request headers:

Conceptual Example

The following demonstrates the concept of content negotiation. In a real implementation, you would need to:

  • Implement XML/CSV serialization methods based on your needs

  • Use libraries like System.Xml.Serialization or CsvHelper

  • Configure appropriate Content-Type headers in Write HTTP Response activity

CORS Headers

Enable Cross-Origin Resource Sharing (CORS) for browser-based clients:

Name
Value

Access-Control-Allow-Origin

https://yourdomain.com

Access-Control-Allow-Methods

GET, POST, PUT, DELETE, OPTIONS

Access-Control-Allow-Headers

Content-Type, Authorization

Access-Control-Max-Age

3600

Pagination

Implement pagination for list endpoints:

Rate Limiting

Track and limit request rates per client:

Part 9: Testing Your HTTP Workflows

Using Postman

  1. Create a Collection: Organize all your workflow endpoints

  2. Set Environment Variables: Configure base URL, auth tokens

  3. Write Tests: Add test scripts to validate responses

Example Postman test script:

Using cURL

Test your endpoints from the command line:

Using HTTP Files (REST Client)

Create a .http file for testing (replace {{baseUrl}} with your server URL):

Environment Variables

Use variables in .http files to easily switch between environments:

  • Development: @baseUrl = https://localhost:5001

  • Staging: @baseUrl = https://staging.example.com

  • Production: @baseUrl = https://api.example.com

Automated Testing with xUnit

Create integration tests for your workflows:

Part 10: Debugging and Troubleshooting

Using Elsa Studio for Debugging

  1. Navigate to Workflow Instances: View all executions of your workflow

  2. Inspect Activity Execution: See inputs/outputs for each activity

  3. Check Journal Entries: View the execution timeline

  4. Review Variables: Inspect variable values at each step

Common Issues and Solutions

Issue: 404 Not Found

Problem: Workflow endpoint not responding

Solutions:

  • Verify the workflow is Published

  • Check that "Trigger Workflow" is enabled on HTTP Endpoint

  • Ensure the path doesn't conflict with other routes

  • Verify Elsa Server is running and configured correctly

Issue: Request Body is Null

Problem: Cannot read POST/PUT request body

Solutions:

  • Set Content-Type: application/json header

  • Ensure JSON is valid

  • Use HTTP Endpoint's "Parsed Content" output

  • Check that body isn't consumed elsewhere in the pipeline

Issue: Headers Not Available

Problem: Cannot read request headers

Solutions:

  • Use HTTP Endpoint's "Headers" output variable

  • Check header names are case-insensitive

  • Verify headers are sent with the request

Issue: CORS Errors

Problem: Browser blocks requests from different origin

Solutions:

  • Add CORS headers to Write HTTP Response activity

  • Handle OPTIONS preflight requests

  • Configure Elsa Server CORS policy

Example CORS workflow configuration:

HTTP Endpoint Activity:

Write HTTP Response Activity:

Enabling Detailed Logging

Configure logging in your Elsa Server's appsettings.json:

Best Practices

1. Use Consistent Response Formats

Always return JSON in a consistent structure:

2. Validate All Inputs

Never trust client input. Always validate:

  • Required fields are present

  • Data types are correct

  • Values are within expected ranges

  • Formats match requirements (email, URL, etc.)

3. Use Appropriate HTTP Methods

  • GET: Retrieve resources (idempotent, no side effects)

  • POST: Create resources (non-idempotent)

  • PUT: Update entire resources (idempotent)

  • PATCH: Partial updates (may be idempotent)

  • DELETE: Remove resources (idempotent)

4. Return Proper Status Codes

Use semantic HTTP status codes to communicate results clearly.

5. Implement Security

  • Validate authentication tokens

  • Implement authorization checks

  • Sanitize inputs to prevent injection attacks

  • Use HTTPS in production

  • Rate limit requests

6. Version Your APIs

Include version in the path:

  • /workflows/v1/tasks

  • /workflows/v2/tasks

Or use headers:

  • Accept: application/vnd.myapi.v1+json

7. Document Your Endpoints

Provide clear documentation for each endpoint:

  • Purpose and description

  • Request format and parameters

  • Response format and status codes

  • Example requests and responses

  • Error scenarios

8. Handle Timeouts

For long-running operations:

  • Return 202 Accepted immediately

  • Process asynchronously

  • Provide status endpoint to check progress

9. Use Workflow Variables Wisely

  • Name variables descriptively

  • Choose appropriate storage (Workflow Instance vs Activity)

  • Clean up large variables when no longer needed

10. Monitor and Log

  • Log important events

  • Track performance metrics

  • Monitor error rates

  • Set up alerts for critical issues

Real-World Example: Complete Task API

Here's a complete workflow combining all the patterns we've learned:

Workflow: Create Task with Full Validation

This workflow demonstrates:

  • Request body parsing

  • Comprehensive validation

  • Authentication check

  • Error handling

  • Proper response codes

  • Header management

Variables:

  • Headers (ObjectDictionary)

  • RequestBody (Object)

  • AuthToken (string)

  • ValidationResult (Object)

  • NewTask (Object)

  • IsAuthenticated (bool)

Activities Flow:

  1. HTTP Endpoint (POST /workflows/tasks)

    • Outputs: Headers, RequestBody

  2. Extract Auth Token

    • AuthToken = Headers.Authorization

  3. Validate Authentication

    • Check if token is valid

    • Branch: Authenticated / Unauthorized

  4. Validate Request Body (if authenticated)

    • Check required fields

    • Validate formats

    • Check business rules

  5. Decision: Valid Input?

    • True: Create task

    • False: Return validation errors

  6. Create Task (if valid)

    • Generate ID

    • Set timestamps

    • Prepare response

  7. Return Response

    • 201 Created: With Location header

    • 400 Bad Request: With validation errors

    • 401 Unauthorized: If auth fails

Summary

Congratulations! You've completed the comprehensive HTTP Workflows tutorial. You now know how to:

  • ✅ Create RESTful endpoints for all HTTP methods (GET, POST, PUT, DELETE)

  • ✅ Handle route parameters and query strings

  • ✅ Parse and validate request bodies

  • ✅ Read and set HTTP headers

  • ✅ Implement proper error handling

  • ✅ Return appropriate HTTP status codes

  • ✅ Test workflows using various tools

  • ✅ Debug and troubleshoot issues

  • ✅ Apply best practices for production-ready APIs

Next Steps

Now that you've mastered HTTP workflows, explore these advanced topics:

Resources

Feedback

Found an issue or have suggestions for improving this tutorial? Please open an issue on our GitHub repository.

Happy workflow building! 🚀

Last updated