# Get Step Types

GET https://services.leadconnectorhq.com/workflows/step-types

**Limited availability.** The workflow API is currently enabled for selected sub-accounts only; any other sub-account receives 403 `WORKFLOW_API_NOT_ENABLED_FOR_LOCATION`.

Browse or fetch the action and trigger schemas available to a sub-account, so a definition can be built against real field names before saving.

Every response is paged and every entry carries its full field schema, including the `assetReferences` that say which fields must hold IDs of assets that already exist. Default page size is 20.

Narrow with `types`, which matches on the step type **key** and matches partially — `email` returns `send_email` as well as `email`. Use `query` instead to match names and descriptions too. Page with `limit` and `skip`.

## Authentication

Send `Authorization: Bearer <token>` using:

- **Sub-Account token: OAuth access token or Private Integration Token.** Scopes: `workflows.readonly`

## Parameters

### Header Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `Version` | string | Yes | API Version. Send `v3`. |

### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `locationId` | string | Yes | Sub-account (location) ID the workflow belongs to |
| `types` | string | No | Comma-separated step type filters (max 50 terms). Each term matches any step type whose **key** contains it, so `email` returns `send_email` as well as `email`, and an exact key still matches itself. Matching looks only at the type key — not the name or description — so a step merely described as emailing something will not appear.  A term matching nothing is reported in `notFound`. Results are paged like any other request. |
| `kind` | string | No | Restrict results to actions or triggers |
| `query` | string | No | Free-text match over type, name and description — broader than `types`, which matches the type key only. Ignored when `types` is supplied. |
| `limit` | number | No | Page size. Kept small by default because every entry carries its full field schema. |
| `skip` | number | No | Number of results to skip before the page starts |

## OpenAPI Specification

