# Search Workflows

POST https://services.leadconnectorhq.com/workflows/search

**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`.

Find workflows in a sub-account by name, status, folder or timestamp. Returns summaries only — pass an ID to Get Workflow Definition for the full definition.

Filters are combined with AND. Page with `offset` for the first few pages and switch to the `searchAfter` cursor for deeper paging; sending both is rejected.

## 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 |

## OpenAPI Specification

```json
{
  "openapi": "3.0.0",
  "info": {
    "title": "LeadConnector API",
    "version": "v3"
  },
  "servers": [
    {
      "url": "https://services.leadconnectorhq.com"
    }
  ],
  "paths": {
    "/workflows/search": {
      "post": {
        "summary": "Search Workflows",
        "operationId": "searchWorkflows",
        "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\nFind workflows in a sub-account by name, status, folder or timestamp. Returns summaries only — pass an ID to Get Workflow Definition for the full definition.\n\nFilters are combined with AND. Page with `offset` for the first few pages and switch to the `searchAfter` cursor for deeper paging; sending both is rejected.",
        "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"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkflowSearchDto"
              },
              "examples": {
                "byName": {
                  "summary": "1. By name — first page",
                  "description": "Workflows whose name contains `onboarding`, most recently updated first. With no `offset` or `searchAfter` the first page comes back, and the response carries the `searchAfter` cursor for the next one.",
                  "value": {
                    "filters": [
                      {
                        "field": "name",
                        "operator": "contains",
                        "value": "onboarding"
                      }
                    ],
                    "sort": [
                      {
                        "field": "updatedAt",
                        "direction": "desc"
                      }
                    ],
                    "limit": 20
                  }
                },
                "nextPage": {
                  "summary": "2. Next page — cursor from the previous response",
                  "description": "The same filters and sort, plus the `searchAfter` the previous page returned, sent back verbatim. Do not send `offset` with it: the two are rejected together.",
                  "value": {
                    "filters": [
                      {
                        "field": "name",
                        "operator": "contains",
                        "value": "onboarding"
                      }
                    ],
                    "sort": [
                      {
                        "field": "updatedAt",
                        "direction": "desc"
                      }
                    ],
                    "limit": 20,
                    "searchAfter": [
                      "1735689600000"
                    ]
                  }
                },
                "publishedByIds": {
                  "summary": "3. Known ids — published only",
                  "description": "`in` takes an array and is the way to fetch a known set of workflows. Filters are combined with AND, so the `status` filter narrows that set to the ones that are live.",
                  "value": {
                    "filters": [
                      {
                        "field": "workflowId",
                        "operator": "in",
                        "value": [
                          "b1b0de82-3efb-4d63-83f6-13c50aef208c",
                          "7f3c1a9e-2d4b-4c6f-9e8a-5b1d0c2e3f4a"
                        ]
                      },
                      {
                        "field": "status",
                        "operator": "eq",
                        "value": "published"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowSearchResponseDto"
                }
              }
            }
          },
          "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"
                }
              }
            }
          },
          "415": {
            "description": "Content-Type is not application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowUnsupportedMediaTypeDto"
                }
              }
            }
          },
          "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": {
      "WorkflowSearchDto": {
        "type": "object",
        "properties": {
          "filters": {
            "description": "Filters, combined with AND. Max 20. Omit to match every workflow in the sub-account.\n\nTo fetch a known set of workflows, filter `workflowId` with `in`. The sub-account scope still applies, so an id from another sub-account simply matches nothing.",
            "example": [
              {
                "field": "name",
                "operator": "contains",
                "value": "onboarding"
              }
            ],
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "description": "Field to filter on",
                  "enum": [
                    "workflowId",
                    "name",
                    "status",
                    "type",
                    "customObjectType",
                    "folderId",
                    "updatedBy",
                    "createdAt",
                    "updatedAt"
                  ],
                  "example": "name"
                },
                "operator": {
                  "type": "string",
                  "description": "Comparison to apply. `eq`, `not_eq`, `in`, `contains` and `not_contains` are text-only; `gt`/`gte`/`lt`/`lte` are date-only. A mismatch is rejected with 422. To match a single instant on a date field, send `gte` and `lte` with the same value.",
                  "enum": [
                    "eq",
                    "not_eq",
                    "in",
                    "contains",
                    "not_contains",
                    "gt",
                    "gte",
                    "lt",
                    "lte"
                  ],
                  "example": "contains"
                },
                "value": {
                  "description": "Value to compare against. An array is required for `in` and rejected for every other operator. Dates are ISO-8601.",
                  "example": "onboarding",
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  ]
                }
              },
              "required": [
                "field",
                "operator",
                "value"
              ],
              "title": "WorkflowSearchFilterDto"
            }
          },
          "sort": {
            "description": "Sort order. Defaults to most recently updated first.",
            "example": [
              {
                "field": "updatedAt",
                "direction": "desc"
              }
            ],
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "description": "Field to sort on",
                  "enum": [
                    "workflowId",
                    "name",
                    "status",
                    "type",
                    "customObjectType",
                    "folderId",
                    "updatedBy",
                    "createdAt",
                    "updatedAt"
                  ],
                  "example": "updatedAt"
                },
                "direction": {
                  "type": "string",
                  "description": "Sort direction",
                  "enum": [
                    "asc",
                    "desc"
                  ],
                  "example": "desc"
                }
              },
              "required": [
                "field",
                "direction"
              ],
              "title": "WorkflowSearchSortDto"
            }
          },
          "limit": {
            "type": "number",
            "description": "Page size",
            "example": 20,
            "default": 20
          },
          "offset": {
            "type": "number",
            "description": "Result offset. Cannot be combined with `searchAfter`. Deep paging is expensive — prefer `searchAfter` past the first few pages.",
            "example": 0
          },
          "searchAfter": {
            "description": "Cursor from a previous response, for stable paging through a large result set. Send it back verbatim; do not construct one.",
            "example": [
              "1735689600000"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "example": {
          "filters": [
            {
              "field": "name",
              "operator": "contains",
              "value": "onboarding"
            }
          ],
          "sort": [
            {
              "field": "updatedAt",
              "direction": "desc"
            }
          ],
          "limit": 20
        },
        "title": "WorkflowSearchDto"
      },
      "WorkflowSearchResponseDto": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "description": "Total matches, ignoring paging",
            "example": 137
          },
          "count": {
            "type": "number",
            "description": "Number returned in this response",
            "example": 20
          },
          "offset": {
            "type": "number",
            "description": "The offset this page started at — echoed back so a caller paging through can tell which slice it received without tracking the request. 0 when no `offset` was sent. Absent when the page was fetched with `searchAfter`, where a numeric position is not known.",
            "example": 0
          },
          "workflows": {
            "description": "The matching workflows",
            "example": [
              {
                "workflowId": "b1b0de82-3efb-4d63-83f6-13c50aef208c",
                "version": "3"
              }
            ],
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Workflow ID",
                  "example": "b1b0de82-3efb-4d63-83f6-13c50aef208c"
                },
                "name": {
                  "type": "string",
                  "description": "Workflow name",
                  "example": "Welcome new contacts"
                },
                "description": {
                  "type": "string",
                  "description": "Workflow description. Always null today — no write path persists a description, so this is reserved rather than populated. Not filterable or sortable for the same reason.",
                  "example": null,
                  "nullable": true
                },
                "status": {
                  "type": "string",
                  "description": "Publish status",
                  "example": "published"
                },
                "folderId": {
                  "type": "string",
                  "description": "Folder the workflow sits in, null at the root",
                  "example": null,
                  "nullable": true
                },
                "workflowType": {
                  "type": "string",
                  "description": "Workflow type. Set only by some builder-created workflows; workflows created through this API never carry one. Not filterable or sortable.",
                  "example": "workflow"
                },
                "createdBy": {
                  "type": "string",
                  "description": "User who created the workflow. Absent today — the search indexer records only `updatedBy`. Not filterable or sortable for the same reason.",
                  "example": null,
                  "nullable": true
                },
                "updatedBy": {
                  "type": "string",
                  "description": "User who last updated the workflow",
                  "example": "aX3kQ2mNpZ"
                },
                "createdAt": {
                  "type": "string",
                  "description": "Creation time",
                  "example": "2026-01-14T09:12:44.000Z"
                },
                "updatedAt": {
                  "type": "string",
                  "description": "Last update time",
                  "example": "2026-02-02T17:41:09.000Z"
                }
              },
              "required": [
                "id"
              ],
              "title": "WorkflowSearchResultDto"
            }
          },
          "searchAfter": {
            "description": "Cursor to pass as `searchAfter` for the next page. Absent on the last page.",
            "example": [
              "1735689600000"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "total",
          "count",
          "workflows"
        ],
        "title": "WorkflowSearchResponseDto"
      },
      "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"
      },
      "WorkflowUnsupportedMediaTypeDto": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "number",
            "description": "HTTP status code",
            "example": 415
          },
          "message": {
            "description": "What was wrong with the Content-Type header",
            "example": [
              "Content-Type must be application/json, not 'text/plain'"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "error": {
            "type": "string",
            "description": "HTTP error name",
            "example": "Unsupported Media Type"
          }
        },
        "title": "WorkflowUnsupportedMediaTypeDto"
      },
      "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 POST 'https://services.leadconnectorhq.com/workflows/search?locationId=<LOCATIONID>' \
  -H 'Authorization: Bearer <TOKEN>' \
  -H 'Version: v3' \
  -H 'Content-Type: application/json' \
  --data-raw '{
  "filters": [
    {
      "field": "name",
      "operator": "contains",
      "value": "onboarding"
    }
  ],
  "sort": [
    {
      "field": "updatedAt",
      "direction": "desc"
    }
  ],
  "limit": 20
}'
```

**Node.js**

```javascript
const options = {
  method: 'POST',
  headers: {
    Authorization: 'Bearer <TOKEN>',
    Version: 'v3',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
      "filters": [
        {
          "field": "name",
          "operator": "contains",
          "value": "onboarding"
        }
      ],
      "sort": [
        {
          "field": "updatedAt",
          "direction": "desc"
        }
      ],
      "limit": 20
  })
};

