Agent Task Configuration Guide
A comprehensive guide to creating and configuring agent tasks in PlanOps.
Table of Contents
- Overview
- Task Structure
- Input Schema
- Output Schema
- Workflow Steps
- Templating & Variables
- Best Practices
- Complete Examples
Overview
Agent tasks are configurable, multi-step workflows that enable AI-powered automation within PlanOps. Tasks can:
- Collect structured user input via dynamic forms
- Call internal APIs and external services
- Execute LLM prompts for analysis and content generation
- Transform and process data
- Create reports, forms, and other outputs
- Automate complex document workflows
Key Concepts
| Concept | Description |
|---|---|
| Input Schema | Defines what information the task needs from the user |
| Output Schema | Defines what the task produces |
| Workflow | Ordered sequence of steps that execute the task logic |
| Context | Runtime variables like project_id, organization_id, user_id |
| Templating | Jinja2-based variable substitution throughout the task |
Task Structure
Every agent task follows this top-level structure:
{
"task_id": "my-task-slug",
"name": "Human-Readable Task Name",
"description": "Clear description of what the task does and when to use it.",
"input_schema": { ... },
"output_schema": { ... },
"workflow": { ... },
"category": "safety",
"tags": ["compliance", "review", "documents"],
"icon": "ShieldCheck",
"color": "#DC2626",
"active": true,
"featured": false,
"required_permissions": ["documents:read", "reports:write"],
"examples": [
{
"input": { ... },
"description": "Example use case description"
}
]
}
Required Fields
| Field | Type | Description |
|---|---|---|
task_id | string | Unique slug identifier (lowercase, hyphens) |
name | string | Display name (4+ characters) |
description | string | What the task does (40+ characters recommended) |
input_schema | object | Input field definitions |
workflow | object | Execution steps |
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
output_schema | object | null | Output field definitions |
category | string | null | Category for grouping (e.g., "safety", "documents") |
tags | array | [] | Tags for discovery and filtering |
icon | string | null | Lucide icon name |
color | string | null | Hex color code |
active | boolean | true | Whether task is available |
featured | boolean | false | Highlight in UI |
required_permissions | array | null | Permissions needed to run |
organization_id | ObjectId | null | Scope to organization |
project_id | ObjectId | null | Scope to project |
examples | array | null | Example inputs for documentation |
Input Schema
The input_schema defines the form users see when running a task.
Structure
{
"input_schema": {
"fields": [
{
"name": "field_name",
"label": "Field Label",
"type": "text",
"description": "Help text for the user",
"required": true,
"default_value": null,
"placeholder": "Enter value..."
}
],
"groups": null
}
}
Field Types
Text Fields
{
"name": "project_description",
"label": "Project Description",
"type": "text",
"description": "Describe the project scope and objectives",
"required": true,
"min_length": 20,
"max_length": 5000,
"placeholder": "Enter project details..."
}
Validation options:
min_length/max_length: Character limitspattern: Regex validation (e.g.,"^[A-Z]{2}-\\d{4}$"for format "XX-0000")
Number Fields
{
"name": "period_days",
"label": "Period (Days)",
"type": "number",
"description": "Number of days to search",
"required": false,
"default_value": 7,
"min_value": 1,
"max_value": 365
}
Boolean Fields
{
"name": "include_attachments",
"label": "Include Attachments",
"type": "boolean",
"description": "Include file attachments in the review",
"required": false,
"default_value": true
}
Date/DateTime Fields
{
"name": "start_date",
"label": "Start Date",
"type": "date",
"description": "Project start date",
"required": true,
"min_date": "2024-01-01",
"max_date": "2030-12-31",
"allow_future": true
}
{
"name": "meeting_time",
"label": "Meeting Time",
"type": "datetime",
"description": "Scheduled meeting date and time",
"required": true
}
Select Fields (Static Options)
{
"name": "document_status",
"label": "Document Status",
"type": "select",
"description": "Filter by document status",
"required": false,
"default_value": "FOR_REVIEW",
"options": [
{"label": "Draft", "value": "DRAFT"},
{"label": "For Review", "value": "FOR_REVIEW"},
{"label": "Approved", "value": "APPROVED"},
{"label": "Construction", "value": "CONSTRUCTION"}
]
}
Select Fields (Dynamic Options)
Dynamic options load choices from an API at runtime:
{
"name": "organization_id",
"label": "Originating Organisation",
"type": "select",
"description": "Filter by organisation",
"required": false,
"dynamic_options": {
"url": "/api/v1/projects/{{context.project_id}}/organizations",
"method": "GET",
"query_params": {
"size": 100
},
"data_path": "items",
"label_field": "name",
"value_field": "_id"
},
"placeholder": "Select an organisation"
}
Dynamic options configuration:
| Property | Required | Description |
|---|---|---|
url | Yes | API endpoint (supports templating) |
method | Yes | HTTP method (usually "GET") |
query_params | No | Query parameters object |
data_path | Yes | Path to array in response (e.g., "items") |
label_field | Yes | Field to display as label |
value_field | Yes | Field to use as value |
Common dynamic option patterns:
// Project reports
{
"url": "/api/v1/reports",
"method": "GET",
"query_params": {
"project_id": "{{context.project_id}}",
"limit": 100
},
"data_path": "items",
"label_field": "title",
"value_field": "id"
}
// Form templates
{
"url": "/api/v1/forms/templates",
"method": "GET",
"query_params": {
"project_id": "{{context.project_id}}",
"limit": 100
},
"data_path": "items",
"label_field": "name",
"value_field": "id"
}
// Project users
{
"url": "/api/v1/projects/{{context.project_id}}/members",
"method": "GET",
"data_path": "items",
"label_field": "name",
"value_field": "id"
}
// Agents
{
"url": "/api/v1/agents/",
"method": "GET",
"query_params": {"limit": 100},
"data_path": "items",
"label_field": "title",
"value_field": "id"
}
Multi-Select Fields
{
"name": "file_types",
"label": "File Types",
"type": "multi_select",
"description": "Select file types to include",
"required": false,
"default_value": ["pdf", "docx"],
"options": [
{"label": "PDF", "value": "pdf"},
{"label": "Word (DOCX)", "value": "docx"},
{"label": "Excel (XLSX)", "value": "xlsx"},
{"label": "AutoCAD (DWG)", "value": "dwg"}
]
}
Document Picker
{
"name": "rams_document",
"label": "RAMS Document",
"type": "document_picker",
"description": "Select the RAMS document to review",
"required": true,
"filter_by": {
"document_type": ["METHOD_STATEMENT", "RISK_ASSESSMENT"]
}
}
File Upload
{
"name": "supporting_files",
"label": "Supporting Documents",
"type": "file_upload",
"description": "Upload supporting documents",
"required": false,
"multiple": true,
"filter_by": {
"file_types": ["pdf", "docx", "doc", "txt", "jpg", "png"]
}
}
Accessing uploaded file content:
{{input.supporting_files.text_content}}- Extracted text (single file){{input.supporting_files.filename}}- Original filename- For multiple files, iterate:
{% for file in input.supporting_files %}...{% endfor %}
User Picker
{
"name": "assigned_reviewer",
"label": "Assigned Reviewer",
"type": "user_picker",
"description": "Select the person to review this document",
"required": true
}
Project Picker
{
"name": "target_project",
"label": "Target Project",
"type": "project_picker",
"description": "Select the project to apply this to",
"required": true
}
Report Picker
{
"name": "source_report",
"label": "Source Report",
"type": "report_picker",
"description": "Select an existing report as the basis",
"required": false
}
Conditional Fields
Fields can show/hide based on other field values:
{
"name": "source_type",
"label": "Source Type",
"type": "select",
"required": true,
"options": [
{"label": "Direct Text", "value": "direct_text"},
{"label": "From Report", "value": "from_report"}
]
},
{
"name": "direct_text",
"label": "Enter Text",
"type": "text",
"required": false,
"depends_on": "source_type",
"show_when": {"source_type": "direct_text"}
},
{
"name": "report_id",
"label": "Select Report",
"type": "select",
"required": false,
"depends_on": "source_type",
"show_when": {"source_type": "from_report"},
"dynamic_options": { ... }
}
Output Schema
The output_schema defines what the task produces.
Structure
{
"output_schema": {
"fields": [
{
"name": "summary",
"label": "Summary",
"type": "text",
"description": "Task execution summary",
"source": "{{step.summary_step.summary_output.response}}"
},
{
"name": "outputs",
"label": "Outputs",
"type": "array",
"description": "Generated output references",
"source": "[{\"type\": \"report\", \"id\": \"{{step.create_report.report.id}}\", ...}]"
},
{
"name": "completion_time",
"label": "Completion Time",
"type": "string",
"source": "{{now().isoformat()}}"
}
],
"format": "json"
}
}
Standard Output Pattern
All tasks should include a standardized summary output:
{
"summary": "Brief description for dashboard display",
"outputs": [
{
"type": "report",
"id": "{{report_id}}",
"title": "Report Title",
"url": "/api/v1/reports/{{report_id}}",
"category": "Safety"
}
],
"completion_time": "{{timestamp}}"
}
Workflow Steps
The workflow section defines the execution logic.
Structure
{
"workflow": {
"allow_parallel": false,
"steps": [
{
"id": "step_id",
"name": "Step Name",
"type": "step_type",
"description": "What this step does",
...step-specific configuration...
"output_key": "result_key"
}
]
}
}
Step Types
1. API Call (api_call)
Call internal or external APIs:
{
"id": "fetch_documents",
"name": "Fetch Project Documents",
"type": "api_call",
"endpoint": "/api/v1/documents/search",
"method": "GET",
"parameters": {
"project_id": "{{context.project_id}}",
"query": "{{input.search_query}}",
"limit": 100
},
"output_key": "documents"
}
POST with body:
{
"id": "create_report",
"name": "Create Report",
"type": "api_call",
"endpoint": "/api/v1/reports",
"method": "POST",
"parameters": {
"title": "{{input.report_title}}",
"project_id": "{{context.project_id}}",
"content": "{{step.generate_content.content.response}}",
"category": "Safety",
"format": "Text",
"tags": ["auto-generated", "{{input.category}}"]
},
"output_key": "report"
}
Using body_source for complex payloads:
{
"id": "create_task",
"name": "Create Task via API",
"type": "api_call",
"endpoint": "/api/v1/agents/{{input.agent_id}}/tasks",
"method": "POST",
"config": {
"body_source": "step.build_payload.task_payload"
},
"output_key": "created_task"
}
2. Tool Call (tool_call)
Call built-in agent tools:
{
"id": "get_programme",
"name": "Get Project Programme",
"type": "tool_call",
"endpoint": "get_project_programme",
"parameters": {
"project_id": "{{context.project_id}}",
"include_milestones": true,
"include_tasks": true,
"include_critical_path": true
},
"output_key": "programme_data",
"on_error": "continue"
}
Available core tools:
| Tool | Description | Parameters |
|---|---|---|
create_project_tasks | Create tasks in a project | project_id, tasks array |
create_form_instances | Create form instances from templates | See form automation |
submit_form | Submit values to complete a form | form_instance_id, form_values |
complete_form_section | Complete a workflow section | form_instance_id, form_section_id, form_values |
search_project_users | Search users in project | project_id, role, search_query, limit |
assign_task | Assign task to user | task_id, user_id |
get_project_programme | Get programme/schedule data | project_id |
3. LLM Prompt (llm_prompt)
Execute AI prompts for analysis, generation, or transformation:
{
"id": "generate_summary",
"name": "Generate Summary",
"type": "llm_prompt",
"parameters": {
"model": "gpt-5-mini",
"fallbacks": ["gpt-5-nano"],
"temperature": 0.3
},
"prompt_template": "You are a construction safety specialist.\n\nReview the following documents:\n{{step.fetch_docs.documents}}\n\nProvide a concise summary focusing on:\n1. Key safety concerns\n2. Compliance gaps\n3. Recommended actions\n\nFormat as markdown.",
"output_key": "summary"
}
⚠️ Critical: LLM Output Structure
All LLM steps produce a standard output object:
{
"response": "<the actual generated text>",
"prompt": "<the rendered prompt sent to LLM>",
"raw_response": "<API metadata>"
}
When referencing LLM output in subsequent steps:
✅ Correct: {{step.generate_summary.summary.response}}
❌ Wrong: {{step.generate_summary.summary}} (returns full object with metadata)
Image generation mode:
{
"id": "create_image",
"name": "Generate Organogram Image",
"type": "llm_prompt",
"parameters": {
"model": "gemini/imagen-4.0-generate-001",
"mode": "image",
"fallbacks": ["dall-e-3"]
},
"prompt_template": "Create a professional organogram showing: {{input.structure_description}}",
"output_key": "organogram_image"
}
4. Data Transformation (data_transformation)
Transform data between steps using Jinja2 templates:
{
"id": "prepare_context",
"name": "Prepare Context",
"type": "data_transformation",
"transformation": "render_template",
"transformation_config": {
"template": "PROJECT: {{context.project_name}}\nDOCUMENTS:\n{% for doc in step.fetch_docs.documents %}* {{doc.title}} ({{doc.status}})\n{% endfor %}"
},
"output_key": "compiled_context"
}
Building JSON objects:
{
"id": "build_summary",
"name": "Build Summary Object",
"type": "data_transformation",
"transformation": "render_template",
"transformation_config": {
"template": "{\"summary\": \"Generated report for {{input.topic}}\", \"outputs\": [{\"type\": \"report\", \"id\": \"{{step.create_report.report.id}}\", \"title\": \"{{input.topic}} Report\", \"url\": \"/api/v1/reports/{{step.create_report.report.id}}\"}], \"completion_time\": \"{{now().isoformat()}}\"}"
},
"output_key": "dashboard_summary"
}
Filtering and processing arrays:
{
"transformation_config": {
"template": "{% set ns = namespace(filtered=[]) %}{% for doc in step.fetch_docs.documents %}{% if doc.status == 'APPROVED' %}{% set ns.filtered = ns.filtered + [doc] %}{% endif %}{% endfor %}{{ ns.filtered | tojson }}"
}
}
5. Form Instance Create (form_instance_create)
Create form instances for digital workflows:
{
"id": "create_signoff_form",
"name": "Create Sign-off Form",
"type": "form_instance_create",
"form_template_id": "{{input.form_template_id | default(context.default_form_id)}}",
"form_values": {
"topic": "{{input.topic}}",
"location": "{{input.location}}",
"date": "{{now().strftime('%Y-%m-%d')}}"
},
"form_config": {
"title": "Sign-off: {{input.topic}}",
"create_public_links": true
},
"condition": "{{input.create_form == true}}",
"on_error": "continue",
"output_key": "form_instance"
}
Output includes:
instance_id: The created form instance IDpublic_share_url: URL for external access (ifcreate_public_links: true)status: Form status
Bulk form creation:
{
"type": "form_instance_create",
"form_template_id": "{{input.form_template_id}}",
"form_instances": [
{"form_values": {"name": "Item 1"}, "form_config": {"title": "Form 1"}},
{"form_values": {"name": "Item 2"}, "form_config": {"title": "Form 2"}}
],
"output_key": "forms"
}
6. Form Submit (form_submit)
Submit values to complete a form:
{
"id": "submit_inspection",
"name": "Submit Inspection Form",
"type": "form_submit",
"form_instance_id": "{{step.create_form.form_instance.instance_id}}",
"form_values": {
"inspector_name": "{{context.user_name}}",
"inspection_date": "{{now().strftime('%Y-%m-%d')}}",
"findings": "{{step.generate_findings.findings.response}}"
},
"output_key": "submission"
}
7. Form Section Complete (form_section_complete)
Complete a specific section in a multi-section form:
{
"id": "complete_review_section",
"name": "Complete Review Section",
"type": "form_section_complete",
"form_instance_id": "{{input.form_instance_id}}",
"form_section_id": "review_section",
"form_values": {
"reviewer_comments": "{{step.generate_review.comments.response}}",
"review_status": "APPROVED"
},
"output_key": "section_completion"
}
Step Control
Conditional Execution
{
"id": "optional_step",
"name": "Generate Extra Content",
"type": "llm_prompt",
"condition": "{{input.include_extra == true}}",
...
}
{
"condition": "{{step.fetch_docs.documents | length > 0}}"
}
Dependencies
{
"id": "final_step",
"name": "Generate Final Report",
"type": "api_call",
"depends_on": ["step_a", "step_b", "step_c"],
...
}
Error Handling
{
"id": "risky_step",
"name": "External API Call",
"type": "api_call",
"on_error": "continue",
"retry_count": 3,
...
}
Options for on_error:
"continue": Continue to next step"stop": Halt execution"retry": Retry withretry_count
Iteration
Execute a step for each item in an array:
{
"id": "process_documents",
"name": "Process Each Document",
"type": "llm_prompt",
"iterate_over": "{{step.fetch_docs.documents}}",
"prompt_template": "Summarize document: {{item.title}}\n\nContent: {{item.content}}",
"output_key": "summaries"
}
Use {{item}} for current item and {{item_index}} for 0-based index.
Templating & Variables
Available Variables
Context Variables
| Variable | Description |
|---|---|
{{context.project_id}} | Current project ID |
{{context.organization_id}} | Current organization ID |
{{context.user_id}} | Executing user's ID |
{{context.user_name}} | Executing user's name |
{{context.project_name}} | Project name |
Input Variables
Access user inputs: {{input.field_name}}
{{input.document_id}}
{{input.search_query | default('', true)}}
{{input.tags | join(', ')}}
Step Output Variables
Access previous step outputs: {{step.step_id.output_key}}
{{step.fetch_docs.documents}}
{{step.generate_report.content.response}}
{{step.create_report.report.id}}
Jinja2 Filters
| Filter | Example | Description |
|---|---|---|
default | {{value | default('N/A', true)}} | Fallback value |
join | {{list | join(', ')}} | Join array |
length | {{items | length}} | Count items |
tojson | {{obj | tojson}} | Convert to JSON |
upper / lower | {{text | upper}} | Case conversion |
trim | {{text | trim}} | Remove whitespace |
replace | {{text | replace('a', 'b')}} | String replace |
Date Functions
{{now()}} # Current datetime
{{now().strftime('%Y-%m-%d')}} # Format date
{{now().isoformat()}} # ISO format
{{(now() - timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%SZ')}} # 7 days ago
Conditional Logic in Templates
{% if input.include_details %}
Additional details: {{input.details}}
{% endif %}
{% if step.fetch_docs.documents | length > 0 %}
Found {{step.fetch_docs.documents | length}} documents
{% else %}
No documents found
{% endif %}
Loops in Templates
{% for doc in step.fetch_docs.documents %}
- {{doc.title}} ({{doc.status}})
{% endfor %}
Best Practices
1. Task Design
- Single purpose: Each task should do one thing well
- Clear naming: Use descriptive names and task_ids
- Comprehensive description: Explain what the task does and when to use it
- Required vs optional: Only mark truly necessary fields as required
- Default values: Provide sensible defaults where appropriate
2. Input Validation
{
"name": "email",
"type": "text",
"required": true,
"pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"placeholder": "user@example.com"
}
3. Error Handling
- Use
on_error: "continue"for non-critical steps - Provide fallback models for LLM steps
- Check for empty arrays before processing
- Use
| default('', true)for optional values
4. LLM Prompts
- Be specific about the expected output format
- Include role/persona context
- Provide examples when helpful
- Request structured output (markdown, JSON) for consistency
- Always use
.responsewhen referencing LLM output
5. Summary Step
Every task should end with a summary step:
{
"id": "summary",
"name": "Generate Summary",
"type": "data_transformation",
"transformation": "render_template",
"transformation_config": {
"template": "{\"summary\": \"Completed analysis of {{input.topic}}\", \"outputs\": [{\"type\": \"report\", \"id\": \"{{step.create_report.report.id}}\", \"title\": \"Analysis Report\", \"url\": \"/api/v1/reports/{{step.create_report.report.id}}\", \"category\": \"Analysis\"}], \"completion_time\": \"{{now().isoformat()}}\"}"
},
"depends_on": ["create_report"],
"output_key": "dashboard_summary"
}
6. Security
- Never expose sensitive data in prompts
- Use required_permissions appropriately
- Validate user inputs server-side
- Scope tasks to organization/project when appropriate
Complete Examples
Example 1: Simple Document Review
A task that reviews documents uploaded in the last N days:
{
"task_id": "recent-document-review",
"name": "Recent Document Review",
"description": "Review documents uploaded in the specified period and generate a summary report.",
"input_schema": {
"fields": [
{
"name": "period_days",
"label": "Period (Days)",
"type": "number",
"description": "Number of days to look back",
"required": false,
"default_value": 7,
"min_value": 1,
"max_value": 90
},
{
"name": "document_types",
"label": "Document Types",
"type": "multi_select",
"description": "Filter by document type",
"required": false,
"options": [
{"label": "Drawing", "value": "DRAWING"},
{"label": "Specification", "value": "SPECIFICATION"},
{"label": "Report", "value": "REPORT"}
]
}
]
},
"output_schema": {
"fields": [
{
"name": "summary",
"label": "Summary",
"type": "text",
"source": "{{step.summary.result.response}}"
},
{
"name": "report_id",
"label": "Report ID",
"type": "string",
"source": "{{step.create_report.report.id}}"
}
],
"format": "json"
},
"workflow": {
"allow_parallel": false,
"steps": [
{
"id": "fetch_documents",
"name": "Fetch Recent Documents",
"type": "api_call",
"endpoint": "/api/v1/documents/search",
"method": "GET",
"parameters": {
"project_id": "{{context.project_id}}",
"start_date": "{{(now() - timedelta(days=input.period_days | default(7))).strftime('%Y-%m-%dT%H:%M:%SZ')}}",
"document_type": "{{input.document_types | join(',') if input.document_types else ''}}",
"limit": 100
},
"output_key": "documents"
},
{
"id": "analyze_documents",
"name": "Analyze Documents",
"type": "llm_prompt",
"parameters": {
"model": "gpt-5-mini",
"fallbacks": ["gpt-5-nano"],
"temperature": 0.2
},
"prompt_template": "You are a document management specialist.\n\nReview these {{step.fetch_documents.documents | length}} documents uploaded in the last {{input.period_days | default(7)}} days:\n\n{{step.fetch_documents.documents | tojson}}\n\nProvide:\n1. Summary of document activity\n2. Key observations\n3. Any concerns or recommendations\n\nFormat as clean markdown.",
"output_key": "analysis"
},
{
"id": "create_report",
"name": "Create Report",
"type": "api_call",
"endpoint": "/api/v1/reports",
"method": "POST",
"parameters": {
"title": "Document Review - Last {{input.period_days | default(7)}} Days",
"project_id": "{{context.project_id}}",
"content": "{{step.analyze_documents.analysis.response}}",
"category": "Documents",
"format": "Text",
"tags": ["document-review", "auto-generated"]
},
"output_key": "report"
},
{
"id": "summary",
"name": "Generate Summary",
"type": "llm_prompt",
"parameters": {
"model": "gpt-5-nano"
},
"prompt_template": "Create a one-sentence summary: Reviewed {{step.fetch_documents.documents | length}} documents from the last {{input.period_days | default(7)}} days.",
"depends_on": ["create_report"],
"output_key": "result"
}
]
},
"category": "documents",
"tags": ["review", "documents", "analysis"],
"icon": "FileSearch",
"color": "#2563eb",
"active": true
}
Example 2: Toolbox Talk with Form
A task that generates a toolbox talk and creates a digital sign-off form:
{
"task_id": "toolbox-talk-generator",
"name": "Toolbox Talk Generator",
"description": "Create a toolbox talk on any safety topic with optional digital sign-off form for worker attendance.",
"input_schema": {
"fields": [
{
"name": "topic",
"label": "Topic",
"type": "text",
"description": "The safety topic for the toolbox talk",
"required": true,
"min_length": 5,
"placeholder": "e.g., Working at height, Manual handling"
},
{
"name": "audience",
"label": "Audience",
"type": "text",
"description": "Who is this talk for?",
"required": false,
"placeholder": "e.g., Scaffolders, All trades"
},
{
"name": "create_signoff_form",
"label": "Create Digital Sign-off",
"type": "boolean",
"description": "Create a form for workers to sign attendance",
"required": false,
"default_value": true
}
]
},
"workflow": {
"steps": [
{
"id": "generate_talk",
"name": "Generate Toolbox Talk",
"type": "llm_prompt",
"parameters": {
"model": "gpt-5-mini",
"temperature": 0.3
},
"prompt_template": "Create a 10-minute toolbox talk on: {{input.topic}}\n\nAudience: {{input.audience | default('General workforce')}}\n\nInclude:\n- Key hazards\n- Control measures\n- Questions for discussion\n- Key takeaways\n\nUse simple, practical language.",
"output_key": "content"
},
{
"id": "create_report",
"name": "Save as Report",
"type": "api_call",
"endpoint": "/api/v1/reports",
"method": "POST",
"parameters": {
"title": "Toolbox Talk: {{input.topic}}",
"project_id": "{{context.project_id}}",
"content": "{{step.generate_talk.content.response}}",
"category": "Safety",
"tags": ["toolbox-talk", "health-safety"]
},
"output_key": "report"
},
{
"id": "create_form",
"name": "Create Sign-off Form",
"type": "form_instance_create",
"condition": "{{input.create_signoff_form | default(true)}}",
"form_template_id": "{{context.toolbox_talk_form_template_id}}",
"form_values": {
"topic": "{{input.topic}}",
"date": "{{now().strftime('%Y-%m-%d')}}",
"report_id": "{{step.create_report.report.id}}"
},
"form_config": {
"title": "Attendance: {{input.topic}}",
"create_public_links": true
},
"on_error": "continue",
"output_key": "signoff_form"
},
{
"id": "summary",
"name": "Build Summary",
"type": "data_transformation",
"transformation": "render_template",
"transformation_config": {
"template": "{% set outputs = [{\"type\": \"report\", \"id\": step.create_report.report.id, \"title\": \"Toolbox Talk: \" ~ input.topic, \"url\": \"/api/v1/reports/\" ~ step.create_report.report.id}] %}{% if step.create_form.signoff_form.public_share_url %}{% set _ = outputs.append({\"type\": \"form\", \"id\": step.create_form.signoff_form.instance_id, \"title\": \"Sign-off Form\", \"url\": step.create_form.signoff_form.public_share_url}) %}{% endif %}{\"summary\": \"Generated toolbox talk on '{{input.topic}}'\", \"outputs\": {{outputs | tojson}}, \"completion_time\": \"{{now().isoformat()}}\"}"
},
"depends_on": ["create_form"],
"output_key": "dashboard_summary"
}
]
},
"category": "health-safety",
"tags": ["toolbox-talk", "safety", "training"],
"icon": "Shield",
"active": true
}
Example 3: Multi-Source Analysis with Conditional Steps
{
"task_id": "rams-compliance-check",
"name": "RAMS Compliance Check",
"description": "Check RAMS documents against ITP requirements with optional comparison to previous assessment.",
"input_schema": {
"fields": [
{
"name": "itp_report_id",
"label": "ITP Report",
"type": "select",
"description": "Select the pre-appointment ITP",
"required": true,
"dynamic_options": {
"url": "/api/v1/reports",
"method": "GET",
"query_params": {
"project_id": "{{context.project_id}}",
"category": "ITP",
"limit": 50
},
"data_path": "items",
"label_field": "title",
"value_field": "id"
}
},
{
"name": "rams_files",
"label": "RAMS Documents",
"type": "file_upload",
"description": "Upload RAMS documents to check",
"required": true,
"multiple": true,
"filter_by": {
"file_types": ["pdf", "docx"]
}
},
{
"name": "previous_assessment_id",
"label": "Previous Assessment (Optional)",
"type": "select",
"description": "Compare against a previous assessment",
"required": false,
"dynamic_options": {
"url": "/api/v1/reports",
"method": "GET",
"query_params": {
"project_id": "{{context.project_id}}",
"tags": "rams-assessment",
"limit": 20
},
"data_path": "items",
"label_field": "title",
"value_field": "id"
}
}
]
},
"workflow": {
"steps": [
{
"id": "fetch_itp",
"name": "Fetch ITP",
"type": "api_call",
"endpoint": "/api/v1/reports/{{input.itp_report_id}}",
"method": "GET",
"output_key": "itp"
},
{
"id": "fetch_previous",
"name": "Fetch Previous Assessment",
"type": "api_call",
"endpoint": "/api/v1/reports/{{input.previous_assessment_id}}",
"method": "GET",
"condition": "{{input.previous_assessment_id}}",
"on_error": "continue",
"output_key": "previous"
},
{
"id": "prepare_rams_content",
"name": "Prepare RAMS Content",
"type": "data_transformation",
"transformation": "render_template",
"transformation_config": {
"template": "{% for file in input.rams_files %}### {{file.filename}}\n{{file.text_content}}\n\n{% endfor %}"
},
"output_key": "rams_content"
},
{
"id": "analyze",
"name": "Analyze Compliance",
"type": "llm_prompt",
"parameters": {
"model": "gpt-5.2",
"fallbacks": ["gpt-4.1"],
"temperature": 0.2
},
"prompt_template": "You are a RAMS compliance auditor.\n\n## ITP Requirements\n{{step.fetch_itp.itp.content}}\n\n## RAMS Documents\n{{step.prepare_rams_content.rams_content}}\n\n{% if step.fetch_previous.previous %}## Previous Assessment\n{{step.fetch_previous.previous.content}}\n\nCompare with previous and note improvements/regressions.{% endif %}\n\nAudit the RAMS against the ITP. Identify gaps and weaknesses only - no recommendations.\n\nFormat:\n1. Executive Summary\n2. Compliance Gaps by ITP Section\n3. Severity Classification (Critical/Major/Minor)\n{% if step.fetch_previous.previous %}4. Change Analysis vs Previous{% endif %}",
"output_key": "analysis"
},
{
"id": "create_report",
"name": "Create Assessment Report",
"type": "api_call",
"endpoint": "/api/v1/reports",
"method": "POST",
"parameters": {
"title": "RAMS Compliance Assessment - {{now().strftime('%Y-%m-%d')}}",
"project_id": "{{context.project_id}}",
"content": "{{step.analyze.analysis.response}}",
"category": "Safety",
"tags": ["rams-assessment", "compliance", "auto-generated"]
},
"output_key": "report"
}
]
},
"category": "safety",
"tags": ["rams", "compliance", "audit"],
"required_permissions": ["documents:read", "reports:write"],
"active": true
}
API Endpoints Reference
Common Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/v1/search | GET | Unified search |
/api/v1/documents/search | GET | Document search |
/api/v1/documents | GET | List documents |
/api/v1/documents/{id} | GET | Get document |
/api/v1/reports | GET/POST | List/create reports |
/api/v1/reports/{id} | GET/PATCH | Get/update report |
/api/v1/projects/{id} | GET | Get project info |
/api/v1/projects/{id}/members | GET | List project members |
/api/v1/projects/{id}/organizations | GET | List project orgs |
/api/v1/forms/templates | GET | List form templates |
/api/v1/agents/{id}/tasks | POST | Create task |
/api/v1/agents/{id}/tasks/{task_id} | PATCH | Update task |
Troubleshooting
Common Issues
1. LLM output contains metadata instead of text
❌ {{step.generate.output}}
✅ {{step.generate.output.response}}
2. Empty array in condition
"condition": "{{step.fetch.items | length > 0}}"
3. Template syntax error
- Escape quotes in JSON:
\"or use single quotes in Jinja - Use
| default('', true)for potentially null values
4. Step not executing
- Check
conditionsyntax - Verify
depends_onstep IDs exist - Check for errors in previous steps
5. API call returning empty
- Verify endpoint URL
- Check method (GET vs POST)
- Confirm parameters are templated correctly
Version History
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2026-01-31 | Initial comprehensive guide |