Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions skill-flavors/studioweb/uipath-api-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,11 @@ Fix failures in category order — **Structure > Expression > Activity Config >
<!--skill-flavor:file-run-cli:start-->
In Studio Web the run goes through the consent-gated, schema-inspected `RunProject` host operation; file arguments are supplied through the fields it declares for `JobAttachment` inputs (the host uploads them), and returned files appear as attachment references in the host result. Validation with `uip api-workflow validate` works offline as usual.
<!--skill-flavor:file-run-cli:end-->

<!--skill-flavor:trigger-binding-registration:start-->
- **Studio Web owns the trigger's deployment registration.** It derives the `EventTrigger` binding from the saved workflow; treat host-generated bindings and solution resources as authoritative (rule 16). Keep the trigger's `objectName` / `eventType` / `eventMode` / `filterExpression` accurate. After saving, confirm it renders as a **trigger card**; a plain connector card means wrong event metadata — re-stub.
<!--skill-flavor:trigger-binding-registration:end-->

<!--skill-flavor:trigger-debug-contract:start-->
- **Exercise a trigger by supplying the payload.** Pass an execution input shaped like the event's output fields through the host `RunProject` operation; the trigger passes it through. With no input the runtime fetches a recent event through the live connection (`polling` always, `webhooks` only where the connector supports debug polling) — side-effecting under rule 21.
<!--skill-flavor:trigger-debug-contract:end-->
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!--skill-flavor:trigger-binding-rule:start-->
2. **Studio Web owns the trigger's deployment registration.** It derives the `EventTrigger` binding from the saved workflow; treat host-generated bindings and solution resources as authoritative (rule 16). Keep the trigger first and its `objectName` / `eventType` / `eventMode` / `filterExpression` accurate. After saving, confirm the activity renders as a **trigger card**; a plain connector card means wrong event metadata, so re-stub.
<!--skill-flavor:trigger-binding-rule:end-->

<!--skill-flavor:trigger-schedule-scope:start-->
4. **Only connector events live in the workflow file.** A schedule is an Orchestrator trigger on the deployed process, managed outside the workflow. See [operating-published-workflows.md](operating-published-workflows.md).
<!--skill-flavor:trigger-schedule-scope:end-->

<!--skill-flavor:trigger-authoring-steps:start-->
1. `uip api-workflow registry resolve "<KEYWORD>" --kind trigger --output json` → trigger type id
2. `uip is connections list <connector-key> --output json` → connection UUID, then `uip is connections ping <CONNECTION_UUID> --output json` — REQUIRED
3. `GenericTrigger` only: `uip is triggers objects <connector-key> <EVENT> --connection-id <CONNECTION_UUID> --output json` → object name
4. `uip is triggers describe <connector-key> <EVENT> <OBJECT> --connection-id <CONNECTION_UUID> --output json` → event parameters
5. `uip api-workflow registry stub <TRIGGER_TYPE_ID> --connection-id <CONNECTION_UUID> [--object-name <OBJECT>] [--inputs '<json>'] --output json`
6. Insert `Data.Activity` as the first entry after `WorkflowStart` in `/solution/<projectName>/Workflow.json` and save.
7. From that project directory, `uip api-workflow validate Workflow.json --output json` until `Data.Status` is `Valid`.
<!--skill-flavor:trigger-authoring-steps:end-->

<!--skill-flavor:trigger-kind-unavailable:start-->
If `resolve` answers `unknown option '--kind'`, the embedded CLI predates trigger support. Report that host capability gap and ask how the user wants to proceed; hand-authoring is not the fallback (rule 3).
<!--skill-flavor:trigger-kind-unavailable:end-->

<!--skill-flavor:trigger-binding-command:start-->
Studio Web derives it from the saved workflow, including the `Property` entry for the event parameters the connector registers with. Treat host-generated bindings and solution resources as authoritative (rule 16). After saving, confirm the activity renders as a **trigger card**; re-stub if it renders as a plain connector card.
<!--skill-flavor:trigger-binding-command:end-->

<!--skill-flavor:trigger-local-run:start-->
Run through the consent-gated, schema-inspected `RunProject` host operation:

| Input | Result |
|---|---|
| Execution input shaped like `outputFields` | The trigger passes it through as the event payload; no connector call. The safe way to exercise the body. |
| No input | The runtime fetches a recent matching event through the live connection: always for `polling`; for `webhooks` only when the connector has a debug-polling configuration, otherwise it fails with publish guidance. Reaches the vendor; side-effecting under rule 21. |

