# Create Workflow

> Create a workflow programmatically

- **URL**: https://orshot.com/docs/api-reference/workflow-create

---

## Overview

Create a workflow from a steps array. This is the same contract the in-app builder uses, with identical validation and plan gating, so a workflow created here behaves exactly like one built in the dashboard.

A workflow is an ordered list of steps, each ``. Use the node catalog (`GET /v1/workflows/nodes`) to discover every buildable node, its `configSchema`, and whether it is available on your plan. You can validate a steps array without writing anything via `POST /v1/workflows/validate`.```markdown tab="Endpoint"
https://api.orshot.com/v1/workflows
```## Request Body

| Parameter     | Type   | Required | Description                                                                                     |
| ------------- | ------ | -------- | ----------------------------------------------------------------------------------------------- |
| `name`        | String | No       | Workflow name, up to 200 characters. Defaults to "Untitled workflow"                             |
| `description` | String | No       | Optional description                                                                             |
| `steps`       | Array  | Yes      | Ordered steps, each ``. The first step must be a trigger or data source     |
| `status`      | String | No       | `draft`, `active`, `paused` or `archived`. Defaults to `draft`                                   |
| `metadata`    | Object | No       | Your own metadata. Server-managed keys (`health`, `poll`, `share`) are ignored                   |

## Request```js
await fetch("https://api.orshot.com/v1/workflows", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_API_KEY>",
  },
  body: JSON.stringify({
    name: "Renders from new Drive files",
    status: "draft",
    steps: [
      {
        key: "google_drive_new_file",
        type: "trigger",
        config: { folderId: "<DRIVE_FOLDER_ID>", pollInterval: 10 },
      },
      {
        key: "render",
        type: "action",
        config: {
          templateId: "1443",
          format: "jpg",
          mapping: { headline: "name", hero_image: "image_url" },
        },
      },
      {
        key: "orshot_url",
        type: "destination",
        config: {},
      },
    ],
  }),
});
```</Tab>```json
{
  "workflow": {
    "id": 91,
    "name": "Renders from new Drive files",
    "status": "draft",
    "trigger_type": "google_drive_new_file",
    "required_connections": ["google-drive"],
    "steps": [ ... ],
    "schedule_config": { "poll": true, "frequency": "custom", "intervalMinutes": 10 },
    "next_run_at": null,
    "created_at": "2026-07-11T10:00:00.000Z"
  },
  "warnings": []
}
```</Tab>
</Tabs>

Returns `201 Created`. `warnings` lists anything that would block a run (for example a missing destination), so you can save a draft now and finish it later.

## Activation Rules

Creating with `status: "active"` enforces everything a run needs up front:

- The steps must be complete and runnable (no warnings)
- Steps gated to a higher plan tier block activation (they save fine as a draft)
- Required provider connections must already be connected in the workspace
- Your plan's active-workflow limit must not be exceeded

## Error Responses

### Invalid Workflow (400)```json
{
  "error": "Invalid workflow",
  "details": ["Step 2 (render): templateId is required"]
}
```### Not Runnable Yet (400)

Returned when activating an incomplete workflow.```json
{
  "error": "Workflow is not runnable yet, finish it before activating",
  "details": ["no_destination"],
  "hints": ["Add a destination step so every render is delivered somewhere."]
}
```### Missing Connection (400)```json
{
  "error": "Connect your Google Drive account before activating this workflow",
  "code": "missing_connection"
}
```### Plan Gated Steps (403)```json
{
  "error": "Some steps in this workflow require a higher plan",
  "code": "plan_gated"
}
```## Rate Limits

- 60 requests per minute per workspace