# Workflows API

CRUD endpoints for workflow templates, plus listing and triggering workflow runs. Workflows are multi-step sequences for lead qualification, property blasts, follow-up campaigns, and more.

## List Templates

```
GET /api/v1/workflows
```

Returns all workflow templates available to your account (your own templates plus system defaults), with full template detail including steps and configuration.

**Query Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `page` | number | Page number (default: 1) |
| `limit` | number | Items per page (default: 20) |

**Response:**

```json
{
  "data": [
    {
      "id": "wf_abc123",
      "name": "Lead Pre-Screening",
      "description": "Qualify prospects with key questions before scheduling a showing.",
      "isPublic": true,
      "isActive": true,
      "triggerType": "manual",
      "slug": "prescreening",
      "steps": [
        { "type": "send_message", "config": { "template": "intro" } },
        { "type": "wait_for_reply", "config": { "timeoutHours": 24 } }
      ],
      "exitConditions": { "maxMessages": 20, "timeoutDays": 7 },
      "questionConfig": {},
      "completionConfig": {},
      "introMessage": "Hi! I'd like to ask a few quick questions.",
      "voiceConfig": { "enabled": true },
      "version": 3,
      "createdAt": "2026-01-15T10:00:00Z",
      "updatedAt": "2026-02-20T08:30:00Z"
    }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 4, "totalPages": 1 }
}
```

## Get Template

```
GET /api/v1/workflows/:id
```

Returns the full workflow template including steps, exit conditions, question config, completion config, intro message, voice config, and version info.

**Response:**

```json
{
  "data": {
    "id": "wf_abc123",
    "name": "Lead Pre-Screening",
    "description": "Qualify prospects with key questions before scheduling a showing.",
    "isPublic": true,
    "isActive": true,
    "triggerType": "manual",
    "slug": "prescreening",
    "steps": [
      { "type": "send_message", "config": { "template": "intro" } },
      { "type": "wait_for_reply", "config": { "timeoutHours": 24 } }
    ],
    "exitConditions": { "maxMessages": 20, "timeoutDays": 7 },
    "questionConfig": {},
    "completionConfig": {},
    "introMessage": "Hi! I'd like to ask a few quick questions.",
    "voiceConfig": { "enabled": true },
    "version": 3,
    "createdAt": "2026-01-15T10:00:00Z",
    "updatedAt": "2026-02-20T08:30:00Z"
  }
}
```

## Create Template

```
POST /api/v1/workflows
```

Create a new workflow template. The `slug` is auto-generated from the name and must be unique per tenant.

**Request Body:**

