Example workflows

Note: This article details three self-service workflow examples. See the overview article to explore execution types, parameters, variables, and the workflow events API.

  • Running CI — Event-driven workflow that triggers a CI pipeline and receives progress updates.
  • Software templates — Event-driven workflow that creates a GitHub repository from a template.
  • 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, including: workflowRuns.postMessage, workflowRuns.addLink, and 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 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:

YAML file contents
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"

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):
{
  "ref": "master",
  "inputs": {
    "dx_workflow_run_id": "{{run.id}}",
    "favorite_number": "{{data.favorite_number}}"
  }
}

Use a GitHub Personal Access Token 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 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 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 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, 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):
{
  "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.

Troubleshooting
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.

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):
{
  "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 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.