---
title: "Example workflows"
canonical_url: "https://docs.getdx.com/self-service/examples/"
md_url: "https://docs.getdx.com/self-service/examples.md"
last_updated: "2026-05-14"
---

# Example workflows
> **Note:** This article details three self-service workflow examples. See the [overview article](https://docs.getdx.com/self-service/overview/) to explore execution types, parameters, variables, and the workflow events API.

- **[Running CI](#running-ci)** — Event-driven workflow that triggers a CI pipeline and receives progress updates.
- **[Software templates](#software-templates)** — Event-driven workflow that creates a GitHub repository from a template.
- **[Freeze deployments](#freeze-deployments)** — Simple workflow that freezes deployments with a single HTTP request.

## Running CI

Use an **event-driven** workflow to trigger a run in an external CI or pipeline (GitHub Actions, GitLab CI, Argo Workflows, etc.) and report progress and final status back to DX. The workflow run stays in an "In Progress" state until the pipeline calls the DX API to change its status.

| Aspect         | Recommendation                                                                                   |
| -------------- | ------------------------------------------------------------------------------------------------ |
| Execution type | Event-driven                                                                                     |
| Scope          | Global or entity-specific (e.g. pass {{entity.identifier}} to the pipeline) |
| Parameters     | Whatever the pipeline needs: branch, environment, ref, custom inputs                             |

### How it works

1. The user triggers the DX workflow (from Self-Service or from an entity).
2. DX sends the configured HTTP request to your CI/pipeline (e.g. GitHub Actions workflow_dispatch).
3. The request includes {{run.id}} so the pipeline can call back to DX.
4. The pipeline runs and calls [workflow events API endpoints](https://docs.getdx.com/self-service/overview/#api-endpoints-for-workflow-events), including: [workflowRuns.postMessage](https://docs.getdx.com/webapi/methods/workflowRuns.postMessage/), [workflowRuns.addLink](https://docs.getdx.com/webapi/methods/workflowRuns.addLink/), and [workflowRuns.changeStatus](https://docs.getdx.com/webapi/methods/workflowRuns.changeStatus/).
5. Messages, links, and status appear on the Workflow Run Detail page. When the pipeline calls `workflowRuns.changeStatus`, the DX run completes.

### Prerequisites

- A [web API token](https://app.getdx.com/admin/webapi) with `workflows:write` scope, stored as a secret in your CI system (e.g. `DX_WORKFLOW_API_KEY`).
- Your CI workflow or job must accept the DX workflow run ID (and any other DX parameters) as inputs and use them in `curl` calls to the DX API.

### Example: GitHub Actions

**1. Repository secret**

In the GitHub repo, navigate to Settings > Security > Actions > **Repository secrets** and add:

- Name: `DX_WORKFLOW_API_KEY`
- Secret: your DX API token with the `workflows:write` scope

**2. Workflow file** (e.g. `.github/workflows/dx_workflow_example.yml`)

The workflow must accept `dx_workflow_run_id` and pass it to the DX API base URL. Example pattern:

<details>
<summary>
YAML file contents
</summary>



```yaml
name: DX Workflow Example

on:
  workflow_dispatch:
    inputs:
      dx_workflow_run_id:
        type: string
        required: true
        description: "The ID of the DX Workflow run that triggered this workflow."
      # Add other inputs that DX will pass (e.g. branch, environment).
      favorite_number:
        type: number
        required: true
        description: "Example parameter from DX"

env:
  DX_API_BASE_URL: https://api.getdx.com
  DX_WORKFLOW_API_KEY: ${{ secrets.DX_WORKFLOW_API_KEY }}
  DX_WORKFLOW_RUN_ID: ${{ github.event.inputs.dx_workflow_run_id }}

jobs:
  run-and-report:
    runs-on: ubuntu-latest
    steps:
      - name: Add link to DX Workflow
        run: |
          WORKFLOW_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
          curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $DX_WORKFLOW_API_KEY" \
            -d "{\"workflow_run_id\": \"$DX_WORKFLOW_RUN_ID\", \"link\": { \"url\": \"$WORKFLOW_URL\", \"label\": \"GitHub Actions Workflow\", \"icon\": \"github\" }}" \
            "${DX_API_BASE_URL}/workflowRuns.addLink"

      - name: Post start message
        run: |
          curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $DX_WORKFLOW_API_KEY" \
            -d "{\"workflow_run_id\": \"$DX_WORKFLOW_RUN_ID\", \"message\": \"Starting the GHA workflow...\"}" \
            "${DX_API_BASE_URL}/workflowRuns.postMessage"

      - name: Run your steps
        run: |
          echo "Do your CI work here..."

      - name: Mark DX Workflow as complete
        if: success()
        run: |
          curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $DX_WORKFLOW_API_KEY" \
            -d "{\"workflow_run_id\": \"$DX_WORKFLOW_RUN_ID\", \"status\": \"SUCCEEDED\"}" \
            "${DX_API_BASE_URL}/workflowRuns.changeStatus"

      - name: Mark DX Workflow as failed
        if: failure()
        run: |
          curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $DX_WORKFLOW_API_KEY" \
            -d "{\"workflow_run_id\": \"$DX_WORKFLOW_RUN_ID\", \"status\": \"FAILED\"}" \
            "${DX_API_BASE_URL}/workflowRuns.changeStatus"
```



</details>

**3. DX workflow configuration**

- **Execution type:** Event driven
- **Parameters:** Define any inputs you want to pass (e.g. Favorite number, identifier `favorite_number`, type integer, required).
- **HTTP request**
  - Method: `POST`
  - URL: `https://api.github.com/repos/<your-org>/<your-repo>/actions/workflows/dx_workflow_example.yml/dispatches`
  - Headers: `Content-Type: application/json`, `Accept: application/json`, `Authorization: Bearer <github-pat>`
  - Body (use DX variables so the run ID and parameters are passed):

```plain
{
  "ref": "master",
  "inputs": {
    "dx_workflow_run_id": "{{run.id}}",
    "favorite_number": "{{data.favorite_number}}"
  }
}
```

Use a [GitHub Personal Access Token](https://github.com/settings/personal-access-tokens) that has `Read and Write` permissions on repository actions and `Read-Only` permissions on metadata. When the DX workflow is triggered, it dispatches the GitHub Actions workflow. As the job runs, it posts messages, adds a link, and sets the DX run status.

## Software templates

Use an **event-driven** workflow to create a new GitHub repository from a template (e.g. cookiecutter). An external template service generates the code, creates the repo, and reports progress back to DX.

| Aspect         | Recommendation                                                                                                                                  |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Execution type | Event-driven                                                                                                                                    |
| Scope          | Global or entity-specific                                                                                                                       |
| Parameters     | Template type (select), GitHub organization (string), repository name (string), project name (string, optional), description (string, optional) |

### How it works

1. The user triggers the DX workflow and provides template type, org/repo name, and optional project details.
2. DX sends the configured HTTP request to your template service.
3. The template service generates code from the template, creates the GitHub repo, and pushes the initial commit.
4. The service calls the [workflow events API](https://docs.getdx.com/self-service/overview/#api-endpoints-for-workflow-events) to post progress messages, add a link to the new repo, and set the final status.
5. Messages, links, and status appear on the Workflow Run Detail page.

### Prerequisites

- **Template service** — A service that exposes an HTTP endpoint (e.g. `POST /api/service`), generates code from a template, creates the GitHub repo, and pushes the initial commit. It must call the DX web API with a token with `workflows:write` scope.
- **DX API key** — A [web API token](https://app.getdx.com/admin/webapi) with `workflows:write` scope, accessible to the template service so it can post messages, add links, and change workflow run status.
- **GitHub token** — Used by the template service to create the repo and push; not used by DX.

DX provides an [example template service](https://github.com/get-dx/software-template-example) you can use as a starting point. It includes a working implementation of the HTTP endpoint, DX API integration, and GitHub repo creation described above.

### DX configuration

In [Self-service](https://app.getdx.com/self-service), click **Create workflow**.

- **Execution type:** Event-driven
- **Parameters:** Define inputs for the template type, GitHub organization, repository name, and any optional fields (project name, description).
- **HTTP request**
  - Method: `POST`
  - URL: Your template service endpoint (e.g. `http://YOUR_SERVICE_HOST:8000/api/service`)
  - Headers: `Content-Type: application/json`
  - Body (use DX variables so the run ID and user inputs are sent to the service):



```json
{
  "dx_workflow_run_id": "{{run.id}}",
  "template_type": "{{data.template_type}}",
  "github_organization": "{{data.github_organization}}",
  "github_repository": "{{data.github_repository}}",
  "project_name": "{{data.project_name}}",
  "description": "{{data.description}}"
}
```



Including `{{run.id}}` is critical: the template service must receive this ID to send status updates to the correct workflow run.

As an alternative to one workflow with a template selector, you can create one workflow per template (e.g. "Create Python Package") with only the parameters that template needs, hardcoding the `template_type` in the HTTP body.

<details>
<summary>
Troubleshooting
</summary>

| Problem                      | What to check                                                                                                                                                 |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DX shows "Status code: null" | DX cannot reach the template service. Verify the service is running, use the correct URL (IP, host.docker.internal, or tunnel), and check firewalls.          |
| No status updates in DX      | Template service cannot reach DX. Verify the DX API key is set in the service environment with `workflows:write` scope and check service logs for API errors. |
| Repository created but empty | GitHub push failed. Verify the GitHub token used by the service has `repo` and `workflow` scopes; check service logs.                                         |
| "Repository already exists"  | The repo name is already taken on GitHub. Use a different name or delete the existing repo.                                                                   |

</details>

## Freeze deployments

Use a **simple** workflow to freeze (lock) deployments for a service during an incident or release. A single HTTP request to your deployment or Git provider does the work; DX marks the run as Success or Failed based on the response status code.

| Aspect         | Recommendation                                                                                                                                    |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Execution type | Simple                                                                                                                                            |
| Scope          | Entity-specific (e.g. Service) so you can pass {{entity.identifier}} or {{entity.name}} to the provider |
| Parameters     | Optional: reason (string), duration (string or select)                                                                                            |

### How it works

1. The user triggers the workflow from the Self-Service page or from a service's Entity Detail page.
2. DX sends one HTTP request (e.g. POST) to your provider's freeze/lock endpoint.
3. The request includes the entity identifier (or name) so the provider knows which service to freeze.
4. If the provider returns a 2XX response, DX marks the workflow run as Success; otherwise Failed. The run completes as soon as the response is received.

### Configuration

**Parameters (optional)** — You can add a "Reason" (string) or "Duration" (string/select) and pass them in the body so the provider or your team can see why deployments were frozen.

**HTTP request**

- **Method:** Usually `POST` (or whatever your provider's API uses for "freeze" or "lock deployment").
- **URL:** The provider's API endpoint (e.g. GitLab project freeze, or your own API that wraps the provider).
- **Headers:** `Content-Type: application/json`, plus an `Authorization` header with a token that has permission to freeze deployments.
- **Body:** Include the entity and any optional parameters. Example (GitLab-style):



```json
{
  "project_id": "{{entity.identifier}}",
  "reason": "{{data.reason}}"
}
```



If your provider uses path parameters, put the entity identifier in the URL (e.g. `/projects/{{entity.identifier}}/freeze`).

### Example: entity-specific freeze

- **Scope:** Entity-specific, applicable to entity type **Service**.
- **Who can run:** For a freeze workflow you may want **Entity owners only** so only users or teams who own the service can trigger it. See [Who can run this workflow](https://docs.getdx.com/self-service/overview/#who-can-run-this-workflow) in the overview.
- **Parameters:** Reason (identifier `reason`, type string, optional).
- **HTTP:** POST to your provider's freeze endpoint with `Authorization: Bearer <token>`, body `{ "project_id": "{{entity.identifier}}", "reason": "{{data.reason}}" }`.

Only one request is sent; no callback to DX is needed. For an event-driven "unfreeze after N minutes" flow, you can use a separate workflow or an external scheduler that calls the provider's "unfreeze" API.
---

## Sitemap

[Overview of all docs pages](/llms.txt)