```json
{
  "name": "Lead Pre-Screening",
  "triggerType": "manual",
  "steps": [
    { "type": "send_message", "config": { "template": "intro" } },
    { "type": "wait_for_reply", "config": { "timeoutHours": 24 } },
    { "type": "agent", "config": { "mode": "interactive" } }
  ],
  "description": "Qualify prospects with key questions before scheduling a showing.",
  "exitConditions": { "maxMessages": 20, "timeoutDays": 7 },
  "introMessage": "Hi! I'd like to ask a few quick questions.",
  "isPublic": true
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Template name (1-100 chars) |
| `steps` | array | Yes | Workflow step definitions (max 20) |
| `triggerType` | string | Yes | `manual`, `deep_link`, `automatic`, or `scheduled` |
| `description` | string | No | Human-readable description (max 500 chars) |
| `triggerConfig` | object | No | Trigger-specific configuration |
| `exitConditions` | object | No | `maxMessages` (1-50) and/or `timeoutDays` (1-90) |
| `questionConfig` | object | No | Question-level configuration |
| `completionConfig` | object | No | Completion behavior configuration |
| `isPublic` | boolean | No | Whether the workflow is publicly accessible (default: `false`) |
| `introMessage` | string | No | Opening message sent to the contact (max 2000 chars) |
| `voiceConfig` | object | No | Voice agent configuration |
| `isActive` | boolean | No | Whether the template is active (default: `true`) |

**Response** `201 Created`:

```json
{
  "data": {
    "id": "wf_new456",
    "name": "Lead Pre-Screening",
    "description": "Qualify prospects with key questions before scheduling a showing.",
    "isPublic": true,
    "isActive": true,
    "triggerType": "manual",
    "slug": "lead-pre-screening",
    "steps": [
      { "type": "send_message", "config": { "template": "intro" } },
      { "type": "wait_for_reply", "config": { "timeoutHours": 24 } },
      { "type": "agent", "config": { "mode": "interactive" } }
    ],
    "exitConditions": { "maxMessages": 20, "timeoutDays": 7 },
    "questionConfig": null,
    "completionConfig": null,
    "introMessage": "Hi! I'd like to ask a few quick questions.",
    "voiceConfig": null,
    "version": 1,
    "createdAt": "2026-02-26T12:00:00Z",
    "updatedAt": "2026-02-26T12:00:00Z"
  }
}
```

Emits a `workflow.created` webhook event.

Supports `Idempotency-Key` header.

## Update Template

```
PATCH /api/v1/workflows/:id
```

Update an existing workflow template. Send only the fields you want to change.

Changes to execution fields (`steps`, `exitConditions`, `questionConfig`, `completionConfig`, `introMessage`) automatically create a version snapshot, incrementing the template's `version` number.

**Request Body:**

```json
{
  "description": "Updated description for the screening workflow.",
  "steps": [
    { "type": "send_message", "config": { "template": "intro_v2" } },
    { "type": "wait_for_reply", "config": { "timeoutHours": 48 } },
    { "type": "agent", "config": { "mode": "interactive" } },
    { "type": "end" }
  ],
  "exitConditions": { "maxMessages": 30, "timeoutDays": 14 }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Template name (1-100 chars) |
| `steps` | array | Workflow step definitions (max 20) |
| `triggerType` | string | `manual`, `deep_link`, `automatic`, or `scheduled` |
| `description` | string | Human-readable description (max 500 chars) |
| `triggerConfig` | object | Trigger-specific configuration |
| `exitConditions` | object | `maxMessages` (1-50) and/or `timeoutDays` (1-90) |
| `questionConfig` | object | Question-level configuration |
| `completionConfig` | object | Completion behavior configuration |
| `isPublic` | boolean | Whether the workflow is publicly accessible |
| `introMessage` | string | Opening message (max 2000 chars) |
| `voiceConfig` | object | Voice agent configuration |
| `isActive` | boolean | Whether the template is active |

**Response** `200 OK`:

```json
{
  "data": {
    "id": "wf_abc123",
    "name": "Lead Pre-Screening",
    "description": "Updated description for the screening workflow.",
    "isPublic": true,
    "isActive": true,
    "triggerType": "manual",
    "slug": "prescreening",
    "steps": [
      { "type": "send_message", "config": { "template": "intro_v2" } },
      { "type": "wait_for_reply", "config": { "timeoutHours": 48 } },
      { "type": "agent", "config": { "mode": "interactive" } },
      { "type": "end" }
    ],
    "exitConditions": { "maxMessages": 30, "timeoutDays": 14 },
    "questionConfig": {},
    "completionConfig": {},
    "introMessage": "Hi! I'd like to ask a few quick questions.",
    "voiceConfig": { "enabled": true },
    "version": 4,
    "createdAt": "2026-01-15T10:00:00Z",
    "updatedAt": "2026-02-26T12:30:00Z"
  }
}
```

Emits a `workflow.updated` webhook event.

## Delete Template

```
DELETE /api/v1/workflows/:id
```

Permanently delete a workflow template and its version history. Completed runs are cascade-deleted.

Returns `409 Conflict` if the template has active runs (status `pending`, `running`, or `paused`). Cancel or wait for those runs to finish before deleting.

**Response:** `204 No Content` on success.

**Error Response** `409 Conflict`:

```json
{
  "type": "https://rentalot.ai/problems/conflict",
  "title": "Conflict",
  "status": 409,
  "detail": "Cannot delete workflow template with active runs. Cancel pending runs first."
}
```

Emits a `workflow.deleted` webhook event.

## List Runs

```
GET /api/v1/workflows/runs
```

**Query Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `page` | number | Page number (default: 1) |
| `limit` | number | Items per page (default: 20) |
| `status` | string | Filter: `active`, `paused`, `completed`, `cancelled`, `failed` |
| `templateId` | string | Filter by workflow template ID |
| `contactId` | string | Filter by contact ID |

**Response:**

```json
{
  "data": [
    {
      "id": "run_abc123",
      "workflowTemplateId": "wf_abc123",
      "templateName": "Lead Pre-Screening",
      "contactId": "550e8400-...",
      "status": "active",
      "currentStep": 3,
      "totalSteps": 8,
      "startedAt": "2026-02-11T14:00:00Z",
      "completedAt": null
    }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 12, "totalPages": 1 }
}
```

## Trigger Run

```
POST /api/v1/workflows/runs
```

Start a new workflow run for a contact.

**Request Body:**

```json
{
  "workflowId": "wf_abc123",
  "contactId": "550e8400-e29b-41d4-a716-446655440000",
  "propertyId": "prop_def456"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `workflowId` | string (UUID) | Yes | The workflow template to run |
| `contactId` | string (UUID) | Yes | The contact to run the workflow for |
| `propertyId` | string (UUID) | No | Property context for property-scoped workflows |

**Response** `201 Created`:

```json
{
  "data": {
    "id": "run_new123",
    "workflowTemplateId": "wf_abc123",
    "contactId": "550e8400-...",
    "status": "active",
    "currentStep": 0,
    "totalSteps": 8,
    "startedAt": "2026-02-11T15:00:00Z"
  }
}
```

## Get Run

```
GET /api/v1/workflows/runs/:id
```

## Idempotency

`POST /api/v1/workflows` and `POST /api/v1/workflows/runs` support the `Idempotency-Key` header.
