diff --git a/docs/workflows/workflow-automations.md b/docs/workflows/workflow-automations.md new file mode 100644 index 0000000..c47a42b --- /dev/null +++ b/docs/workflows/workflow-automations.md @@ -0,0 +1,559 @@ +# Workflow Task Automations Integration Guide + +This guide explains how to integrate external systems with the Workflows API to handle automated workflow tasks. Automated tasks are defined within a running workflow execution and trigger automation flows behind the scenes. + +## Overview + +Workflow task automations allow you to execute automation flows as part of a workflow execution. When a workflow reaches an automated task (`task_execution_type: "AUTOMATION"`), your system needs to: + +1. Detect the automated task +2. Trigger the automation execution +3. Monitor the automation status +4. Update the workflow task with the result + +## Integration Process + +### Step 1: Search for Entities with Workflow Context + +First, retrieve entities along with their workflow context using the Entity Search API. + +**API Reference:** [Search Entities](/api/entity#tag/Entities/operation/searchEntities) + +**Request:** +```http +POST /v1/entity:search +Content-Type: application/json + +{ + "q": "...", + "hydrate": true +} +``` + +**Response:** +```json +{ + "results": [ + { + "_id": "11111111-1111-1111-1111-111111111111", + "_schema": "opportunity", + "_title": "Solar Installation Project", + "workflows": { + "rUqQv3Xd": [ + { + "id": "85mk9wzyp98", + "definition_id": "rUqQv3Xd", + "name": "Solar Workflow", + "status": "STARTED", + "assignees": ["10014629", "10002602"], + "duedate": "2023-01-01T10:00:00.000Z", + "last_update_time": "2023-01-01T10:00:00.000Z", + "task_id": "8tpx9mi4ryb", + "task_name": "Clean Energy", + "task_assignees": ["10014629", "10002602", "10016846"], + "task_duedate": "2023-01-10T23:23:27.111Z", + "task_execution_type": "AUTOMATION", + "phase_id": "362pq2sy9ll", + "phase_name": "Research" + }, + { + "id": "3ur6cry7r7x", + "definition_id": "rUqQv3Xd", + "name": "Solar Workflow", + "status": "STARTED", + "assignees": ["10014629", "10002602"], + "duedate": "2023-01-01T10:00:00.000Z", + "last_update_time": "2023-01-01T10:00:00.000Z", + "task_id": "7olrwq658ne", + "task_name": "Conclusions", + "task_assignees": ["10014629"], + "task_duedate": "2023-01-01T10:00:00.000Z", + "task_execution_type": "MANUAL", + "phase_id": "q2rivpohtla", + "phase_name": "Closing" + } + ], + "V_opYKRV": [ + { + "id": "opse3yk2tit", + "definition_id": "V_opYKRV", + "name": "Wallbox Workflow", + "status": "STARTED", + "assignees": ["10014629", "10002602"], + "last_update_time": "2023-01-01T10:00:00.000Z", + "task_id": "rdaq2e48y00", + "task_name": "Schedule & perform E-Check on site", + "task_assignees": [], + "task_execution_type": "MANUAL", + "phase_id": "k0b4ht9i0vb", + "phase_name": "Pre-Check", + "task_duedate": "2023-01-01T10:00:00.000Z" + } + ], + "primary": { + "id": "85mk9wzyp98", + "definition_id": "rUqQv3Xd", + "name": "Solar Workflow", + "status": "STARTED", + "assignees": ["10014629", "10002602"], + "duedate": "2023-01-01T10:00:00.000Z", + "last_update_time": "2023-01-01T10:00:00.000Z", + "task_id": "8tpx9mi4ryb", + "task_name": "Clean Energy", + "task_assignees": ["10014629", "10002602", "10016846"], + "task_duedate": "2023-01-10T23:23:27.111Z", + "task_execution_type": "AUTOMATION", + "phase_id": "362pq2sy9ll", + "phase_name": "Research" + } + } + } + ] +} +``` + +**Key Fields:** +- `id`: Workflow execution ID (e.g., `"85mk9wzyp98"`) +- `task_execution_type`: Type of task execution (`"AUTOMATION"` or `"MANUAL"`) +- `task_id`: The specific task/step ID within the workflow +- `_id`: Entity ID that the workflow is attached to + +--- + +### Step 2: Check for Automation Tasks + +Filter the workflow context to identify automated tasks: + +```javascript +const automationTasks = workflows[definitionId].filter( + workflow => workflow.task_execution_type === 'AUTOMATION' +) +``` + +If `task_execution_type === 'AUTOMATION'`, proceed to retrieve the full workflow execution data. + +--- + +### Step 3: Retrieve Full Workflow Execution + +Use the workflow execution `id` from Step 1 to fetch the complete workflow data. + +**API Reference:** [Get Workflow Execution](/api/workflow-execution#tag/Workflow-Executions/operation/getExecution) + +**Request:** +```http +GET /v1/workflows/executions/{id} +``` + +**Example:** +```http +GET /v1/workflows/executions/85mk9wzyp98 +``` + +**Response:** +```json +{ + "id": "85mk9wzyp98", + "definition_id": "rUqQv3Xd", + "name": "Solar Workflow", + "status": "STARTED", + "entity_id": "11111111-1111-1111-1111-111111111111", + "assignees": ["10014629", "10002602"], + "flow": [ + { + "id": "8tpx9mi4ryb", + "name": "Clean Energy", + "type": "TASK", + "status": "STARTED", + "execution_type": "AUTOMATION", + "assignees": ["10014629", "10002602", "10016846"], + "duedate": "2023-01-10T23:23:27.111Z", + "automationConfig": { + "flowId": "abc123-automation-flow-id", + "execution_id": null, + "execution_status": null + }, + "phase_id": "362pq2sy9ll" + }, + { + "id": "7olrwq658ne", + "name": "Conclusions", + "type": "TASK", + "status": "PENDING", + "execution_type": "MANUAL", + "assignees": ["10014629"], + "duedate": "2023-01-01T10:00:00.000Z", + "phase_id": "q2rivpohtla" + } + ], + "created_at": "2023-01-01T09:00:00.000Z", + "updated_at": "2023-01-01T10:00:00.000Z" +} +``` + +**Key Fields:** +- `flow`: Array of all tasks/steps in the workflow +- `automationConfig.flowId`: The automation flow ID to execute +- `execution_type`: Confirms this is an `"AUTOMATION"` task + +--- + +### Step 4: Find the Target Task + +Locate the specific task within the `flow` array using the `task_id` from Step 1. The flow can contain: +- **Top-level steps** (`type: "STEP"`) +- **Sections/Phases** (`type: "SECTION"`) which contain a `steps` array + +```javascript +function findTask(flow, taskId) { + for (const item of flow) { + if (item.type === 'STEP' && item.id === taskId) { + // Found top-level step + return item + } + + if (item.type === 'SECTION' && item.steps) { + // Search within section/phase + const step = item.steps.find(s => s.id === taskId) + if (step) return step + } + } + return null +} + +const targetTask = findTask(workflowExecution.flow, '8tpx9mi4ryb') +const automationFlowId = targetTask.automationConfig.flowId +``` + +--- + +### Step 5: Trigger the Automation Execution + +Use the `flowId` from the task's `automationConfig` to trigger the automation execution. + +**API Reference:** [Start Automation Execution](/api/automation#tag/executions/operation/startExecution) + +**Request:** +```http +POST /v1/automation/executions +Content-Type: application/json + +{ + "entity_id": "11111111-1111-1111-1111-111111111111", + "flow_id": "abc123-automation-flow-id" +} +``` + +**Response:** +```json +{ + "id": "9baf184f-bc81-4128-bca3-d974c90a12c4", + "execution_status": "pending", + "entity_id": "e3d3ebac-baab-4395-abf4-50b5bf1f8b74", + "org_id": "e3d3ebac-baab-4395-abf4-50b5bf1f8b74", + "flow_id": "7791b04a-16d2-44a2-9af9-2d59c25c512f", + "flow_name": "Handle contact form", + "created_at": "2023-01-01T10:00:00.000Z", + "updated_at": "2023-01-01T10:00:00.000Z", + "current_action_id": "action-123", + "actions": [ + { + "id": "action-123", + "name": "Send Email", + "type": "send-email", + "status": "pending" + } + ], + "trigger_context": { + "entity_id": "11111111-1111-1111-1111-111111111111" + } +} +``` + +**Key Fields:** +- `id`: The automation execution ID (e.g., `"9baf184f-bc81-4128-bca3-d974c90a12c4"`) +- `execution_status`: Initial status, typically `"pending"` + +--- + +### Step 6: Monitor Automation Execution Status + +Implement long polling to track the automation execution status. Poll every **1 second** until the execution reaches a terminal state. + +**API Reference:** [Get Automation Execution](/api/automation#tag/executions/operation/getExecution) + +**Request:** +```http +GET /v1/automation/executions/{id} +``` + +**Example:** +```http +GET /v1/automation/executions/9baf184f-bc81-4128-bca3-d974c90a12c4 +``` + +**Response (In Progress):** +```json +{ + "id": "9baf184f-bc81-4128-bca3-d974c90a12c4", + "execution_status": "in_progress", + "entity_id": "e3d3ebac-baab-4395-abf4-50b5bf1f8b74", + "org_id": "e3d3ebac-baab-4395-abf4-50b5bf1f8b74", + "flow_id": "7791b04a-16d2-44a2-9af9-2d59c25c512f", + "flow_name": "Handle contact form", + "created_at": "2023-01-01T10:00:00.000Z", + "updated_at": "2023-01-01T10:00:05.000Z", + "current_action_id": "action-123", + "actions": [ + { + "id": "action-123", + "name": "Send Email", + "type": "send-email", + "status": "in_progress" + } + ], + "trigger_context": { + "entity_id": "11111111-1111-1111-1111-111111111111" + } +} +``` + +**Response (Completed):** +```json +{ + "id": "9baf184f-bc81-4128-bca3-d974c90a12c4", + "execution_status": "success", + "entity_id": "e3d3ebac-baab-4395-abf4-50b5bf1f8b74", + "org_id": "e3d3ebac-baab-4395-abf4-50b5bf1f8b74", + "flow_id": "7791b04a-16d2-44a2-9af9-2d59c25c512f", + "flow_name": "Handle contact form", + "created_at": "2023-01-01T10:00:00.000Z", + "updated_at": "2023-01-01T10:00:30.000Z", + "current_action_id": "action-123", + "actions": [ + { + "id": "action-123", + "name": "Send Email", + "type": "send-email", + "status": "success" + } + ], + "trigger_context": { + "entity_id": "11111111-1111-1111-1111-111111111111" + } +} +``` + +**Terminal States:** +- `success` - Automation completed successfully +- `failed` - Automation encountered an error +- `cancelled` - Automation was cancelled +- `skipped` - Automation was skipped +- `hot` - Automation is in a hot/error state + +**Polling Implementation:** +```javascript +async function pollAutomationStatus(executionId) { + const terminalStates = ['success', 'failed', 'cancelled', 'skipped', 'hot'] + + while (true) { + const response = await fetch(`/v1/automation/executions/${executionId}`) + const execution = await response.json() + + if (terminalStates.includes(execution.execution_status)) { + return execution + } + + // Wait 1 second before next poll + await new Promise(resolve => setTimeout(resolve, 1000)) + } +} +``` + +--- + +### Step 7: Update the Workflow Task + +Once the automation execution completes, update the workflow task with the final status. + +**API Reference:** [Update Workflow Step](/api/workflow-execution#tag/Workflow-Steps/operation/updateStep) + +**Request:** +```http +PATCH /v1/workflows/executions/{workflow_execution_id}/steps/{step_id} +Content-Type: application/json + +{ + "status": "COMPLETED", + "automationConfig": { + "flowId": "abc123-automation-flow-id", + "executionId": "9baf184f-bc81-4128-bca3-d974c90a12c4", + "executionStatus": "success" + } +} +``` + +**Example:** +```http +PATCH /v1/workflows/executions/85mk9wzyp98/steps/8tpx9mi4ryb +Content-Type: application/json + +{ + "status": "COMPLETED", + "automationConfig": { + "flowId": "abc123-automation-flow-id", + "executionId": "9baf184f-bc81-4128-bca3-d974c90a12c4", + "executionStatus": "success" + } +} +``` + +**Response:** +```json +{ + "id": "8tpx9mi4ryb", + "name": "Clean Energy", + "type": "STEP", + "status": "COMPLETED", + "execution_type": "AUTOMATION", + "assignees": ["10014629", "10002602", "10016846"], + "duedate": "2023-01-10T23:23:27.111Z", + "automationConfig": { + "flowId": "abc123-automation-flow-id", + "executionId": "9baf184f-bc81-4128-bca3-d974c90a12c4", + "executionStatus": "success" + }, + "completed_at": "2023-01-01T10:00:30.000Z" +} +``` + +### Status Mapping + +Map the automation execution status to the appropriate workflow task status: + +| Automation Status | Workflow Task Status | Description | +|-------------------|---------------------|-------------| +| `success` | `COMPLETED` | Task completed successfully | +| `failed` | `FAILED` | Task failed due to automation error | +| `skipped` | `SKIPPED` | Task was skipped | +| `cancelled` | `SKIPPED` | Task was cancelled | +| `hot` | `FAILED` | Task failed with critical error | + +--- + +## Complete Integration Flow + +```mermaid +sequenceDiagram + participant E as External System + participant EA as Entity API + participant WA as Workflow API + participant AA as Automation API + + E->>EA: POST /v1/entity:search + EA-->>E: Entity with workflow context + + Note over E: Check if task_execution_type === 'AUTOMATION' + + E->>WA: GET /v1/workflows/executions/{id} + WA-->>E: Full workflow execution data + + Note over E: Find task and extract automationConfig.flowId + + E->>AA: POST /v1/automation/executions + AA-->>E: Automation execution_id + + loop Poll every 1 second + E->>AA: GET /v1/automation/executions/{execution_id} + AA-->>E: Execution status + Note over E: Continue until terminal state + end + + E->>WA: PATCH /v1/workflows/executions/{id}/steps/{step_id} + WA-->>E: Updated task status +``` + +--- + +## Error Handling + +### Automation Execution Fails + +If the automation execution fails (`execution_status: "failed"`), update the workflow task accordingly: + +```http +PATCH /v1/workflows/executions/85mk9wzyp98/steps/8tpx9mi4ryb +Content-Type: application/json + +{ + "status": "FAILED", + "automationConfig": { + "flowId": "abc123-automation-flow-id", + "executionId": "9baf184f-bc81-4128-bca3-d974c90a12c4", + "executionStatus": "failed" + }, + "error": { + "message": "Automation failed to complete", + "code": "AUTOMATION_FAILED" + } +} +``` + +### Timeout Handling + +Implement a maximum timeout for polling (e.g., 5 minutes): + +```javascript +async function pollAutomationStatusWithTimeout(executionId, timeoutMs = 300000) { + const terminalStates = ['success', 'failed', 'cancelled', 'skipped', 'hot'] + const startTime = Date.now() + + while (Date.now() - startTime < timeoutMs) { + const response = await fetch(`/v1/automation/executions/${executionId}`) + const execution = await response.json() + + if (terminalStates.includes(execution.execution_status)) { + return execution + } + + await new Promise(resolve => setTimeout(resolve, 1000)) + } + + throw new Error('Automation execution timeout') +} +``` + +--- + +## Best Practices + +1. **Always check `task_execution_type`** before attempting to trigger automation +2. **Store the automation `id`** (execution ID) for tracking and debugging purposes +3. **Implement proper timeout handling** to avoid infinite polling +4. **Log all API calls** for troubleshooting +5. **Handle network errors** gracefully with retry logic +6. **Update workflow tasks promptly** after automation completion +7. **Monitor long-running automations** and alert on timeouts +8. **Handle both top-level steps and sections** when searching for tasks in the flow +9. **Use correct field names**: `executionId` and `executionStatus` when updating workflow tasks + +--- + +## API Reference Summary + +| Endpoint | Method | Purpose | +|----------|--------|---------| +| `/v1/entity:search` | POST | Search entities with workflow context | +| `/v1/workflows/executions/{id}` | GET | Retrieve full workflow execution | +| `/v1/automation/executions` | POST | Trigger automation execution | +| `/v1/automation/executions/{execution_id}` | GET | Check automation execution status | +| `/v1/workflows/executions/{id}/steps/{step_id}` | PATCH | Update workflow task status | + +--- + +## Related Documentation + +- [Workflows API Reference](/api-reference/workflows) +- [Automation API Reference](/api-reference/automation) +- [Entity API Reference](/api-reference/entities) +- [Workflow Execution Types](/workflows/execution-types)