Studio Web's **Test trigger** panel checks filter matches against recent events without running the workflow. Offline `uip api-workflow validate` stays the autonomous pre-flight.
<!--skill-flavor:trigger-local-run:end-->

<!--skill-flavor:trigger-clean-gate-antipattern:start-->
A clean `uip api-workflow validate` proves the file is well formed, and only that. The subscription follows from the saved trigger activity (rule 2), so re-read it before calling the work done.
<!--skill-flavor:trigger-clean-gate-antipattern:end-->
12 changes: 11 additions & 1 deletion skills/uipath-api-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- User asks about **nested control flow** — If inside ForEach, TryCatch around a loop, conditional Break, multi-way branching, etc.
- User asks for an **Integration Service connector activity** (Gmail Send Email, Outlook Get Newest Email, GitHub Search Issues, Slack Send Message, etc.) — follow the discovery flow in [references/connector-activity-discovery.md](references/connector-activity-discovery.md)
- User asks for a **generic HTTP Request** that needs to render in StudioWeb's designer — same discovery flow
- User wants the workflow to **start from a connector event** ("when a Slack button is clicked"), or to change an existing trigger — see [references/trigger-authoring-guide.md](references/trigger-authoring-guide.md)
- User asks about **JavaScript expressions, `$context`, `$input`, `$workflow`, `WorkflowStart`, or the `export.as` pattern**
<!--skill-flavor:surface-operations-scope:start-->
- User asks how to **debug** a failing API workflow run — the local `validate` → `run --no-auth` loop, or a **post-publish cloud run** (job logs/traces). See [references/operating-published-workflows.md](references/operating-published-workflows.md)
Expand Down Expand Up @@ -93,14 +94,21 @@
- **Connector params use flat dotted keys and BARE literals.** `"message.toRecipients": "..."`, not nested objects; plain `"x@y.com"`, not `"${'x@y.com'}"` — rule 5's wrap is **inverted** here (`${'...'}` clears the field on save). Real references (`${$context...}`) stay wrapped.
- **NEVER use Http kind with a vendor connection UUID** (401 "Invalid Element token"). IntSvc output is wrapped: read `$context.outputs.<ExportBucketKey>.content.<field>`.
<!--skill-flavor:connector-solution-registration:start-->
- **(Solutions-mode + IntSvc only)** sync the connection into the catalogue: `uip api-workflow bindings sync --workflow <Workflow.json>` then `uip solution resource refresh --solution-folder <path>`. Skip for Http kind, non-connector activities, and standalone (no `Solution/`) projects.
- **(Solutions-mode + IntSvc only)** sync the connection into the catalogue: `uip api-workflow bindings sync --workflow <Workflow.json>` then `uip solution resources refresh --solution-folder <path>`. Skip for Http kind, non-connector activities, and standalone (no `Solution/`) projects.
<!--skill-flavor:connector-solution-registration:end-->
16a. **A connector-event trigger is a separate catalog, a separate activity shape, and a second artifact.** When the workflow must start from an event (Slack button clicked, new Outlook calendar entry), run `uip api-workflow registry resolve "<keyword>" --kind trigger` then `registry stub` — never hand-author. The stub is the workflow's FIRST activity after `WorkflowStart`, at most one, `call: "UiPath.IntSvcEvent"`. A `GenericTrigger` needs `--object-name`. Full flow, filter syntax, anti-patterns: [references/trigger-authoring-guide.md](references/trigger-authoring-guide.md).
<!--skill-flavor:trigger-binding-registration:start-->
- **Run `uip api-workflow bindings sync` after every trigger add or edit.** It writes the `EventTrigger` entry in `bindings_v2.json` that registers the Orchestrator event trigger on deploy. **Without it the workflow validates, packs, publishes and deploys clean — and never fires.** No gate catches this. In Solutions mode follow with `uip solution resources refresh` (rule 16).
<!--skill-flavor:trigger-binding-registration:end-->
<!--skill-flavor:trigger-debug-contract:start-->
- **A `webhooks` trigger cannot run locally without input.** `uip api-workflow run` replays a live event only for `polling` (side-effecting under rule 21); for either mode, `--input-arguments` shaped like the event payload exercises the rest of the workflow offline.
<!--skill-flavor:trigger-debug-contract:end-->
<!--skill-flavor:runtime-invocation-io:start-->
17. **Pass input as a JSON string.** `--input-arguments '{"key":"value"}'`. Invalid JSON exits 1.
18. **Always `--output json`** when parsing CLI output programmatically. Success → `{ "Result": "Success", "Code": "WorkflowRun", "Data": {...} }`. Failure → `{ "Result": "Failure", "Message": "...", "Instructions": "..." }` with exit 1.
<!--skill-flavor:runtime-invocation-io:end-->
<!--skill-flavor:project-creation:start-->
19. **Scaffold with `uip api-workflow init`; publish goes through the solution packager.** Create every API workflow project with `uip api-workflow init <name>` (rule 19a) — never hand-assemble the project files. Project-level CLI commands also exist: `uip api-workflow build <projectDir>` (compile) and `uip api-workflow pack <projectDir> <outputDir>` (single-project `.nupkg`, useful to test one project in isolation). Solution-level build/publish go through `uip solution pack <solutionDir> <outputDir>` + `uip solution publish <package.zip>`. There is NO `uip api-workflow publish` command. Project type must be `"Api"` in the solution `.uipx`.