```json
{
  "openapi": "3.0.0",
  "info": {
    "title": "LeadConnector API",
    "version": "v3"
  },
  "servers": [
    {
      "url": "https://services.leadconnectorhq.com"
    }
  ],
  "paths": {
    "/workflows/step-types": {
      "get": {
        "summary": "Get Step Types",
        "operationId": "getStepTypes",
        "description": "**Limited availability.** The workflow API is currently enabled for selected sub-accounts only; any other sub-account receives 403 `WORKFLOW_API_NOT_ENABLED_FOR_LOCATION`.\n\nBrowse or fetch the action and trigger schemas available to a sub-account, so a definition can be built against real field names before saving.\n\nEvery response is paged and every entry carries its full field schema, including the `assetReferences` that say which fields must hold IDs of assets that already exist. Default page size is 20.\n\nNarrow with `types`, which matches on the step type **key** and matches partially — `email` returns `send_email` as well as `email`. Use `query` instead to match names and descriptions too. Page with `limit` and `skip`.",
        "parameters": [
          {
            "name": "Version",
            "in": "header",
            "description": "API Version. Send `v3`.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "v3"
              ]
            }
          },
          {
            "name": "locationId",
            "required": true,
            "in": "query",
            "description": "Sub-account (location) ID the workflow belongs to",
            "schema": {
              "example": "ve9EPM428h8vShlRW1KT",
              "type": "string"
            }
          },
          {
            "name": "types",
            "required": false,
            "in": "query",
            "description": "Comma-separated step type filters (max 50 terms). Each term matches any step type whose **key** contains it, so `email` returns `send_email` as well as `email`, and an exact key still matches itself. Matching looks only at the type key — not the name or description — so a step merely described as emailing something will not appear.\n\nA term matching nothing is reported in `notFound`. Results are paged like any other request.",
            "schema": {
              "example": "email,add_contact_tag",
              "type": "string"
            }
          },
          {
            "name": "kind",
            "required": false,
            "in": "query",
            "description": "Restrict results to actions or triggers",
            "schema": {
              "example": "action",
              "enum": [
                "action",
                "trigger"
              ],
              "type": "string"
            }
          },
          {
            "name": "query",
            "required": false,
            "in": "query",
            "description": "Free-text match over type, name and description — broader than `types`, which matches the type key only. Ignored when `types` is supplied.",
            "schema": {
              "example": "tag",
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Page size. Kept small by default because every entry carries its full field schema.",
            "schema": {
              "default": 20,
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "skip",
            "required": false,
            "in": "query",
            "description": "Number of results to skip before the page starts",
            "schema": {
              "default": 0,
              "example": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StepTypesResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BadRequestDTO"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnauthorizedDTO"
                }
              }
            }
          },
          "403": {
            "description": "The workflow API is not enabled for this sub-account (code `WORKFLOW_API_NOT_ENABLED_FOR_LOCATION`). Not a token or scope failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowApiNotEnabledDto"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowDefinitionUnprocessableDto"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Two limits apply, and either can refuse a request. The integration (OAuth app or private integration) has a daily quota and a 10-second burst limit for this sub-account, reported in the `X-RateLimit-*` headers; for private integrations the daily quota is shared by every private integration token of the sub-account. The sub-account also has its own budget per 10-second window, shared by every token used for it, with reads (get step types, get definition, search) and writes (validate, create, update, change status, delete) counted separately. No header reports the sub-account budget, so a 429 while `X-RateLimit-Remaining` and `X-RateLimit-Daily-Remaining` are both above 0 means that budget is spent. `Retry-After` is not sent: if `X-RateLimit-Daily-Remaining` is 0, wait `X-RateLimit-Daily-Reset` milliseconds; otherwise wait 10 seconds, then retry.",
            "headers": {
              "X-RateLimit-Max": {
                "description": "The integration's requests per burst window for this sub-account",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "The integration's requests left in the current burst window",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Interval-Milliseconds": {
                "description": "Length of the burst window",
                "schema": {
                  "type": "integer",
                  "example": 10000
                }
              },
              "X-RateLimit-Limit-Daily": {
                "description": "The integration's daily request quota for this sub-account",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Daily-Remaining": {
                "description": "Requests left in the daily quota",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Daily-Reset": {
                "description": "Milliseconds until the daily quota resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowRateLimitedDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "workflows.readonly"
            ]
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "name": "Authorization",
        "in": "header",
        "description": "Use the Access Token generated with user type as Sub-Account (OR) Private Integration Token of Sub-Account.",
        "type": "http"
      },
      "Location-Access": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "name": "Authorization",
        "in": "header",
        "description": "Use the Access Token generated with user type as Sub-Account (OR) Private Integration Token of Sub-Account.",
        "type": "http"
      },
      "Location-Access-Only": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "name": "Authorization",
        "in": "header",
        "description": "Use the Access Token generated with user type as Sub-Account.",
        "type": "http"
      },
      "Agency-Access": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "name": "Authorization",
        "in": "header",
        "description": "Use the Access Token generated with user type as Agency (OR) Private Integration Token of Agency.",
        "type": "http"
      },
      "Agency-Access-Only": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "name": "Authorization",
        "in": "header",
        "description": "Use the Access Token generated with user type as Agency.",
        "type": "http"
      }
    },
    "schemas": {
      "StepTypesResponseDto": {
        "type": "object",
        "properties": {
          "locationId": {
            "type": "string",
            "description": "Sub-account the catalog was scoped to",
            "example": "ve9EPM428h8vShlRW1KT"
          },
          "total": {
            "type": "number",
            "description": "Total matching step types before paging",
            "example": 1440
          },
          "count": {
            "type": "number",
            "description": "Number returned in this response",
            "example": 20
          },
          "skip": {
            "type": "number",
            "description": "The `skip` this page started at — echoed back so a caller paging through can tell which slice it received without tracking the request.",
            "example": 0
          },
          "stepTypes": {
            "description": "The step types",
            "example": [
              {
                "kind": "action",
                "type": "add_contact_tag",
                "name": "Add Contact Tag",
                "provider": "native"
              }
            ],
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "description": "Whether this is an action or a trigger",
                  "example": "action"
                },
                "type": {
                  "type": "string",
                  "description": "Type key to use in the definition",
                  "example": "add_contact_tag"
                },
                "name": {
                  "type": "string",
                  "description": "Display name",
                  "example": "Add Contact Tag"
                },
                "description": {
                  "type": "string",
                  "description": "What the step does",
                  "example": "Adds one or more tags to the contact"
                },
                "category": {
                  "type": "string",
                  "description": "Grouping shown in the builder",
                  "example": "Contact"
                },
                "provider": {
                  "type": "string",
                  "description": "Whether the step is native or from a marketplace app",
                  "example": "native"
                },
                "fields": {
                  "description": "Configurable fields. Returned in detail mode only.",
                  "example": [
                    {
                      "field": "tags",
                      "fieldType": "array",
                      "required": true
                    }
                  ],
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "field": {
                        "type": "string",
                        "description": "Attribute name inside `attributes`",
                        "example": "tags"
                      },
                      "fieldType": {
                        "type": "string",
                        "description": "Value type",
                        "example": "array"
                      },
                      "required": {
                        "type": "boolean",
                        "description": "Whether the step will not save without it",
                        "example": true
                      },
                      "requiredEnforcement": {
                        "type": "string",
                        "description": "Where a `required: true` obligation is actually enforced. Present only when it is `builder-only`, meaning the builder makes the omission unreachable but validation here does NOT reject it — the value is generated client-side and may belong in `__customInputFields__` rather than under this field name. A successful validate or save is not evidence the field was present. Ordinary fields are unannotated.",
                        "enum": [
                          "builder-and-ir",
                          "builder-only"
                        ],
                        "example": "builder-only"
                      },
                      "itemsType": {
                        "type": "string",
                        "description": "Element type when `fieldType` is `array`",
                        "example": "string"
                      },
                      "title": {
                        "type": "string",
                        "description": "Human label",
                        "example": "Tags"
                      },
                      "helpText": {
                        "type": "string",
                        "description": "Guidance shown next to the field",
                        "example": "Shown beneath the field in the builder"
                      },
                      "options": {
                        "description": "Allowed values, when constrained",
                        "example": [
                          {
                            "label": "Published",
                            "value": "published"
                          }
                        ],
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "assetType": {
                        "type": "string",
                        "description": "Set when this field must hold the ID of an existing asset of this type",
                        "example": "pipeline"
                      },
                      "hasDynamicOptions": {
                        "type": "boolean",
                        "description": "True when valid values must be looked up rather than hard-coded",
                        "example": true
                      }
                    },
                    "required": [
                      "field",
                      "fieldType",
                      "required"
                    ],
                    "title": "StepTypeFieldDto"
                  }
                },
                "assetReferences": {
                  "description": "Assets this step references. Returned in detail mode only.",
                  "example": [
                    {
                      "path": "tags",
                      "assetType": "TAG"
                    }
                  ],
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "path": {
                        "type": "string",
                        "description": "Where the ID lives in the definition",
                        "example": "attributes.pipeline_id"
                      },
                      "assetType": {
                        "type": "string",
                        "description": "Asset type the ID must refer to",
                        "example": "pipeline"
                      }
                    },
                    "required": [
                      "path",
                      "assetType"
                    ],
                    "title": "StepTypeAssetReferenceDto"
                  }
                },
                "triggerFilters": {
                  "type": "object",
                  "description": "Triggers only. `definitions` is the filter catalogue: one entry per configurable filter, with its `field`, `label`, `fieldType`, `required`, allowed `operators` and `valueSource`. `filtersComplete: false` means that list is partial — do not treat it as exhaustive. For marketplace triggers `feedNormalization: \"incomplete\"` means the same thing: `definitions` is a lower bound, because the app declared filters this projection could not normalize. `incompleteFields` names filters with no derivable operators, which cannot be authored.",
                  "additionalProperties": true,
                  "example": {
                    "filtersComplete": true,
                    "definitions": [
                      {
                        "field": "team_id",
                        "label": "Workspace",
                        "fieldType": "select",
                        "required": true,
                        "operators": [
                          {
                            "value": "eq",
                            "label": "is",
                            "requiresValue": true
                          }
                        ],
                        "valueSource": {
                          "kind": "dynamic"
                        }
                      }
                    ]
                  }
                }
              },
              "required": [
                "kind",
                "type",
                "name"
              ],
              "title": "StepTypeDto"
            }
          },
          "notFound": {
            "description": "`types` terms that matched no step type key",
            "example": [
              {
                "type": "made_up_type"
              }
            ],
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "description": "A `types` term that matched no step type key. Terms match partially, so this means nothing contained it — not that an exact key is missing.",
                  "example": "send_smss"
                }
              },
              "required": [
                "type"
              ],
              "title": "StepTypeNotFoundDto"
            }
          },
          "warnings": {
            "description": "Non-blocking findings",
            "example": [
              {
                "path": "actions[4]",
                "message": "Step has no incoming edges and may be unreachable."
              }
            ],
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string",
                  "description": "Path within the definition",
                  "example": "actions[4]"
                },
                "message": {
                  "type": "string",
                  "description": "What to be aware of",
                  "example": "Action 'orphan-step' has no incoming edges and may be unreachable."
                },
                "type": {
                  "type": "string",
                  "description": "Step type the warning relates to",
                  "example": "sms"
                }
              },
              "title": "WorkflowDefinitionWarningDto"
            }
          }
        },
        "required": [
          "locationId",
          "total",
          "count",
          "skip",
          "stepTypes",
          "notFound",
          "warnings"
        ],
        "title": "StepTypesResponseDto"
      },
      "BadRequestDTO": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "example": 400
          },
          "message": {
            "type": "string",
            "example": "Bad Request"
          }
        },
        "title": "BadRequestDTO"
      },
      "UnauthorizedDTO": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "example": 401
          },
          "message": {
            "type": "string",
            "example": "Invalid token: access token is invalid"
          },
          "error": {
            "type": "string",
            "example": "Unauthorized"
          }
        },
        "title": "UnauthorizedDTO"
      },
      "WorkflowApiNotEnabledDto": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "description": "HTTP status code",
            "example": 403
          },
          "error": {
            "type": "string",
            "description": "HTTP error name",
            "example": "Forbidden"
          },
          "code": {
            "type": "string",
            "description": "Stable code to branch on. Only ever this value for an enrolment refusal.",
            "example": "WORKFLOW_API_NOT_ENABLED_FOR_LOCATION"
          },
          "message": {
            "type": "string",
            "description": "Why the request was refused",
            "example": "The workflow API is not enabled for location ve9EPM428h8vShlRW1KT. This is not a token or scope problem — the API is currently available to selected sub-accounts only, and this one is not among them."
          }
        },
        "title": "WorkflowApiNotEnabledDto"
      },
      "WorkflowDefinitionUnprocessableDto": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "description": "HTTP status code",
            "example": 422
          },
          "message": {
            "description": "Validation errors describing why the request could not be processed",
            "example": [
              "Unprocessable Entity"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "error": {
            "type": "string",
            "description": "HTTP error name",
            "example": "Unprocessable Entity"
          },
          "errors": {
            "description": "Per-finding detail. This is what tells you which step is wrong.",
            "example": [
              {
                "code": "STEP_VALIDATION_FAILED",
                "message": "Action is missing a required field.",
                "stage": "attributes"
              }
            ],
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "Stable error code you can branch on",
                  "example": "MISSING_REQUIRED_FIELDS"
                },
                "message": {
                  "type": "string",
                  "description": "Human-readable explanation",
                  "example": "Body is required"
                },
                "path": {
                  "type": "string",
                  "description": "Path within the definition",
                  "example": "definition.actions[2].attributes.body"
                },
                "stage": {
                  "type": "string",
                  "description": "Which validation phase produced this",
                  "example": "schema"
                },
                "context": {
                  "type": "object",
                  "description": "Which step or asset the finding relates to",
                  "additionalProperties": true,
                  "example": {
                    "stepId": "9f2c1e40-…",
                    "stepName": "SMS",
                    "stepType": "sms"
                  }
                }
              },
              "title": "WorkflowDefinitionErrorDto"
            }
          }
        },
        "title": "WorkflowDefinitionUnprocessableDto"
      },
      "WorkflowRateLimitedDto": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "description": "HTTP status code",
            "example": 429
          },
          "message": {
            "type": "string",
            "description": "`Too Many Requests` when the integration's quota is spent, `Too Many Requests !` when the sub-account budget is spent. Branch on the status code, not this text.",
            "example": "Too Many Requests !"
          }
        },
        "title": "WorkflowRateLimitedDto"
      }
    }
  }
}
```

## Code Examples

**cURL**

```bash
curl -L -X GET 'https://services.leadconnectorhq.com/workflows/step-types?locationId=<LOCATIONID>' \
  -H 'Authorization: Bearer <TOKEN>' \
  -H 'Version: v3'