try {
  const response = await fetch('https://services.leadconnectorhq.com/workflows/search?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/search"

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

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

payload = {
    "filters": [
        {
            "field": "name",
            "operator": "contains",
            "value": "onboarding"
        }
    ],
    "sort": [
        {
            "field": "updatedAt",
            "direction": "desc"
        }
    ],
    "limit": 20
}

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

**PHP**

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

curl_setopt_array($curl, [
  CURLOPT_URL => 'https://services.leadconnectorhq.com/workflows/search?locationId=<LOCATIONID>',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer <TOKEN>',
    'Version: v3',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => '{
  "filters": [
    {
      "field": "name",
      "operator": "contains",
      "value": "onboarding"
    }
  ],
  "sort": [
    {
      "field": "updatedAt",
      "direction": "desc"
    }
  ],
  "limit": 20
}',
]);

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

**Java**

```java
HttpResponse<String> response = Unirest
  .post("https://services.leadconnectorhq.com/workflows/search?locationId=<LOCATIONID>")
  .header("Authorization", "Bearer <TOKEN>")
  .header("Version", "v3")
  .header("Content-Type", "application/json")
  .body("{\"filters\":[{\"field\":\"name\",\"operator\":\"contains\",\"value\":\"onboarding\"}],\"sort\":[{\"field\":\"updatedAt\",\"direction\":\"desc\"}],\"limit\":20}")
  .asString();
```

**Go**

```go
package main

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

func main() {
  url := "https://services.leadconnectorhq.com/workflows/search?locationId=<LOCATIONID>"
  payload := strings.NewReader(`{
  "filters": [
    {
      "field": "name",
      "operator": "contains",
      "value": "onboarding"
    }
  ],
  "sort": [
    {
      "field": "updatedAt",
      "direction": "desc"
    }
  ],
  "limit": 20
}`)
  req, _ := http.NewRequest("POST", url, payload)
  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/search?locationId=<LOCATIONID>")

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

request = Net::HTTP::Post.new(url)
request["Authorization"] = "Bearer <TOKEN>"
request["Version"] = "v3"
request["Content-Type"] = "application/json"
request.body = '{
  "filters": [
    {
      "field": "name",
      "operator": "contains",
      "value": "onboarding"
    }
  ],
  "sort": [
    {
      "field": "updatedAt",
      "direction": "desc"
    }
  ],
  "limit": 20
}'

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

**PowerShell**

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

$body = '{
  "filters": [
    {
      "field": "name",
      "operator": "contains",
      "value": "onboarding"
    }
  ],
  "sort": [
    {
      "field": "updatedAt",
      "direction": "desc"
    }
  ],
  "limit": 20
}'

$response = Invoke-RestMethod -Uri 'https://services.leadconnectorhq.com/workflows/search?locationId=<LOCATIONID>' -Method 'POST' -Headers $headers -Body $body -ContentType 'application/json'
$response | ConvertTo-Json
```