Check warning on line 111 in skills/uipath-api-workflow/SKILL.md

View workflow job for this annotation

GitHub Actions / skills/uipath-api-workflow

Possibly stale `uip api-workflow publish` (valid prefix: `api-workflow`)

19a. **Create projects with `uip api-workflow init <name>` — it produces the correct Studio Web editable shape and wires the solution.** Run it from inside the solution directory (the folder containing the `.uipx`):
```bash
Expand Down Expand Up @@ -278,14 +286,14 @@

```bash
# 0. Create the solution (skip if one already exists). Creates ./MySolution/ with the .uipx.
uip solution init MySolution --output json

Check warning on line 289 in skills/uipath-api-workflow/SKILL.md

View workflow job for this annotation

GitHub Actions / skills/uipath-api-workflow

Possibly stale `uip solution init MySolution` (valid prefix: `solution init`)

# 1. Scaffold the project — correct Studio Web shape + auto-registers in the .uipx (rule 19a).
# init's <name> arg takes no slashes, so cd into the solution dir first; it registers the
# project in the nearest parent .uipx. Creates MyApiProject/ with project.uiproj,
# Workflow.json, entry-points.json, bindings_v2.json.
cd ./MySolution
uip api-workflow init MyApiProject --output json

Check warning on line 296 in skills/uipath-api-workflow/SKILL.md

View workflow job for this annotation

GitHub Actions / skills/uipath-api-workflow

Possibly stale `uip api-workflow init MyApiProject` (valid prefix: `api-workflow init`)