```

**Node.js**

```javascript
const options = {
  method: 'GET',
  headers: {
    Authorization: 'Bearer <TOKEN>',
    Version: 'v3'
  }
};

try {
  const response = await fetch('https://services.leadconnectorhq.com/workflows/step-types?locationId=<LOCATIONID>', options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**Python**

```python
import requests

url = "https://services.leadconnectorhq.com/workflows/step-types"

headers = {
    "Authorization": "Bearer <TOKEN>",
    "Version": "v3"
}

params = {
    "locationId": "<LOCATIONID>"
}

response = requests.get(url, params=params, headers=headers)
print(response.json())
```

**PHP**

```php
<?php
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => 'https://services.leadconnectorhq.com/workflows/step-types?locationId=<LOCATIONID>',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer <TOKEN>',
    'Version: v3',
  ],
]);

$response = curl_exec($curl);
curl_close($curl);
echo $response;
```

**Java**

```java
HttpResponse<String> response = Unirest
  .get("https://services.leadconnectorhq.com/workflows/step-types?locationId=<LOCATIONID>")
  .header("Authorization", "Bearer <TOKEN>")
  .header("Version", "v3")
  .asString();
```

**Go**

```go
package main

import (
  "fmt"
  "net/http"
  "io"
)

func main() {
  url := "https://services.leadconnectorhq.com/workflows/step-types?locationId=<LOCATIONID>"
  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Add("Authorization", "Bearer <TOKEN>")
  req.Header.Add("Version", "v3")

  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := io.ReadAll(res.Body)
  fmt.Println(string(body))
}
```

**Ruby**

```ruby
require 'uri'
require 'net/http'

url = URI("https://services.leadconnectorhq.com/workflows/step-types?locationId=<LOCATIONID>")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer <TOKEN>"
request["Version"] = "v3"

response = http.request(request)
puts response.read_body
```

**PowerShell**

```powershell
$headers = @{
  "Authorization" = "Bearer <TOKEN>"
  "Version" = "v3"
}

$response = Invoke-RestMethod -Uri 'https://services.leadconnectorhq.com/workflows/step-types?locationId=<LOCATIONID>' -Method 'GET' -Headers $headers
$response | ConvertTo-Json
```
