@devrev-computer/skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/bin/install.mjs +158 -0
- package/package.json +33 -0
- package/skills/account-evaluation/account-evaluation.md +64 -0
- package/skills/account-research/account-research.md +323 -0
- package/skills/account-research/references/signals-guide.md +52 -0
- package/skills/create-workflow-template/create-workflow-template.md +1091 -0
- package/skills/create-workflow-template/examples/3592-Generate rca from pia-template.json +1 -0
- package/skills/create-workflow-template/examples/4392-Async opportunity review agent-template.json +1 -0
- package/skills/create-workflow-template/examples/4441-Ticket escalator from customer message-template.json +1 -0
- package/skills/create-workflow-template/examples/4505-Auto-update issue tcd as end of sprint date-template.json +1 -0
- package/skills/create-workflow-template/examples/5040-Devrevu - enablement journey - poc emails-template.json +1 -0
- package/skills/create-workflow-template/examples/5158-Devrevu - enablement journey - mailing for non enablement journey users-template.json +1 -0
- package/skills/create-workflow-template/examples/5216-Account segment missing notification-template.json +1 -0
- package/skills/create-workflow-template/examples/working-csat-score-on-ticket-resolved.json +1 -0
- package/skills/create-workflow-template/examples/working-enhancement-replace-agent.json +1 -0
- package/skills/create-workflow-template/examples/working-invoke-code-sample.json +1 -0
- package/skills/create-workflow-template/examples/working-loop-variable-sample.json +1 -0
- package/skills/create-workflow-template/operations/actions.md +2919 -0
- package/skills/create-workflow-template/operations/blockings.md +38 -0
- package/skills/create-workflow-template/operations/controls.md +108 -0
- package/skills/create-workflow-template/operations/schema-index.md +166 -0
- package/skills/create-workflow-template/operations/schemas/account_created.md +58 -0
- package/skills/create-workflow-template/operations/schemas/account_updated.md +73 -0
- package/skills/create-workflow-template/operations/schemas/add_comment.md +29 -0
- package/skills/create-workflow-template/operations/schemas/airdrop_sync_run_started.md +33 -0
- package/skills/create-workflow-template/operations/schemas/airdrop_sync_run_status_updated.md +35 -0
- package/skills/create-workflow-template/operations/schemas/article_created.md +96 -0
- package/skills/create-workflow-template/operations/schemas/article_updated.md +135 -0
- package/skills/create-workflow-template/operations/schemas/ask_ai.md +11 -0
- package/skills/create-workflow-template/operations/schemas/classify_object.md +22 -0
- package/skills/create-workflow-template/operations/schemas/contact_created.md +43 -0
- package/skills/create-workflow-template/operations/schemas/contact_updated.md +65 -0
- package/skills/create-workflow-template/operations/schemas/conversation_created.md +108 -0
- package/skills/create-workflow-template/operations/schemas/conversation_sla_tracker_updated.md +46 -0
- package/skills/create-workflow-template/operations/schemas/conversation_updated.md +130 -0
- package/skills/create-workflow-template/operations/schemas/convert_conversation_to_ticket.md +13 -0
- package/skills/create-workflow-template/operations/schemas/create_account.md +62 -0
- package/skills/create-workflow-template/operations/schemas/create_article.md +79 -0
- package/skills/create-workflow-template/operations/schemas/create_brand.md +42 -0
- package/skills/create-workflow-template/operations/schemas/create_contact.md +65 -0
- package/skills/create-workflow-template/operations/schemas/create_dm.md +53 -0
- package/skills/create-workflow-template/operations/schemas/create_enhancement.md +63 -0
- package/skills/create-workflow-template/operations/schemas/create_incident.md +136 -0
- package/skills/create-workflow-template/operations/schemas/create_issue.md +150 -0
- package/skills/create-workflow-template/operations/schemas/create_meeting.md +105 -0
- package/skills/create-workflow-template/operations/schemas/create_opportunity.md +123 -0
- package/skills/create-workflow-template/operations/schemas/create_ticket.md +184 -0
- package/skills/create-workflow-template/operations/schemas/csat_response_received.md +73 -0
- package/skills/create-workflow-template/operations/schemas/dev_user_created.md +54 -0
- package/skills/create-workflow-template/operations/schemas/dev_user_updated.md +99 -0
- package/skills/create-workflow-template/operations/schemas/enhancement_created.md +46 -0
- package/skills/create-workflow-template/operations/schemas/enhancement_updated.md +89 -0
- package/skills/create-workflow-template/operations/schemas/evaluate_sentiment.md +14 -0
- package/skills/create-workflow-template/operations/schemas/execute_metric_action.md +11 -0
- package/skills/create-workflow-template/operations/schemas/feature_created.md +40 -0
- package/skills/create-workflow-template/operations/schemas/for_each.md +45 -0
- package/skills/create-workflow-template/operations/schemas/get_account.md +59 -0
- package/skills/create-workflow-template/operations/schemas/get_airdrop_sync_unit.md +32 -0
- package/skills/create-workflow-template/operations/schemas/get_brand.md +40 -0
- package/skills/create-workflow-template/operations/schemas/get_complete_enhancement_details.md +13 -0
- package/skills/create-workflow-template/operations/schemas/get_conversation.md +120 -0
- package/skills/create-workflow-template/operations/schemas/get_customer.md +60 -0
- package/skills/create-workflow-template/operations/schemas/get_enhancement.md +66 -0
- package/skills/create-workflow-template/operations/schemas/get_feature.md +56 -0
- package/skills/create-workflow-template/operations/schemas/get_incident.md +85 -0
- package/skills/create-workflow-template/operations/schemas/get_issue.md +117 -0
- package/skills/create-workflow-template/operations/schemas/get_kg_schema.md +23 -0
- package/skills/create-workflow-template/operations/schemas/get_meeting.md +87 -0
- package/skills/create-workflow-template/operations/schemas/get_metric_trackers.md +20 -0
- package/skills/create-workflow-template/operations/schemas/get_node_schema.md +29 -0
- package/skills/create-workflow-template/operations/schemas/get_opportunity.md +93 -0
- package/skills/create-workflow-template/operations/schemas/get_org_user.md +57 -0
- package/skills/create-workflow-template/operations/schemas/get_org_user_preference.md +40 -0
- package/skills/create-workflow-template/operations/schemas/get_part.md +55 -0
- package/skills/create-workflow-template/operations/schemas/get_self.md +54 -0
- package/skills/create-workflow-template/operations/schemas/get_session_details.md +45 -0
- package/skills/create-workflow-template/operations/schemas/get_sprint_board.md +103 -0
- package/skills/create-workflow-template/operations/schemas/get_ticket.md +136 -0
- package/skills/create-workflow-template/operations/schemas/get_workspace.md +21 -0
- package/skills/create-workflow-template/operations/schemas/go_back.md +13 -0
- package/skills/create-workflow-template/operations/schemas/http.md +38 -0
- package/skills/create-workflow-template/operations/schemas/hybrid_search.md +144 -0
- package/skills/create-workflow-template/operations/schemas/if_else.md +16 -0
- package/skills/create-workflow-template/operations/schemas/incident_created.md +88 -0
- package/skills/create-workflow-template/operations/schemas/incident_updated.md +126 -0
- package/skills/create-workflow-template/operations/schemas/init_variable.md +67 -0
- package/skills/create-workflow-template/operations/schemas/invoice_created.md +21 -0
- package/skills/create-workflow-template/operations/schemas/invoice_updated.md +41 -0
- package/skills/create-workflow-template/operations/schemas/invoke_code.md +132 -0
- package/skills/create-workflow-template/operations/schemas/issue_created.md +105 -0
- package/skills/create-workflow-template/operations/schemas/issue_sla_tracker_updated.md +46 -0
- package/skills/create-workflow-template/operations/schemas/issue_updated.md +172 -0
- package/skills/create-workflow-template/operations/schemas/link_incident_with_issue.md +14 -0
- package/skills/create-workflow-template/operations/schemas/link_ticket_with_issue.md +14 -0
- package/skills/create-workflow-template/operations/schemas/list_enhancements.md +74 -0
- package/skills/create-workflow-template/operations/schemas/list_issues.md +108 -0
- package/skills/create-workflow-template/operations/schemas/list_sessions.md +79 -0
- package/skills/create-workflow-template/operations/schemas/list_sprint.md +29 -0
- package/skills/create-workflow-template/operations/schemas/list_web_sessions.md +87 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_accounts.md +106 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_articles.md +126 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_customers.md +88 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_dev_users.md +75 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_enhancements.md +112 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_incidents.md +113 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_issues.md +217 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_meetings.md +150 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_opportunity.md +161 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_sprints.md +50 -0
- package/skills/create-workflow-template/operations/schemas/loop_over_tickets.md +203 -0
- package/skills/create-workflow-template/operations/schemas/manual_trigger.md +11 -0
- package/skills/create-workflow-template/operations/schemas/meeting_created.md +116 -0
- package/skills/create-workflow-template/operations/schemas/meeting_updated.md +152 -0
- package/skills/create-workflow-template/operations/schemas/oasis_sql_execute.md +11 -0
- package/skills/create-workflow-template/operations/schemas/opportunity_created.md +92 -0
- package/skills/create-workflow-template/operations/schemas/opportunity_updated.md +124 -0
- package/skills/create-workflow-template/operations/schemas/pick_user.md +16 -0
- package/skills/create-workflow-template/operations/schemas/question_answer_created.md +44 -0
- package/skills/create-workflow-template/operations/schemas/question_answer_updated.md +75 -0
- package/skills/create-workflow-template/operations/schemas/recall_chats.md +13 -0
- package/skills/create-workflow-template/operations/schemas/router.md +15 -0
- package/skills/create-workflow-template/operations/schemas/send_notification.md +19 -0
- package/skills/create-workflow-template/operations/schemas/set_variable.md +67 -0
- package/skills/create-workflow-template/operations/schemas/sleep_for.md +12 -0
- package/skills/create-workflow-template/operations/schemas/sleep_until.md +17 -0
- package/skills/create-workflow-template/operations/schemas/sprint_updated.md +37 -0
- package/skills/create-workflow-template/operations/schemas/suggest_part.md +14 -0
- package/skills/create-workflow-template/operations/schemas/task_updated.md +79 -0
- package/skills/create-workflow-template/operations/schemas/test_example.md +16 -0
- package/skills/create-workflow-template/operations/schemas/ticket_created.md +136 -0
- package/skills/create-workflow-template/operations/schemas/ticket_sla_tracker_updated.md +46 -0
- package/skills/create-workflow-template/operations/schemas/ticket_updated.md +198 -0
- package/skills/create-workflow-template/operations/schemas/timeline_comment_created.md +70 -0
- package/skills/create-workflow-template/operations/schemas/update_account.md +68 -0
- package/skills/create-workflow-template/operations/schemas/update_article.md +95 -0
- package/skills/create-workflow-template/operations/schemas/update_brand.md +44 -0
- package/skills/create-workflow-template/operations/schemas/update_contact.md +53 -0
- package/skills/create-workflow-template/operations/schemas/update_conversation.md +149 -0
- package/skills/create-workflow-template/operations/schemas/update_enhancement.md +64 -0
- package/skills/create-workflow-template/operations/schemas/update_incident.md +156 -0
- package/skills/create-workflow-template/operations/schemas/update_issue.md +173 -0
- package/skills/create-workflow-template/operations/schemas/update_meeting.md +114 -0
- package/skills/create-workflow-template/operations/schemas/update_opportunity.md +137 -0
- package/skills/create-workflow-template/operations/schemas/update_question_answer.md +60 -0
- package/skills/create-workflow-template/operations/schemas/update_ticket.md +188 -0
- package/skills/create-workflow-template/operations/schemas/watch_ticket_for_updates.md +225 -0
- package/skills/create-workflow-template/operations/schemas/web_search.md +17 -0
- package/skills/create-workflow-template/operations/schemas/while.md +24 -0
- package/skills/create-workflow-template/operations/schemas/widget_created.md +75 -0
- package/skills/create-workflow-template/operations/schemas/widget_updated.md +98 -0
- package/skills/create-workflow-template/operations/schemas/workspace_created.md +20 -0
- package/skills/create-workflow-template/operations/triggers.md +1583 -0
- package/skills/customer-brief/customer-brief.md +66 -0
- package/skills/deal-review-meddpicc/deal-review-meddpicc.md +58 -0
- package/skills/next-step-for-opportunity/next-step-for-opportunity.md +55 -0
- package/skills/opportunity-feature-prioritizer/SKILL.md +183 -0
- package/skills/sales-call-plan-coach/sales-call-plan-coach.md +73 -0
- package/skills/sales-context/sales-context.md +44 -0
- package/skills/sales-search-and-lookup/sales-search-and-lookup.md +58 -0
- package/skills/skill-creator/SKILL.md +570 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/references/tool-patterns.md +290 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
- package/skills/trace-diagnosis/trace-diagnosis.md +186 -0
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-name: create-workflow-template
|
|
3
|
+
user-invocable: true
|
|
4
|
+
description: Create a DevRev workflow template JSON from a natural language description
|
|
5
|
+
arguments:
|
|
6
|
+
- name: description
|
|
7
|
+
description: Natural language description of the workflow to create
|
|
8
|
+
required: true
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Create DevRev Workflow Template
|
|
12
|
+
|
|
13
|
+
Generate a valid DevRev workflow template JSON. Study the example templates in `.claude/skills/create-workflow-template/examples/` before generating to ensure the output matches the production format.
|
|
14
|
+
|
|
15
|
+
## Workflow Process
|
|
16
|
+
|
|
17
|
+
1. **Understand the user's intent** — Parse the natural language description to identify the trigger event, the sequence of actions, any conditions/branching, and any loops.
|
|
18
|
+
2. **Look up operations** — Consult the operations reference docs in `.claude/skills/create-workflow-template/operations/` to find the correct slugs, namespaces, and input/output schemas for each operation needed. For detailed field-level schemas (input/output ports, field types, required fields), check `.claude/skills/create-workflow-template/operations/schemas/<slug>.md` — the schema index is at `.claude/skills/create-workflow-template/operations/schema-index.md`.
|
|
19
|
+
3. **Study example templates** — Read relevant templates from `.claude/skills/create-workflow-template/examples/` to see real-world patterns for the operations you need. **Prioritize `working-*.json` examples** — these are validated, import-tested templates. Use python3 to pretty-print: `python3 -c "import json; d=json.load(open('.claude/skills/create-workflow-template/examples/FILE.json')); inner=json.loads(d['data']); print(json.dumps(inner, indent=2))"`.
|
|
20
|
+
4. **Build the template JSON** — Assemble the complete workflow JSON following the specification below, matching the format used in the example templates.
|
|
21
|
+
5. **Output the JSON** — Write the template to a file in `templates/<workflow_name>.json` and also display a summary to the user.
|
|
22
|
+
6. **Save as example (self-improvement)** — After a template is confirmed working (user reports successful import or no errors), copy it to `.claude/skills/create-workflow-template/examples/working-<descriptive-name>.json`. This grows the example library over time, making the skill better with every successful workflow. Also update the example templates table in this file with the new entry.
|
|
23
|
+
|
|
24
|
+
## Template Wrapper Format
|
|
25
|
+
|
|
26
|
+
Templates are wrapped in an outer envelope. The `data` field is a **stringified JSON** of the inner workflow:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"templateVersion": "2.0.0",
|
|
31
|
+
"data": "<stringified inner workflow JSON>"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To produce this, build the inner workflow object first, then `JSON.stringify()` it into the `data` field.
|
|
36
|
+
|
|
37
|
+
## Inner Workflow Structure
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"serialization_version": { "major": 2, "minor": 0, "patch": 0 },
|
|
42
|
+
"title": "Workflow Title (1-256 chars)",
|
|
43
|
+
"description": "What this workflow does (up to 65536 chars)",
|
|
44
|
+
"labels": ["optional-label"],
|
|
45
|
+
"steps": [ ...step objects... ]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| Field | Type | Required | Description |
|
|
50
|
+
|-------|------|----------|-------------|
|
|
51
|
+
| `serialization_version` | object | Yes | Always `{"major": 2, "minor": 0, "patch": 0}` |
|
|
52
|
+
| `title` | string | Yes | 1-256 chars |
|
|
53
|
+
| `description` | string | No | Up to 65536 chars |
|
|
54
|
+
| `labels` | string[] | No | Up to 16 labels, each up to 64 chars. Use `["agent_interaction"]` for agent workflows |
|
|
55
|
+
| `steps` | array | Yes | All workflow steps (triggers + actions + controls + blockings) |
|
|
56
|
+
|
|
57
|
+
## Step Structure
|
|
58
|
+
|
|
59
|
+
Each entry in `steps` is a node in the workflow graph:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"name": "Step Display Name",
|
|
64
|
+
"description": "What this step does",
|
|
65
|
+
"reference_key": "unique_step_key",
|
|
66
|
+
"operation": {
|
|
67
|
+
"namespace": "devrev",
|
|
68
|
+
"slug": "operation_slug"
|
|
69
|
+
},
|
|
70
|
+
"input_values": [ ...input value mappings... ],
|
|
71
|
+
"next_steps": [ ...edges to next steps... ],
|
|
72
|
+
"ui_metadata": {
|
|
73
|
+
"position": { "x": 400, "y": 200 }
|
|
74
|
+
},
|
|
75
|
+
"block_step_reference_key": "parent_loop_ref_key"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
| Field | Type | Required | Description |
|
|
80
|
+
|-------|------|----------|-------------|
|
|
81
|
+
| `name` | string | Yes | 1-256 chars |
|
|
82
|
+
| `description` | string | No | Up to 65536 chars |
|
|
83
|
+
| `reference_key` | string | Yes | Unique within workflow. Pattern: `^[a-zA-Z_][a-zA-Z0-9_]*$` |
|
|
84
|
+
| `operation` | object | Yes | `{namespace, slug}` — identifies the operation |
|
|
85
|
+
| `input_values` | array | No | Input data/expressions for this step |
|
|
86
|
+
| `next_steps` | array | No | Outgoing edges to other steps |
|
|
87
|
+
| `ui_metadata` | object | No | UI position `{position: {x, y}}` |
|
|
88
|
+
| `block_step_reference_key` | string | No | Ref key of parent loop/block step (for nested steps) |
|
|
89
|
+
| `system_options` | object | No | Execution options (e.g. `execute_as_user`) |
|
|
90
|
+
| `scopes` | object | No | Access scopes |
|
|
91
|
+
|
|
92
|
+
### Port Schema Rules (`input_ports` / `output_ports`)
|
|
93
|
+
|
|
94
|
+
The UI needs port schemas to render step configuration panels. Rules by operation type:
|
|
95
|
+
|
|
96
|
+
| Operation Type | `input_ports` | `output_ports` | Notes |
|
|
97
|
+
|---------------|:---:|:---:|-------|
|
|
98
|
+
| **Triggers** (`*_created`, `*_updated`) | No | No | System resolves schemas automatically |
|
|
99
|
+
| **Timer trigger** (`timer_trigger`) | **Yes** | **Yes** | Needs uenum type field + composite schemas for interval/cron. See Timer Trigger section |
|
|
100
|
+
| **Control ops** (`if_else`, `while`, `router`) | **Yes** | **Yes** | Need input schema + named output ports (true/false/error, block_start/output/error) |
|
|
101
|
+
| **Loop ops** (`loop_over_*`) | **Yes** | **Yes** | Need input schema (filter fields) + `block_callback` input port + `block_start`/`output`/`error` output ports. `block_start` uses `type: "block_start"` |
|
|
102
|
+
| **`for_each`** | **Yes** | **Yes** | Dynamic schema — avoid in templates, use `loop_over_*` instead |
|
|
103
|
+
| **`init_variable`** | **Yes** | **Yes** | Output port needs `scope_variables` array. See Variable Management section |
|
|
104
|
+
| **`set_variable`** | **Yes** | **Yes** | Input port needs `uenum` fields with `allowed_values`. See Variable Management section |
|
|
105
|
+
| **Action ops** (`update_*`, `create_*`, `get_*`, etc.) | **Yes** | No | Need input schema so UI renders field picker. Get schemas from `operations/schemas/<slug>.md` raw JSON files at `/tmp/op_schemas/<slug>.json` |
|
|
106
|
+
| **`invoke_code`** | **Yes** | **Yes** | Dynamic schema with `invalidate_on_field_update`. See `operations/schemas/invoke_code.md` for full patterns |
|
|
107
|
+
| **`manual_trigger`** | No | **Yes** | Has dynamic output schema |
|
|
108
|
+
|
|
109
|
+
**Port schema format** — uses the internal schema format with `field_descriptors`, `composite_schemas`, `data_name`, `db_name`, `oasis` metadata. Get the raw JSON from `/tmp/op_schemas/<slug>.json` (downloaded from `devrev/flow` repo `.gen.json` files) and include the relevant port's schema object.
|
|
110
|
+
|
|
111
|
+
**Error ports** — Control ops and `invoke_code` should include an `error` output port:
|
|
112
|
+
```json
|
|
113
|
+
{"name": "error", "schema": {"field_descriptors": [
|
|
114
|
+
{"field_type": "text", "data_name": "message", "db_name": "message", "description": "Error message with more details about the error", "name": "message", "oasis": {"name": "message"}, "ui": {"display_name": "Error Message"}},
|
|
115
|
+
{"field_type": "enum", "allowed_values": ["bad_request", "not_found", "internal"], "data_name": "type", "db_name": "type", "description": "Type of the error", "name": "type", "oasis": {"name": "type"}, "ui": {"display_name": "Error Type"}}
|
|
116
|
+
], "type": "field_descriptor"}, "type": "error"}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**`if_else` ports:**
|
|
120
|
+
```json
|
|
121
|
+
"input_ports": [{"name": "input", "schema": {"field_descriptors": [{"field_type": "struct", "data_name": "condition", "db_name": "condition", "description": "Condition to evaluate", "is_required": true, "name": "condition", "oasis": {"name": "condition"}}], "type": "field_descriptor"}, "type": "default"}],
|
|
122
|
+
"output_ports": [{"name": "true", "type": "default"}, {"name": "false", "type": "default"}, <error_port>]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**`invoke_code` ports** — include `input_ports` with variable composite + code editor schema, `output_ports` with empty output + error. Do NOT include `input_values` — the user configures code, input variables, and output schema in the UI after import. See `operations/schemas/invoke_code.md` for the full `input_ports` JSON.
|
|
126
|
+
|
|
127
|
+
## How Operations Are Referenced
|
|
128
|
+
|
|
129
|
+
Always use `namespace` + `slug`:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
"operation": {
|
|
133
|
+
"namespace": "devrev",
|
|
134
|
+
"slug": "ticket_created"
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- First-party DevRev operations: `"devrev"` namespace
|
|
139
|
+
- Custom objects: `"custom_object"` namespace
|
|
140
|
+
- Third-party snap-in operations: use their snap-in namespace (e.g. `"send-emails"`)
|
|
141
|
+
- Look up the correct slug from `.claude/skills/create-workflow-template/operations/*.md` files
|
|
142
|
+
|
|
143
|
+
## Connecting Steps (next_steps)
|
|
144
|
+
|
|
145
|
+
Each step can connect to one or more next steps via port-based edges:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
"next_steps": [
|
|
149
|
+
{
|
|
150
|
+
"port_name": "output",
|
|
151
|
+
"next_step_reference_key": "target_step_ref",
|
|
152
|
+
"next_port_name": "input"
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Port names by operation type:**
|
|
158
|
+
|
|
159
|
+
| Operation | Output Ports |
|
|
160
|
+
|-----------|-------------|
|
|
161
|
+
| Regular triggers/actions | `"output"` |
|
|
162
|
+
| `if_else` | `"true"` and `"false"` |
|
|
163
|
+
| `loop_over_*` / `for_each` / `while` | `"block_start"` (loop body) and `"output"` (after loop) |
|
|
164
|
+
| `router` | Named route ports |
|
|
165
|
+
|
|
166
|
+
The `next_port_name` on the target step is almost always `"input"`.
|
|
167
|
+
|
|
168
|
+
## Input Values (Data Mapping)
|
|
169
|
+
|
|
170
|
+
The `input_values` array defines how data flows into each step. **Use string type names** (not integer IDs):
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
"input_values": [
|
|
174
|
+
{
|
|
175
|
+
"port_name": "input",
|
|
176
|
+
"fields": [
|
|
177
|
+
{
|
|
178
|
+
"name": "field_name",
|
|
179
|
+
"value": {
|
|
180
|
+
"type": "<type_name>",
|
|
181
|
+
"value": <value>
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Value Types
|
|
190
|
+
|
|
191
|
+
| Type Name | Description | Example |
|
|
192
|
+
|-----------|-------------|---------|
|
|
193
|
+
| `"literal"` | Static/fixed value | `{"type": "literal", "value": "internal"}` |
|
|
194
|
+
| `"jsonata_expression"` | References other steps' outputs | `{"type": "jsonata_expression", "value": "$get('step_ref').field"}` |
|
|
195
|
+
| `"composite_value"` | Structured object with nested fields | See composite section below |
|
|
196
|
+
| `"text_template"` | Template string with embedded expressions | `{"type": "text_template", "value": "Hello {% expr $get('step_ref').name %}"}` |
|
|
197
|
+
| `"list_value"` | List of items | See list section below |
|
|
198
|
+
|
|
199
|
+
### Literal Values
|
|
200
|
+
|
|
201
|
+
Use for static values — strings, numbers, booleans, DON IDs, or structured objects:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{ "type": "literal", "value": "internal" }
|
|
205
|
+
{ "type": "literal", "value": true }
|
|
206
|
+
{ "type": "literal", "value": "don:core:dvrv-us-1:devo/0:custom_stage/27" }
|
|
207
|
+
{ "type": "literal", "value": {"seconds": 30} }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### JSONata Expressions
|
|
211
|
+
|
|
212
|
+
Reference outputs from previous steps:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{ "type": "jsonata_expression", "value": "$get('issue_created_1', 'output').id" }
|
|
216
|
+
{ "type": "jsonata_expression", "value": "$get('get_ticket_1', 'output').owned_by[0].id" }
|
|
217
|
+
{ "type": "jsonata_expression", "value": "$get('issue_updated_1', 'output').sprint.end_date" }
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Syntax: `$get('<reference_key>', '<port_name>').<field_path>`
|
|
221
|
+
- Port name defaults to `'output'` if omitted: `$get('step_ref').field`
|
|
222
|
+
|
|
223
|
+
### Text Templates
|
|
224
|
+
|
|
225
|
+
Mix static text with embedded expressions:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{ "type": "text_template", "value": "Hi {% expr $get('get_ticket_1', 'output').owned_by[0].id %}, the ticket has been escalated." }
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### List Values
|
|
232
|
+
|
|
233
|
+
Wrap arrays — each item specifies its own type:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"type": "list_value",
|
|
238
|
+
"value": {
|
|
239
|
+
"items": [
|
|
240
|
+
{ "type": "literal", "value": ["stage"] },
|
|
241
|
+
{ "type": "jsonata_expression", "value": "$append($get('step_ref', 'output').owned_by, [])" }
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Common pattern** — wrapping a single expression as a list (required for array-typed fields like `receivers`, `fields_to_watch`):
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"type": "list_value",
|
|
251
|
+
"value": {
|
|
252
|
+
"items": [
|
|
253
|
+
{ "type": "jsonata_expression", "value": "$append($get('trigger_1', 'output').owned_by, [])" }
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Composite Values
|
|
260
|
+
|
|
261
|
+
Structured objects with named sub-fields:
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"type": "composite_value",
|
|
266
|
+
"value": {
|
|
267
|
+
"fields": [
|
|
268
|
+
{ "name": "set", "value": { "type": "literal", "value": ["don:identity:dvrv-us-1:devo/0:devu/1"] } }
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Trigger Configuration
|
|
275
|
+
|
|
276
|
+
Trigger steps are regular steps that use a trigger-type operation. They support two special input fields:
|
|
277
|
+
|
|
278
|
+
### `fields_to_watch` — Filter which field changes fire the trigger
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"name": "fields_to_watch",
|
|
283
|
+
"value": {
|
|
284
|
+
"type": "list_value",
|
|
285
|
+
"value": {
|
|
286
|
+
"items": [
|
|
287
|
+
{ "type": "literal", "value": ["stage"] }
|
|
288
|
+
]
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### `_filter` — Conditional filter for when the trigger fires
|
|
295
|
+
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"name": "_filter",
|
|
299
|
+
"value": {
|
|
300
|
+
"type": "literal",
|
|
301
|
+
"value": {
|
|
302
|
+
"conditions": [{
|
|
303
|
+
"conditions": [{
|
|
304
|
+
"operands": [
|
|
305
|
+
{ "type": "jsonata_expression", "value": "$get('opportunity_updated_1', 'output').stage.name" },
|
|
306
|
+
{ "type": "literal", "value": "3-evaluate" }
|
|
307
|
+
],
|
|
308
|
+
"operator": "equals",
|
|
309
|
+
"type": "rule"
|
|
310
|
+
}],
|
|
311
|
+
"logical_operator": "and",
|
|
312
|
+
"negate": false,
|
|
313
|
+
"type": "group"
|
|
314
|
+
}],
|
|
315
|
+
"logical_operator": "and",
|
|
316
|
+
"negate": false,
|
|
317
|
+
"type": "group"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Timer Trigger
|
|
324
|
+
|
|
325
|
+
The `timer_trigger` operation requires full `input_ports` and `output_ports` (unlike event-based triggers which resolve schemas automatically).
|
|
326
|
+
|
|
327
|
+
**Input port** — includes `uenum` type field and composite schemas for interval/cron:
|
|
328
|
+
```json
|
|
329
|
+
"input_ports": [{"name": "input", "schema": {
|
|
330
|
+
"composite_schemas": [
|
|
331
|
+
{"description": "Timer interval configuration", "fields": [
|
|
332
|
+
{"field_type": "enum", "allowed_values": ["days", "hours"], "data_name": "unit", "db_name": "unit",
|
|
333
|
+
"default_value": "days", "description": "Unit of time for the interval", "is_required": true,
|
|
334
|
+
"name": "unit", "oasis": {"name": "unit"}, "ui": {"display_name": "Unit of time"}},
|
|
335
|
+
{"field_type": "int", "data_name": "number_of_units", "db_name": "number_of_units",
|
|
336
|
+
"default_value": 1, "description": "Number of Units", "is_required": true,
|
|
337
|
+
"name": "number_of_units", "oasis": {"name": "number_of_units"}, "ui": {"display_name": "Number of Units"}}
|
|
338
|
+
], "name": "timer_interval"},
|
|
339
|
+
{"description": "Timer cron configuration", "fields": [
|
|
340
|
+
{"field_type": "tokens", "data_name": "schedule", "db_name": "schedule",
|
|
341
|
+
"description": "The cron expression for the timer trigger.", "is_required": true,
|
|
342
|
+
"name": "schedule", "oasis": {"name": "schedule"}}
|
|
343
|
+
], "name": "timer_cron"}
|
|
344
|
+
],
|
|
345
|
+
"field_descriptors": [
|
|
346
|
+
{"field_type": "uenum", "allowed_values": [
|
|
347
|
+
{"id": 1, "label": "Interval based", "ordinal": 1, "tooltip": "Trigger at regular intervals", "value": "interval"},
|
|
348
|
+
{"id": 2, "label": "Cron based", "ordinal": 2, "tooltip": "Trigger at specific time using cron", "value": "cron"}
|
|
349
|
+
], "data_name": "type", "db_name": "type", "default_value": 1,
|
|
350
|
+
"description": "The type of timer trigger", "name": "type", "oasis": {"name": "type"}, "ui": {"display_name": "Type"}},
|
|
351
|
+
{"field_type": "composite", "composite_type": "timer_interval", "data_name": "interval", "db_name": "interval",
|
|
352
|
+
"description": "The interval at which the workflow should trigger.", "is_required": false,
|
|
353
|
+
"name": "interval", "oasis": {"name": "interval"}},
|
|
354
|
+
{"field_type": "composite", "composite_type": "timer_cron", "data_name": "cron", "db_name": "cron",
|
|
355
|
+
"description": "The cron configuration.", "is_required": false, "name": "cron", "oasis": {"name": "cron"}},
|
|
356
|
+
{"field_type": "struct", "data_name": "_filter", "db_name": "_filter", "description": "Filter for the trigger event",
|
|
357
|
+
"name": "_filter", "oasis": {"name": "_filter"}, "ui": {"display_name": "Filter"}}
|
|
358
|
+
], "type": "field_descriptor"}, "type": "default"}]
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Output port** — exposes the scheduled time:
|
|
362
|
+
```json
|
|
363
|
+
"output_ports": [{"name": "output", "schema": {"field_descriptors": [
|
|
364
|
+
{"field_type": "timestamp", "data_name": "scheduled_time", "db_name": "scheduled_time",
|
|
365
|
+
"description": "The time when the workflow was scheduled to trigger", "is_required": true,
|
|
366
|
+
"name": "scheduled_time", "oasis": {"name": "scheduled_time"}}
|
|
367
|
+
], "type": "field_descriptor"}, "type": "default"}]
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Critical Field Type Requirements
|
|
371
|
+
|
|
372
|
+
### `uenum` Fields MUST Have `allowed_values`
|
|
373
|
+
|
|
374
|
+
Fields with `field_type: "uenum"` will cause import error `"Missing required field: allowed_values"` if `allowed_values` is missing. Each value must be an object with `{id, label, ordinal, value}`:
|
|
375
|
+
|
|
376
|
+
```json
|
|
377
|
+
{"field_type": "uenum", "allowed_values": [
|
|
378
|
+
{"id": 1, "label": "Set Value", "ordinal": 1, "value": "set"},
|
|
379
|
+
{"id": 2, "label": "Concatenate", "ordinal": 2, "value": "concat"}
|
|
380
|
+
], "data_name": "operation", ...}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The `value` can be a string or a complex object (e.g., for variable references):
|
|
384
|
+
```json
|
|
385
|
+
{"id": 1, "label": "init_variable_1 - summary", "ordinal": 1,
|
|
386
|
+
"value": {"output_port_name": "output", "step_reference_key": "init_variable_1", "variable_name": "summary"}}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### `array` Fields MUST Have `base_type`
|
|
390
|
+
|
|
391
|
+
Fields with `field_type: "array"` will cause import error `"discriminator not set: base_type"` if `base_type` is missing:
|
|
392
|
+
|
|
393
|
+
```json
|
|
394
|
+
{"field_type": "array", "base_type": "id", "data_name": "owned_by", ...}
|
|
395
|
+
{"field_type": "array", "base_type": "text", "data_name": "external_ref", ...}
|
|
396
|
+
{"field_type": "array", "base_type": "composite", "composite_type": "pair", "data_name": "headers", ...}
|
|
397
|
+
{"field_type": "array", "base_type": "enum", "allowed_values": ["days", "hours"], "data_name": "units", ...}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Common `base_type` values: `id`, `text`, `composite`, `enum`
|
|
401
|
+
|
|
402
|
+
## If/Else Conditions
|
|
403
|
+
|
|
404
|
+
The `if_else` operation takes a structured condition as a literal:
|
|
405
|
+
|
|
406
|
+
```json
|
|
407
|
+
{
|
|
408
|
+
"name": "condition",
|
|
409
|
+
"value": {
|
|
410
|
+
"type": "literal",
|
|
411
|
+
"value": {
|
|
412
|
+
"type": "group",
|
|
413
|
+
"logical_operator": "and",
|
|
414
|
+
"negate": false,
|
|
415
|
+
"conditions": [{
|
|
416
|
+
"conditions": [{
|
|
417
|
+
"operands": [
|
|
418
|
+
{ "type": "jsonata_expression", "value": "$get('get_account_1', 'output').tnt__segment" }
|
|
419
|
+
],
|
|
420
|
+
"operator": "exists",
|
|
421
|
+
"type": "rule"
|
|
422
|
+
}],
|
|
423
|
+
"logical_operator": "and",
|
|
424
|
+
"negate": false,
|
|
425
|
+
"type": "group"
|
|
426
|
+
}]
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
**Condition operators:** `equals`, `not_equals`, `contains_any`, `is_empty`, `is_not_empty`, `greater_than`, `less_than`, `greater_than_or_equal`, `less_than_or_equal`, `contains`, `not_contains`, `starts_with`, `ends_with`, `exists`, `not_exists`
|
|
433
|
+
|
|
434
|
+
**Logical operators:** `and`, `or`
|
|
435
|
+
|
|
436
|
+
**Note:** Conditions are typically nested as groups-within-groups (see the `_filter` example above). Each rule has `operands` (each with `type` and `value`), `operator`, and `type: "rule"`. Rules are wrapped in `type: "group"` with `logical_operator`.
|
|
437
|
+
|
|
438
|
+
## Loops and Iteration
|
|
439
|
+
|
|
440
|
+
### `loop_over_*` Operations (Preferred for Native Objects)
|
|
441
|
+
|
|
442
|
+
For iterating over DevRev native objects, use the dedicated `loop_over_*` operations. These have **static, well-defined schemas** that import reliably. Available operations:
|
|
443
|
+
|
|
444
|
+
| Operation | Object Type |
|
|
445
|
+
|-----------|------------|
|
|
446
|
+
| `loop_over_issues` | Issues |
|
|
447
|
+
| `loop_over_tickets` | Tickets |
|
|
448
|
+
| `loop_over_accounts` | Accounts |
|
|
449
|
+
| `loop_over_articles` | Articles |
|
|
450
|
+
| `loop_over_customers` | Customers |
|
|
451
|
+
| `loop_over_dev_users` | Dev Users |
|
|
452
|
+
| `loop_over_enhancements` | Enhancements |
|
|
453
|
+
| `loop_over_incidents` | Incidents |
|
|
454
|
+
| `loop_over_meetings` | Meetings |
|
|
455
|
+
| `loop_over_opportunity` | Opportunities |
|
|
456
|
+
| `loop_over_sprints` | Sprints |
|
|
457
|
+
|
|
458
|
+
**Key structural requirements for `loop_over_*`:**
|
|
459
|
+
|
|
460
|
+
1. **`block_callback` input port** — MUST be included:
|
|
461
|
+
```json
|
|
462
|
+
{"name": "block_callback", "schema": {"type": "field_descriptor"}, "type": "block_callback"}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
2. **`block_start` output port** — uses `type: "block_start"` (NOT `"default"`):
|
|
466
|
+
```json
|
|
467
|
+
{"name": "block_start", "schema": {"type": "field_descriptor"}, "type": "block_start"}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
3. **Two `next_steps` edges** — `block_start` to loop body, `output` to after-loop step:
|
|
471
|
+
```json
|
|
472
|
+
"next_steps": [
|
|
473
|
+
{"port_name": "block_start", "next_step_reference_key": "first_body_step", "next_port_name": "input"},
|
|
474
|
+
{"port_name": "output", "next_step_reference_key": "after_loop_step", "next_port_name": "input"}
|
|
475
|
+
]
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
4. **Block body steps** — MUST set `block_step_reference_key` pointing to the loop:
|
|
479
|
+
```json
|
|
480
|
+
"block_step_reference_key": "loop_over_issues_1"
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
5. **Accessing current item** — Fields are directly on `block_start` (NO `.item` prefix):
|
|
484
|
+
```json
|
|
485
|
+
"$get('loop_over_issues_1', 'block_start').id"
|
|
486
|
+
"$get('loop_over_issues_1', 'block_start').title"
|
|
487
|
+
"$get('loop_over_issues_1', 'block_start').stage.name"
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
6. **Input port schema** — Copy the full schema from the operation's schema file (`operations/schemas/loop_over_issues.md`). The `input_ports` include `input` (with filter fields like `owned_by`, `state`, `stages`, `limit`) and `block_callback`.
|
|
491
|
+
|
|
492
|
+
### `for_each` (Only When No Native Loop Exists)
|
|
493
|
+
|
|
494
|
+
**WARNING:** `for_each` has a **dynamic schema** (populated by a schema handler at runtime). Its `items_to_iterate` field has `field_type: "array"` but requires a `base_type` that is resolved dynamically — this makes templates using `for_each` prone to import errors like `"discriminator not set: base_type"`. **Only use `for_each` when no dedicated `loop_over_*` operation exists for the object type you need to iterate.**
|
|
495
|
+
|
|
496
|
+
The `for_each` operation still uses the same block body patterns:
|
|
497
|
+
- `block_step_reference_key` on body steps
|
|
498
|
+
- `block_start` / `output` port names for next_steps
|
|
499
|
+
- `block_callback` input port
|
|
500
|
+
- Access current item: `$get('for_each_1', 'block_start').item`
|
|
501
|
+
|
|
502
|
+
### Variable Management (init_variable + set_variable + $get_variable)
|
|
503
|
+
|
|
504
|
+
Variables allow accumulating data across loop iterations (e.g., building a summary string). The complete pattern is:
|
|
505
|
+
|
|
506
|
+
#### Step 1: `init_variable` — Create the variable before the loop
|
|
507
|
+
|
|
508
|
+
**Input port** with `variables` field (composite `devrev:schema`):
|
|
509
|
+
```json
|
|
510
|
+
"input_ports": [{"name": "input", "schema": {"field_descriptors": [
|
|
511
|
+
{"field_type": "composite", "composite_type": "devrev:schema", "data_name": "variables", "db_name": "variables",
|
|
512
|
+
"description": "The variables to initialize.", "name": "variables", "oasis": {"name": "variables"}}
|
|
513
|
+
], "invalidate_on_field_update": ["variables"], "type": "field_descriptor"}, "type": "default"}]
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Input values** — define the variable schema:
|
|
517
|
+
```json
|
|
518
|
+
"input_values": [{"port_name": "input", "fields": [{"name": "variables", "value": {"type": "literal", "value": {
|
|
519
|
+
"fields": [{"description": "", "field_type": "text", "is_filterable": false, "name": "summary",
|
|
520
|
+
"ui": {"create_view": {}, "detail_view": {"is_hidden": true}, "display_name": "summary", "summary_view": {"is_hidden": true}}
|
|
521
|
+
}]
|
|
522
|
+
}}}]}]
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**Output port** — MUST include `scope_variables` array matching the variable definition:
|
|
526
|
+
```json
|
|
527
|
+
"output_ports": [{"name": "output", "schema": {"type": "field_descriptor"},
|
|
528
|
+
"scope_variables": [{"field_descriptor": {
|
|
529
|
+
"field_type": "text", "data_name": "summary", "db_name": "summary", "description": "",
|
|
530
|
+
"is_filterable": false, "name": "summary", "oasis": {"name": "summary"},
|
|
531
|
+
"ui": {"create_view": {}, "detail_view": {"is_hidden": true}, "display_name": "summary", "summary_view": {"is_hidden": true}}
|
|
532
|
+
}}],
|
|
533
|
+
"type": "default"}, <error_port>]
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
#### Step 2: `set_variable` — Modify the variable inside the loop body
|
|
537
|
+
|
|
538
|
+
The `set_variable` step requires `uenum` fields with `allowed_values`. See the **uenum field type** section below.
|
|
539
|
+
|
|
540
|
+
**Input port** with three fields — `variable` (uenum), `operation` (uenum), `value` (text):
|
|
541
|
+
```json
|
|
542
|
+
"input_ports": [{"name": "input", "schema": {"field_descriptors": [
|
|
543
|
+
{"field_type": "uenum", "allowed_values": [
|
|
544
|
+
{"id": 1, "label": "init_variable_1 - summary", "ordinal": 1,
|
|
545
|
+
"value": {"output_port_name": "output", "step_reference_key": "init_variable_1", "variable_name": "summary"}}
|
|
546
|
+
], "data_name": "variable", "db_name": "variable", "description": "Variable",
|
|
547
|
+
"is_required": true, "name": "variable", "oasis": {"name": "variable"}, "ui": {"display_name": "Variable"}},
|
|
548
|
+
{"field_type": "uenum", "allowed_values": [
|
|
549
|
+
{"id": 1, "label": "Set Value", "ordinal": 1, "value": "set"},
|
|
550
|
+
{"id": 2, "label": "Concatenate", "ordinal": 2, "value": "concat"}
|
|
551
|
+
], "data_name": "operation", "db_name": "operation", "description": "Operation to perform.",
|
|
552
|
+
"is_required": false, "name": "operation", "oasis": {"name": "operation"}, "ui": {"display_name": "Operation"}},
|
|
553
|
+
{"field_type": "text", "data_name": "value", "db_name": "value", "description": "Value",
|
|
554
|
+
"is_filterable": false, "is_required": true, "name": "value", "oasis": {"name": "value"}, "ui": {"display_name": "Value"}}
|
|
555
|
+
], "invalidate_on_field_update": ["variable"], "type": "field_descriptor"}, "type": "default"}]
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**Input values** — specify which variable, what operation, and what value:
|
|
559
|
+
```json
|
|
560
|
+
"input_values": [{"port_name": "input", "fields": [
|
|
561
|
+
{"name": "variable", "value": {"type": "literal", "value": {
|
|
562
|
+
"output_port_name": "output", "step_reference_key": "init_variable_1", "variable_name": "summary"}}},
|
|
563
|
+
{"name": "operation", "value": {"type": "literal", "value": "concat"}},
|
|
564
|
+
{"name": "value", "value": {"type": "text_template", "value": "{% expr $get('http_1', 'output').body %}"}}
|
|
565
|
+
]}]
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
#### Step 3: `$get_variable` — Read the accumulated variable after the loop
|
|
569
|
+
|
|
570
|
+
After the loop completes, access the variable using `$get_variable()`:
|
|
571
|
+
```
|
|
572
|
+
{% expr $get_variable('init_variable_1','summary',{'output_port_name' : 'output'}) %}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Syntax: `$get_variable('<init_step_ref>', '<variable_name>', {'output_port_name': 'output'})`
|
|
576
|
+
|
|
577
|
+
## Sleep/Wait Operations
|
|
578
|
+
|
|
579
|
+
**Sleep For** (pause for duration — value is a literal object with `seconds`):
|
|
580
|
+
```json
|
|
581
|
+
{
|
|
582
|
+
"name": "Sleep For",
|
|
583
|
+
"operation": { "namespace": "devrev", "slug": "sleep_for" },
|
|
584
|
+
"reference_key": "sleep_for_1",
|
|
585
|
+
"input_values": [{
|
|
586
|
+
"port_name": "input",
|
|
587
|
+
"fields": [{
|
|
588
|
+
"name": "duration",
|
|
589
|
+
"value": { "type": "literal", "value": {"seconds": 30} }
|
|
590
|
+
}]
|
|
591
|
+
}]
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Sleep Until** (pause until timestamp from another step):
|
|
596
|
+
```json
|
|
597
|
+
{
|
|
598
|
+
"name": "Sleep Until",
|
|
599
|
+
"operation": { "namespace": "devrev", "slug": "sleep_until" },
|
|
600
|
+
"reference_key": "sleep_until_1",
|
|
601
|
+
"input_values": [{
|
|
602
|
+
"port_name": "input",
|
|
603
|
+
"fields": [{
|
|
604
|
+
"name": "sleep_until",
|
|
605
|
+
"value": { "type": "jsonata_expression", "value": "$get('ticket_created_1', 'output').target_close_date" }
|
|
606
|
+
}]
|
|
607
|
+
}]
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
## Ask AI Operation
|
|
612
|
+
|
|
613
|
+
Use `ask_ai` for AI-powered text analysis, classification, or generation:
|
|
614
|
+
|
|
615
|
+
```json
|
|
616
|
+
{
|
|
617
|
+
"name": "Ask AI",
|
|
618
|
+
"operation": { "namespace": "devrev", "slug": "ask_ai" },
|
|
619
|
+
"reference_key": "ask_ai_1",
|
|
620
|
+
"input_values": [{
|
|
621
|
+
"port_name": "input",
|
|
622
|
+
"fields": [
|
|
623
|
+
{ "name": "model", "value": { "type": "literal", "value": "Normal (default)" } },
|
|
624
|
+
{ "name": "output_format", "value": { "type": "literal", "value": "text" } },
|
|
625
|
+
{
|
|
626
|
+
"name": "ai_input",
|
|
627
|
+
"value": {
|
|
628
|
+
"type": "text_template",
|
|
629
|
+
"value": "Analyze the following comment and classify as Escalate or Ignore: {% expr $get('timeline_comment_created_1', 'output').body %}"
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
]
|
|
633
|
+
}]
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Access the AI response: `$get('ask_ai_1', 'output').ai_output`
|
|
638
|
+
|
|
639
|
+
## HTTP Operation
|
|
640
|
+
|
|
641
|
+
Use `http` for external API calls:
|
|
642
|
+
|
|
643
|
+
```json
|
|
644
|
+
{
|
|
645
|
+
"name": "HTTP Request",
|
|
646
|
+
"operation": { "namespace": "devrev", "slug": "http" },
|
|
647
|
+
"reference_key": "http_1",
|
|
648
|
+
"input_values": [{
|
|
649
|
+
"port_name": "input",
|
|
650
|
+
"fields": [
|
|
651
|
+
{ "name": "method", "value": { "type": "literal", "value": "POST" } },
|
|
652
|
+
{ "name": "auth_type", "value": { "type": "literal", "value": "Bearer" } },
|
|
653
|
+
{ "name": "url", "value": { "type": "text_template", "value": "https://api.devrev.ai/rev-users.get" } },
|
|
654
|
+
{ "name": "body", "value": { "type": "text_template", "value": "{\"id\":\"{% expr $get('prev_step', 'output').user_id %}\"}" } }
|
|
655
|
+
]
|
|
656
|
+
}]
|
|
657
|
+
}
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
## Code Node (`invoke_code`)
|
|
661
|
+
|
|
662
|
+
The Code node executes custom Python code within a workflow. Use it when native nodes don't provide the flexibility needed — data transformation, complex calculations, text processing, custom business logic, or dynamic value generation.
|
|
663
|
+
|
|
664
|
+
### How It Works
|
|
665
|
+
|
|
666
|
+
1. The workflow engine collects `input_values` mapped from previous steps
|
|
667
|
+
2. Python code executes in a secure sandbox (the `run` function receives inputs as a dict)
|
|
668
|
+
3. The `run` function returns a dict — returned values become available to downstream nodes via `output_schema`
|
|
669
|
+
|
|
670
|
+
### Template Structure
|
|
671
|
+
|
|
672
|
+
The `invoke_code` step requires three input fields. **Critical type requirements:**
|
|
673
|
+
- `code` uses `text_template` type (NOT `literal`)
|
|
674
|
+
- `input_values` items use `composite_value_list` (NOT `composite_value`)
|
|
675
|
+
- `output_schema` uses `literal` with full UI metadata on each field
|
|
676
|
+
|
|
677
|
+
```json
|
|
678
|
+
{
|
|
679
|
+
"name": "Execute Code",
|
|
680
|
+
"description": "Runs custom Python code",
|
|
681
|
+
"reference_key": "invoke_code_1",
|
|
682
|
+
"operation": { "namespace": "devrev", "slug": "invoke_code" },
|
|
683
|
+
"input_values": [{
|
|
684
|
+
"port_name": "input",
|
|
685
|
+
"fields": [
|
|
686
|
+
{
|
|
687
|
+
"name": "code",
|
|
688
|
+
"value": {
|
|
689
|
+
"type": "text_template",
|
|
690
|
+
"value": "import re\n\ndef run(inputs):\n text = inputs.get('description', '')\n cleaned = re.sub(r'\\\\bagent\\\\b', 'computer', text, flags=re.IGNORECASE)\n return {'cleaned': cleaned}"
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
"name": "input_values",
|
|
695
|
+
"value": {
|
|
696
|
+
"type": "list_value",
|
|
697
|
+
"value": {
|
|
698
|
+
"items": [{
|
|
699
|
+
"type": "composite_value_list",
|
|
700
|
+
"value": [{
|
|
701
|
+
"fields": [
|
|
702
|
+
{ "name": "value", "value": { "type": "jsonata_expression", "value": "$get('trigger_1', 'output').description" } },
|
|
703
|
+
{ "name": "name", "value": { "type": "literal", "value": "description" } }
|
|
704
|
+
]
|
|
705
|
+
}]
|
|
706
|
+
}]
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
"name": "output_schema",
|
|
712
|
+
"value": {
|
|
713
|
+
"type": "literal",
|
|
714
|
+
"value": {
|
|
715
|
+
"fields": [{
|
|
716
|
+
"description": "",
|
|
717
|
+
"field_type": "text",
|
|
718
|
+
"is_filterable": false,
|
|
719
|
+
"name": "cleaned",
|
|
720
|
+
"ui": {
|
|
721
|
+
"create_view": {},
|
|
722
|
+
"detail_view": {"is_hidden": true},
|
|
723
|
+
"display_name": "cleaned",
|
|
724
|
+
"summary_view": {"is_hidden": true}
|
|
725
|
+
}
|
|
726
|
+
}]
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
]
|
|
731
|
+
}],
|
|
732
|
+
"next_steps": [
|
|
733
|
+
{ "port_name": "output", "next_step_reference_key": "next_step_1", "next_port_name": "input" }
|
|
734
|
+
]
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### Input Fields
|
|
739
|
+
|
|
740
|
+
| Field | Value Type | Required | Description |
|
|
741
|
+
|-------|------|----------|-------------|
|
|
742
|
+
| `code` | `text_template` | Yes | Python code with a `def run(inputs):` function that returns a dict |
|
|
743
|
+
| `input_values` | `list_value` of `composite_value_list` | No | Array of `{name, value}` pairs passed as the `inputs` dict |
|
|
744
|
+
| `output_schema` | `literal` object with UI metadata | Yes* | `{fields: [{name, field_type, description, is_filterable, ui}]}` — defines outputs available downstream |
|
|
745
|
+
|
|
746
|
+
*Output schema is required if downstream steps reference the code's outputs.
|
|
747
|
+
|
|
748
|
+
### Output Port Schema
|
|
749
|
+
|
|
750
|
+
The `output` port's `field_descriptors` must match the `output_schema` fields:
|
|
751
|
+
```json
|
|
752
|
+
"output_ports": [
|
|
753
|
+
{"name": "output", "schema": {"field_descriptors": [
|
|
754
|
+
{"field_type": "text", "data_name": "cleaned", "db_name": "cleaned", "description": "cleaned", "name": "cleaned", "oasis": {"name": "cleaned"}}
|
|
755
|
+
], "type": "field_descriptor"}, "type": "default"},
|
|
756
|
+
<error_port>
|
|
757
|
+
]
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
### Output Schema Field Types
|
|
761
|
+
|
|
762
|
+
| `field_type` | Python Return Type | Example |
|
|
763
|
+
|-------------|-------------------|---------|
|
|
764
|
+
| `text` | `str` | `"hello"` |
|
|
765
|
+
| `int` | `int` | `42` |
|
|
766
|
+
| `double` | `float` | `3.14` |
|
|
767
|
+
| `bool` | `bool` | `True` / `False` |
|
|
768
|
+
| `timestamp` | `str` (ISO 8601) | `"2026-01-28T18:08:38+0000"` |
|
|
769
|
+
| `id` | `str` (DevRev ID) | `"don:core:dvrv-us-1:devo/0:ticket/123"` |
|
|
770
|
+
| `[]text` | `list[str]` | `["a", "b", "c"]` |
|
|
771
|
+
|
|
772
|
+
### Accessing Code Outputs
|
|
773
|
+
|
|
774
|
+
Downstream steps reference code outputs via JSONata:
|
|
775
|
+
|
|
776
|
+
```
|
|
777
|
+
$get('invoke_code_1', 'output').word_count
|
|
778
|
+
$get('invoke_code_1', 'output').is_long
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Python Environment
|
|
782
|
+
|
|
783
|
+
**Execution limits:**
|
|
784
|
+
- Timeout: 30s default, max 120s (configurable)
|
|
785
|
+
- Memory: 256MB
|
|
786
|
+
- Output + log size: 512KB
|
|
787
|
+
|
|
788
|
+
**Available libraries:** `json`, `re`, `math`, `datetime`, `requests`, `collections`, `itertools`, `functools`, `string`, `random`, `decimal`, `statistics`, `typing`, `copy`, `textwrap`, `hashlib`, `hmac`, `secrets`, `base64`, `uuid`, `urllib`, `html`, `xml`, `csv`, `zipfile`, `gzip`, `zoneinfo`, `io`, `dataclasses`, `operator`
|
|
789
|
+
|
|
790
|
+
**Blocked libraries:** `os`, `sys`, `subprocess`, `shutil`, `pathlib`, `multiprocessing`, `threading`, `concurrent`, `pickle`
|
|
791
|
+
|
|
792
|
+
### Code Patterns
|
|
793
|
+
|
|
794
|
+
**Data transformation:**
|
|
795
|
+
```python
|
|
796
|
+
def run(inputs):
|
|
797
|
+
tags_str = inputs.get('tags', '')
|
|
798
|
+
return {'tag_list': [t.strip() for t in tags_str.split(',') if t.strip()]}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
**Text processing with regex:**
|
|
802
|
+
```python
|
|
803
|
+
import re
|
|
804
|
+
def run(inputs):
|
|
805
|
+
text = inputs.get('text', '')
|
|
806
|
+
emails = re.findall(r'[\\w.-]+@[\\w.-]+\\.\\w+', text)
|
|
807
|
+
cleaned = re.sub(r'@\\w+', '', text).strip()
|
|
808
|
+
return {'emails': emails, 'cleaned_text': cleaned, 'has_emails': len(emails) > 0}
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
**Conditional routing logic:**
|
|
812
|
+
```python
|
|
813
|
+
def run(inputs):
|
|
814
|
+
severity = inputs.get('severity', 'low').lower()
|
|
815
|
+
scores = {'critical': 100, 'high': 75, 'medium': 50, 'low': 25}
|
|
816
|
+
score = scores.get(severity, 25)
|
|
817
|
+
return {'priority_score': score, 'escalate': score >= 75, 'team': 'tier-2' if score >= 75 else 'tier-1'}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
**Date arithmetic:**
|
|
821
|
+
```python
|
|
822
|
+
from datetime import datetime, timedelta
|
|
823
|
+
def run(inputs):
|
|
824
|
+
created = inputs.get('created_date', '')
|
|
825
|
+
dt = datetime.fromisoformat(created.replace('Z', '+00:00'))
|
|
826
|
+
sla_deadline = (dt + timedelta(hours=24)).isoformat()
|
|
827
|
+
return {'sla_deadline': sla_deadline}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
**Dynamic message generation:**
|
|
831
|
+
```python
|
|
832
|
+
def run(inputs):
|
|
833
|
+
name = inputs.get('customer_name', 'Customer')
|
|
834
|
+
ticket_id = inputs.get('ticket_id', 'N/A')
|
|
835
|
+
summary = inputs.get('summary', '')[:100]
|
|
836
|
+
msg = f"Hello {name}, your ticket #{ticket_id} has been received. Summary: {summary}"
|
|
837
|
+
return {'notification_message': msg, 'subject_line': f'[#{ticket_id}] Support request received'}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
**HTTP request (using requests library):**
|
|
841
|
+
```python
|
|
842
|
+
import requests
|
|
843
|
+
def run(inputs):
|
|
844
|
+
url = inputs.get('api_url', '')
|
|
845
|
+
resp = requests.get(url, timeout=10)
|
|
846
|
+
data = resp.json()
|
|
847
|
+
return {'status': resp.status_code, 'result': data.get('result', '')}
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### Best Practices
|
|
851
|
+
|
|
852
|
+
- Always use `inputs.get('key', default)` — never `inputs['key']`
|
|
853
|
+
- Define output schema for every key returned — outputs are invisible downstream without it
|
|
854
|
+
- Add `try`/`except` for error handling; return error info in outputs
|
|
855
|
+
- Keep code focused — one Code node per logical operation
|
|
856
|
+
- Use `\\n` for newlines when embedding code in the JSON `code` field (the value is a single string)
|
|
857
|
+
|
|
858
|
+
## UI Metadata
|
|
859
|
+
|
|
860
|
+
Assign positions to steps for visual layout. Space them logically:
|
|
861
|
+
- Triggers at the top (y ~0)
|
|
862
|
+
- Subsequent steps flow downward (increment y by ~150)
|
|
863
|
+
- Branches spread horizontally (vary x by ~200-300)
|
|
864
|
+
|
|
865
|
+
```json
|
|
866
|
+
"ui_metadata": {
|
|
867
|
+
"position": { "x": 0, "y": 0 }
|
|
868
|
+
}
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
## Reference Key Naming Convention
|
|
872
|
+
|
|
873
|
+
Use the operation slug with a numeric suffix (matching the pattern from example templates):
|
|
874
|
+
- Triggers: `ticket_created_1`, `issue_updated_1`, `opportunity_updated_1`
|
|
875
|
+
- Actions: `add_comment_1`, `update_ticket_1`, `send_notification_1`, `get_account_1`
|
|
876
|
+
- Controls: `if_else_1`, `for_each_1`, `while_1`
|
|
877
|
+
- Blockings: `sleep_for_1`, `sleep_until_1`
|
|
878
|
+
|
|
879
|
+
## Available Operations
|
|
880
|
+
|
|
881
|
+
Consult these files for the full list of available operations:
|
|
882
|
+
|
|
883
|
+
- **Triggers (145):** `.claude/skills/create-workflow-template/operations/triggers.md`
|
|
884
|
+
- **Actions (245):** `.claude/skills/create-workflow-template/operations/actions.md`
|
|
885
|
+
- **Blockings (2):** `.claude/skills/create-workflow-template/operations/blockings.md`
|
|
886
|
+
- **Controls (8):** `.claude/skills/create-workflow-template/operations/controls.md`
|
|
887
|
+
|
|
888
|
+
## Operation Schemas (Input/Output Ports)
|
|
889
|
+
|
|
890
|
+
For 106 operations, detailed resolved schemas are available with exact field names, types, required flags, enum values, and composite sub-fields:
|
|
891
|
+
|
|
892
|
+
- **Schema Index:** `.claude/skills/create-workflow-template/operations/schema-index.md` (39 triggers, 62 actions, 5 other)
|
|
893
|
+
- **Per-operation schemas:** `.claude/skills/create-workflow-template/operations/schemas/<slug>.md`
|
|
894
|
+
|
|
895
|
+
Use these schemas to determine the exact field names and types when building `input_values` for a step. For example, to see what fields `create_ticket` accepts, read `operations/schemas/create_ticket.md`.
|
|
896
|
+
|
|
897
|
+
## Example Templates
|
|
898
|
+
|
|
899
|
+
Study these real-world templates in `.claude/skills/create-workflow-template/examples/` for patterns:
|
|
900
|
+
|
|
901
|
+
### Validated Working Examples (`working-*.json`)
|
|
902
|
+
|
|
903
|
+
These templates have been confirmed to import successfully. **Always study these first** when building similar workflows:
|
|
904
|
+
|
|
905
|
+
| File | Steps | Pattern Demonstrated |
|
|
906
|
+
|------|-------|---------------------|
|
|
907
|
+
| `working-loop-variable-sample.json` | 7 | **Loop + variable pattern**: timer_trigger -> init_variable -> loop_over_issues -> [http -> set_variable] -> ask_ai -> send_notification. Shows `loop_over_*`, `init_variable`, `set_variable`, `$get_variable`, `block_step_reference_key`, `uenum` fields, `block_callback` port, timer trigger with uenum |
|
|
908
|
+
| `working-invoke-code-sample.json` | 4 | **Code node pattern**: enhancement_updated -> if_else -> invoke_code -> update_enhancement. Shows `invoke_code` with `text_template` code, `composite_value_list` inputs, `output_schema` with UI metadata |
|
|
909
|
+
| `working-csat-score-on-ticket-resolved.json` | 5 | **HTTP + AI pattern**: ticket_updated -> if_else (check resolved) -> http (timeline-entries API) -> ask_ai (CSAT analysis) -> add_comment. Shows HTTP with Bearer auth, `text_template` body with expressions, AI prompt composition |
|
|
910
|
+
| `working-enhancement-replace-agent.json` | 4 | **Code + update pattern**: enhancement_updated -> if_else (contains check) -> invoke_code (regex replace) -> update_enhancement. Shows `invoke_code` with Python regex, passing data between steps |
|
|
911
|
+
|
|
912
|
+
### Reference Examples (from production)
|
|
913
|
+
|
|
914
|
+
| File | Steps | Pattern Demonstrated |
|
|
915
|
+
|------|-------|---------------------|
|
|
916
|
+
| `4392-Async opportunity review agent` | 2 | Simple trigger -> action, agent interaction |
|
|
917
|
+
| `4505-Auto-update issue tcd` | 3 | Trigger with `fields_to_watch` + `_filter`, chained updates |
|
|
918
|
+
| `5216-Account segment missing notification` | 4 | Trigger -> get -> if_else -> comment, `_filter` with stage check |
|
|
919
|
+
| `4441-Ticket escalator from customer message` | 11 | Complex: trigger -> ask_ai -> if_else -> get -> nested if_else -> update + notify |
|
|
920
|
+
| `3592-Generate rca from pia` | 8 | Incident trigger -> ask_ai -> create_article, if_else branching |
|
|
921
|
+
| `5040-Devrevu enablement journey poc emails` | 8 | Custom object trigger, HTTP calls, sleep_for, third-party operations |
|
|
922
|
+
| `5158-Devrevu enablement journey mailing` | 43 | Complex sequential flow with many sleep_for + if_else + third-party email |
|
|
923
|
+
|
|
924
|
+
To read an example: `python3 -c "import json; d=json.load(open('.claude/skills/create-workflow-template/examples/FILENAME.json')); inner=json.loads(d['data']); print(json.dumps(inner, indent=2))"`
|
|
925
|
+
|
|
926
|
+
## Complete Example: Opportunity Updated -> Check Account -> Notify
|
|
927
|
+
|
|
928
|
+
Based on the real `5216-Account segment missing notification` template pattern:
|
|
929
|
+
|
|
930
|
+
```json
|
|
931
|
+
{
|
|
932
|
+
"serialization_version": { "major": 2, "minor": 0, "patch": 0 },
|
|
933
|
+
"title": "Account Segment Missing Notification",
|
|
934
|
+
"description": "When an opportunity moves to evaluate stage, check if the account has a segment set. If not, notify the opportunity owner.",
|
|
935
|
+
"steps": [
|
|
936
|
+
{
|
|
937
|
+
"name": "Opportunity Updated",
|
|
938
|
+
"description": "Triggers when an opportunity is updated",
|
|
939
|
+
"reference_key": "opportunity_updated_1",
|
|
940
|
+
"operation": { "namespace": "devrev", "slug": "opportunity_updated" },
|
|
941
|
+
"input_values": [{
|
|
942
|
+
"port_name": "input",
|
|
943
|
+
"fields": [
|
|
944
|
+
{
|
|
945
|
+
"name": "fields_to_watch",
|
|
946
|
+
"value": {
|
|
947
|
+
"type": "list_value",
|
|
948
|
+
"value": { "items": [{ "type": "literal", "value": ["stage"] }] }
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
"name": "_filter",
|
|
953
|
+
"value": {
|
|
954
|
+
"type": "literal",
|
|
955
|
+
"value": {
|
|
956
|
+
"type": "group",
|
|
957
|
+
"logical_operator": "and",
|
|
958
|
+
"negate": false,
|
|
959
|
+
"conditions": [{
|
|
960
|
+
"type": "group",
|
|
961
|
+
"logical_operator": "and",
|
|
962
|
+
"negate": false,
|
|
963
|
+
"conditions": [{
|
|
964
|
+
"type": "rule",
|
|
965
|
+
"operator": "equals",
|
|
966
|
+
"operands": [
|
|
967
|
+
{ "type": "jsonata_expression", "value": "$get('opportunity_updated_1', 'output').stage.name" },
|
|
968
|
+
{ "type": "literal", "value": "3-evaluate" }
|
|
969
|
+
]
|
|
970
|
+
}]
|
|
971
|
+
}]
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
]
|
|
976
|
+
}],
|
|
977
|
+
"next_steps": [
|
|
978
|
+
{ "port_name": "output", "next_step_reference_key": "get_account_1", "next_port_name": "input" }
|
|
979
|
+
],
|
|
980
|
+
"ui_metadata": { "position": { "x": 0, "y": 0 } }
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
"name": "Get Account",
|
|
984
|
+
"description": "Fetch the account linked to the opportunity",
|
|
985
|
+
"reference_key": "get_account_1",
|
|
986
|
+
"operation": { "namespace": "devrev", "slug": "get_account" },
|
|
987
|
+
"input_values": [{
|
|
988
|
+
"port_name": "input",
|
|
989
|
+
"fields": [{
|
|
990
|
+
"name": "id",
|
|
991
|
+
"value": { "type": "jsonata_expression", "value": "$get('opportunity_updated_1', 'output').account.id" }
|
|
992
|
+
}]
|
|
993
|
+
}],
|
|
994
|
+
"next_steps": [
|
|
995
|
+
{ "port_name": "output", "next_step_reference_key": "if_else_1", "next_port_name": "input" }
|
|
996
|
+
],
|
|
997
|
+
"ui_metadata": { "position": { "x": 0, "y": 150 } }
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
"name": "Check Segment Exists",
|
|
1001
|
+
"description": "Check if the account has a segment set",
|
|
1002
|
+
"reference_key": "if_else_1",
|
|
1003
|
+
"operation": { "namespace": "devrev", "slug": "if_else" },
|
|
1004
|
+
"input_values": [{
|
|
1005
|
+
"port_name": "input",
|
|
1006
|
+
"fields": [{
|
|
1007
|
+
"name": "condition",
|
|
1008
|
+
"value": {
|
|
1009
|
+
"type": "literal",
|
|
1010
|
+
"value": {
|
|
1011
|
+
"type": "group",
|
|
1012
|
+
"logical_operator": "and",
|
|
1013
|
+
"negate": false,
|
|
1014
|
+
"conditions": [{
|
|
1015
|
+
"type": "group",
|
|
1016
|
+
"logical_operator": "and",
|
|
1017
|
+
"negate": false,
|
|
1018
|
+
"conditions": [{
|
|
1019
|
+
"type": "rule",
|
|
1020
|
+
"operator": "exists",
|
|
1021
|
+
"operands": [
|
|
1022
|
+
{ "type": "jsonata_expression", "value": "$get('get_account_1', 'output').tnt__segment" }
|
|
1023
|
+
]
|
|
1024
|
+
}]
|
|
1025
|
+
}]
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}]
|
|
1029
|
+
}],
|
|
1030
|
+
"next_steps": [
|
|
1031
|
+
{ "port_name": "true", "next_step_reference_key": "add_comment_1", "next_port_name": "input" }
|
|
1032
|
+
],
|
|
1033
|
+
"ui_metadata": { "position": { "x": 0, "y": 300 } }
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
"name": "Add Comment",
|
|
1037
|
+
"description": "Notify the opportunity owner about missing segment",
|
|
1038
|
+
"reference_key": "add_comment_1",
|
|
1039
|
+
"operation": { "namespace": "devrev", "slug": "add_comment" },
|
|
1040
|
+
"input_values": [{
|
|
1041
|
+
"port_name": "input",
|
|
1042
|
+
"fields": [
|
|
1043
|
+
{
|
|
1044
|
+
"name": "object",
|
|
1045
|
+
"value": { "type": "jsonata_expression", "value": "$get('opportunity_updated_1', 'output').id" }
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
"name": "visibility",
|
|
1049
|
+
"value": { "type": "literal", "value": "internal" }
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
"name": "body",
|
|
1053
|
+
"value": {
|
|
1054
|
+
"type": "text_template",
|
|
1055
|
+
"value": "Hey {% expr $get('opportunity_updated_1', 'output').owned_by[0].id %}, the opportunity has moved to evaluate stage but the account is missing a segment. Please update it."
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
]
|
|
1059
|
+
}],
|
|
1060
|
+
"ui_metadata": { "position": { "x": 0, "y": 450 } }
|
|
1061
|
+
}
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
## Validation Checklist
|
|
1067
|
+
|
|
1068
|
+
Before outputting the template, verify:
|
|
1069
|
+
|
|
1070
|
+
1. `serialization_version` is `{"major": 2, "minor": 0, "patch": 0}`
|
|
1071
|
+
2. `title` is present and 1-256 chars
|
|
1072
|
+
3. Every step has a unique `reference_key` matching `^[a-zA-Z_][a-zA-Z0-9_]*$`
|
|
1073
|
+
4. Every step has a valid `operation` with `namespace` and `slug`
|
|
1074
|
+
5. At least one step is a trigger operation
|
|
1075
|
+
6. All `next_step_reference_key` values reference existing steps
|
|
1076
|
+
7. `if_else` steps use `"true"` and `"false"` port names
|
|
1077
|
+
8. `loop_over_*`/`for_each`/`while` steps use `"block_start"` and `"output"` port names
|
|
1078
|
+
9. Steps inside loops have `block_step_reference_key` pointing to their parent loop
|
|
1079
|
+
10. JSONata expressions use `$get('reference_key', 'output')` syntax referencing existing steps
|
|
1080
|
+
11. Input field names match the operation's input schema (check `.claude/skills/create-workflow-template/operations/*.md`)
|
|
1081
|
+
12. No circular references in `next_steps`
|
|
1082
|
+
13. Value types use string names: `"literal"`, `"jsonata_expression"`, `"text_template"`, `"list_value"`, `"composite_value"`, `"composite_value_list"`
|
|
1083
|
+
14. Trigger `_filter` conditions use the nested group-of-groups structure
|
|
1084
|
+
15. The final output is wrapped: `{"templateVersion": "2.0.0", "data": "<stringified inner JSON>"}`
|
|
1085
|
+
16. **All `uenum` fields have `allowed_values`** with `{id, label, ordinal, value}` objects — missing this causes "Missing required field: allowed_values"
|
|
1086
|
+
17. **All `array` fields have `base_type`** (`id`, `text`, `composite`, `enum`) — missing this causes "discriminator not set: base_type"
|
|
1087
|
+
18. **`loop_over_*` steps** have `block_callback` input port and `block_start` output port with `type: "block_start"`
|
|
1088
|
+
19. **`init_variable` output port** includes `scope_variables` array matching the variable definitions
|
|
1089
|
+
20. **`invoke_code` code field** uses `text_template` type (not `literal`), input_values items use `composite_value_list` (not `composite_value`)
|
|
1090
|
+
21. **Use `loop_over_*`** instead of `for_each` for native DevRev objects (issues, tickets, accounts, etc.) — `for_each` has dynamic schemas that cause import errors
|
|
1091
|
+
22. **After loops**, read accumulated variables with `$get_variable('init_variable_1','var_name',{'output_port_name':'output'})` (not `$get`)
|