# 1b. TDD gate (rule 22): if ./MyApiProject/evals/ exists (next to Workflow.json), STOP and ask the two questions
# (tests: change / add? loop mode?) and wait for the answers before step 2.
Expand Down Expand Up @@ -317,6 +325,7 @@
| [references/task-types.md](references/task-types.md) | Adding/editing any single activity — exact JSON shape, required fields, export pattern, common mistakes, basic nesting hints per type |
| [references/control-flow-patterns.md](references/control-flow-patterns.md) | Combining activities into hierarchical structures — nested If, ForEach inside DoWhile, TryCatch around/inside loops, conditional Break, multi-way branching, key uniqueness rules |
| [references/connector-activity-discovery.md](references/connector-activity-discovery.md) | Authoring HTTP Request / Gmail / Outlook / GitHub / Slack / etc. activities via `uip api-workflow registry resolve` + `stub` — three-step flow, sample stub output, field-shape rules, multipart subsection, worked examples |
| [references/trigger-authoring-guide.md](references/trigger-authoring-guide.md) | **Triggers** — starting from a connector event: `resolve --kind trigger` + `stub`, the `EventTrigger` binding, JMESPath filters, polling vs webhooks |
| [references/expressions-and-context.md](references/expressions-and-context.md) | Writing JS expressions, propagating outputs via `export.as`, accessing `$context` / `$input` / `$workflow`, JS_Invoke argument passing, strict-mode gotchas, key patterns |
| [references/files-and-base64.md](references/files-and-base64.md) | **Files & base64** — `JobAttachment` references, the File to Base64 / Base64 to File activities (exact JSON, `$helpers.file.*`), `serializeData()` for inline bodies/Responses, passing local files in and getting files out of a run, pitfalls |
<!--skill-flavor:cli-reference-navigation:start-->
Expand All @@ -340,6 +349,7 @@
| [assets/templates/loop-aggregation-example.json](assets/templates/loop-aggregation-example.json) | DoWhile + ForEach + Assign accumulation — pure-compute aggregation pattern |
| [assets/templates/nested-control-flow-example.json](assets/templates/nested-control-flow-example.json) | Heavy nesting demo — TryCatch around DoWhile around If with conditional Break |
| [assets/templates/file-base64-roundtrip-example.json](assets/templates/file-base64-roundtrip-example.json) | **Files** — a `document` file input → File to Base64 → Base64 to File → Response returning both references. The exact `run.script` shape Studio Web writes for the two activities (rule 23). Verified end-to-end with a signed-in run: local file in → `.base64` reference → decoded file out, bytes identical. |
| [assets/templates/trigger-workflow-template.json](assets/templates/trigger-workflow-template.json) | **Trigger** — `WorkflowStart` → `UiPath.IntSvcEvent` → Response reading the payload. `<REPLACE_WITH_*>` values are sentinels: re-stub for the real ones (rule 16a). |
<!--skill-flavor:template-execution-proof:start-->
| [assets/templates/connector-call-example.json](assets/templates/connector-call-example.json) | **Http kind** — HTTP Request curated activity (`call: "UiPath.Http"`) for arbitrary REST calls. Generated by `registry stub` against the catfacts URL. Shows the canonical shape: `connectionId: "ImplicitConnection"`, `unifiedTypesCompatible: true`, `savedJitInputFieldId: "in_http-request"`, URL in `bodyParameters.url`. Verified end-to-end with `uip api-workflow run --no-auth`. |
<!--skill-flavor:template-execution-proof:end-->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"document": {
"dsl": "1.0.0",
"name": "Workflow",
"tags": {},
"version": "0.0.1",
"namespace": "default",
"metadata": {
"variables": []
}
},
"do": [
{
"Sequence_1": {
"do": [
{
"WorkflowStart": {
"set": "${ { ...Object.entries($workflow.definition?.document?.metadata?.variables?.schema?.document?.properties || {}).reduce((acc, [name, def]) => ({ ...acc, [name]: def?.default }), {}), ...($workflow.input || {}) } }",
"output": {
"as": "${$input}"
},
"export": {
"as": "{ ...$context, variables: { ...$context.variables, ...$output } }"
},
"metadata": {
"activityType": "Assign",
"displayName": "Workflow start",
"fullName": "Assign",
"isTransparent": true
}
}
},
{
"Button_Clicked_1": {
"call": "UiPath.IntSvcEvent",
"with": {
"connector": "uipath-salesforce-slack",
"connectionId": "<REPLACE_WITH_VENDOR_CONNECTION_UUID>",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A placeholder connection gets registered silently. bindings sync writes an EventTrigger keyed
<REPLACE_WITH_VENDOR_CONNECTION_UUID> with no warning. The e2e check that the key is non-empty accepts it.

"connectionResourceId": "<REPLACE_WITH_VENDOR_CONNECTION_UUID>",
"eventParameters": {
"channel_id": "<CHANNEL_ID>"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template and the offline seed contradict the guide. is triggers describe marks both user_id and channel_id as
required for Slack Button Clicked. The template and seed set only channel_id, and the stub warns about the missing
user_id — the warning the guide says to heed.

},
"objectName": "button",
"eventType": "BUTTON_CLICKED",
"eventMode": "webhooks",
"filterExpression": "(channel_id == '<CHANNEL_ID>')"
},
"export": {
"as": "{ ...$context, outputs: { ...$context?.outputs, \"button_1\": $output } }"
},
"metadata": {
"activityType": "Connector",
"fullName": "Connector",
"displayName": "Button Clicked",
"uiPathActivityTypeId": "<REPLACE_WITH_TRIGGER_TYPE_ID>",
"configuration": "{\"essentialConfiguration\": {\"connectorVersion\": \"\", \"executionType\": null, \"scriptRef\": null, \"customFieldsRequestDetails\": null, \"instanceParameters\": {\"connectorKey\": \"uipath-salesforce-slack\", \"objectName\": \"button\", \"activityType\": \"CuratedTrigger\", \"version\": \"1.0.0\", \"eventOperation\": \"BUTTON_CLICKED\", \"eventMode\": \"webhooks\", \"supportsStreaming\": false}, \"objectName\": \"button\", \"packageVersion\": \"1.0.0\", \"httpMethod\": null, \"path\": null, \"filter\": null}}"
}
}
},
{
"Response_1": {
"response": "${{ clicked: $context.outputs.button_1?.content }}",
"markJobAsFailed": false,
"then": "end",
"metadata": {
"activityType": "Response",
"displayName": "Response",
"fullName": "Response"
}
}
}
],
"metadata": {
"activityType": "Sequence",
"displayName": "Sequence",
"fullName": "Sequence"
}
}
}
],
"evaluate": {
"mode": "strict",
"language": "javascript"
}
}
Loading
Loading