Agent Skill · UiPath

uipath-api-workflow

[PREVIEW] UiPath API Workflow assistant — author, run, package, publish JSON workflows executed by `uip api-workflow run`. Covers logical/hierarchical structure (Sequence, Assign, JavaScript, If with #Wrapper/#Then/#Else, ForEach, DoWhile, Break, TryCatch, Wait, Response — including nested patterns) AND HTTP / Integration Service connector activities (Gmail, Outlook, GitHub, Slack, etc.) authored via `uip api-workflow registry resolve` + `stub`. Triggers on prompts about UiPath API workflows, project type "Api", JSON workflow files containing `document.dsl`/`do[]`, any of those activity types, or fetching data from a public/vendor API. Uses `uip api-workflow run` for local execution and `uip solution pack`/`publish` for deployment. For .flow Maestro→uipath-maestro-flow. For .xaml/coded RPA→uipath-rpa. For coded agents→uipath-agents. For Coded Apps→uipath-coded-apps.

Provider: UiPath Path in repo: skills/uipath-api-workflow/SKILL.md

Skill body

UiPath API Workflow Assistant

Preview — this skill is in preview. Behavior, file shapes, and rules may change as the underlying executor and StudioWeb roundtrip story evolve.

Build, run, and publish UiPath API Workflows — JSON files conforming to the CNCF Serverless Workflow DSL 1.0.0 with UiPath activity-type extensions. Executed by @uipath/api-workflow-executor via uip api-workflow run. Packaged as Type: "Api" projects via uip solution pack.

When to Use This Skill

Do NOT use for: .flow Maestro flows (→ uipath-maestro-flow), .xaml / coded RPA (→ uipath-rpa), coded agents (→ uipath-agents), Coded Web Apps (→ uipath-coded-apps).

Connector activities come from the registry, not from guessing. HTTP Request and Integration Service Connector activities (Gmail Send Email, Outlook Get Newest Email, etc.) need StudioWeb-side editor metadata (metadata.uiPathActivityTypeId + metadata.configuration) to render correctly in the designer. The data is NOT in uip is activities list. Use uip api-workflow registry resolve + uip api-workflow registry stub: resolve searches the StudioWeb TypeCache (projectType=Api) for candidate GUIDs; stub combines the TypeCache entry with the Integration Service Elements schema (full endpoint path, request/response fields, multipart signal) and emits a ready-to-paste activity object — picking Http kind (UiPath.Http) or IntSvc kind (UiPath.IntSvc) automatically, building metadata.configuration, computing the slot and export-bucket keys, and declaring multipartParameters when needed. Follow the flow in references/connector-activity-discovery.md. NEVER invent a uiPathActivityTypeId or hand-author the configuration blob — both come from the stub output.

Core Principles

  1. Know before you write. Read the existing workflow file before editing. Read an example template before creating from scratch.
  2. Start minimal, iterate to correct. Add one activity at a time. Run with --no-auth --output json after each addition. Fix what breaks. Repeat.
  3. Validate before running. uip api-workflow validate <Workflow.json> is the closure step for every authoring session. It performs static checks (JSON Schema + semantic) without touching the network — catching malformed JSON, unknown activityType values, missing per-activity required keys (e.g. If without do+switch, Assign without set, Connector without metadata.configuration/essentialConfiguration), bad evaluate.language/evaluate.mode, and duplicate/empty workflow variables. Use it as a fast pre-flight; uip api-workflow run is the runtime validator that catches what static analysis can’t (live HTTP calls, expression evaluation, connection state).
  4. Fix errors by category. Triage: Structure > Expression > Activity Config > Logic. Higher-category fixes often resolve lower-category errors automatically.

Critical Rules

  1. Workflow file is JSON, not YAML. Top-level keys: document (with dsl: "1.0.0"), evaluate (language: "javascript", mode: "strict"), do (one root sequence — named Sequence_1 in the template skeleton, but the literal key may differ in existing workflows; always read the actual key from the file before editing — containing WorkflowStart + user activities). See references/workflow-file-format.md.
  2. WorkflowStart is always the first activity inside the root sequence’s do array. It hydrates variable defaults into $context.variables and forwards inputs to $input. Never remove, rename, or modify it. isTransparent: true (only WorkflowStart uses true).
  3. Every activity is a single-key object wrapped in the do array: { "<ActivityKey>": { ...activity body... } }. Activity keys must be globally unique across the whole workflow — including #Wrapper, #Then, #Else, #Body suffixes.
  4. Every activity should export its output to propagate state. Two patterns:
    • Variables (Assign only): { ...$context, variables: { ...$context.variables, ...$output } }
    • Outputs (everything else): { ...$context, outputs: { ...$context?.outputs, "<ActivityKey>": $output } } See references/expressions-and-context.md.
  5. String literals in Assign.set / Response / If when MUST be wrapped as "${'literal'}" — a JS string inside an expression. Plain "literal" runs fine under uip api-workflow run, but StudioWeb’s designer normalizes unwrapped values to "${literal}" on save (treating them as expressions you typed into the property panel). At runtime the bare identifier literal has no binding → ReferenceError: literal is not defined. Use single quotes inside the expression to avoid JSON escaping: "set": { "tier": "${'PLATINUM'}" }. Numbers, booleans, and references like ${$context.variables.X} need no extra wrapping. (Response payloads have a related but distinct constraint — see rule 15.) Scope: this rule applies to Assign / Response / If / variable contexts only. It does NOT apply to connector bodyParameters / queryParameters / pathParameters — those take BARE literals; ${'...'} there is read as an expression and the field is cleared on save. See rule 16 and references/connector-activity-discovery.md#field-shape-rules-flat-keys-bare-literals-renamed-export-hub-prefix. See references/troubleshooting.md.
  6. Each Assign activity MUST set exactly ONE variable. Assign.set is a single-key object, NOT a multi-variable update. StudioWeb’s designer collapses multi-key set blocks to one key on save, silently dropping the others — the runtime then only updates the surviving key. To update N variables, use N separate Assign activities placed sequentially in the same do array. Example: instead of "set": { "sum": "${$context.variables.sum + 1}", "count": "${$context.variables.count + 1}" } (loses count after StudioWeb save), write two Assigns — Assign_Sum with "set": { "sum": "${...}" } and Assign_Count with "set": { "count": "${...}" }. Each runs in order; each Assign’s variables export merges its single key into $context.variables.
  7. If activity requires the wrapper pattern. If_N#Wrapper contains If_N (switch), If_N#Then, If_N#Else. Both #Then and #Else MUST end with "then": "exit" to prevent fall-through. Conditions in when MUST be wrapped in ${...}. For deeply-nested If patterns and multi-way branching, see references/control-flow-patterns.md.
  8. Loops (ForEach, DoWhile) require a #Body element inside do. ForEach body uses index-aware accumulation (resets on iteration 0); DoWhile body uses simple accumulation. Loop variables (each, at) are plain strings, NOT expressions.
  9. DoWhile for.in is always "${ [1] }". The doWhile condition controls repetition. The body MUST update the condition variable, otherwise the loop runs forever.
  10. Nested loops MUST use distinct iterator/index names. Outer for.each: "outerItem", inner for.each: "innerItem". Reusing currentItem shadows the outer. “Distinct” just means “not the same string” — semantic (outerItem / innerItem) and incremental (item1 / item2, currentItem / currentItem2) naming both work.
  11. Loop iterators and catch error variables are prefixed with $ in expressions. Declare for.each: "currentItem" (plain string, no $); reference it everywhere else (in when conditions, in script bodies, in set expressions, in body export patterns) as $currentItem — the $ is a literal character in the global identifier name. currentItem is not a reserved name — for.each: "customer" binds $customer, for.each: "row" binds $row, etc. Same shape for for.at ($currentItemIndex, $idx, etc.) and catch.as ($error, $err, etc.). Empirically verified: the executor calls setVariables({"$currentItem": item, ...})currentItem (no $) is not bound as a global. Forgetting the $ produces <name> is not defined.
  12. Break exits only the innermost enclosing loop. To exit nested loops, set a flag variable + check it in the outer loop. Break value MUST be the string "true", with then: "exit" and set: "${$input}". Only valid inside a #Body.
  13. Use $workflow.input.<name> to read workflow inputs, never $input.<name>. $input is the task’s input — for any non-first task, it’s the previous task’s output, NOT the workflow arguments.
  14. JavaScript scripts read $context/$workflow/$input as globals. Scripts MUST return a value. The task’s run.script.arguments field is StudioWeb designer scaffolding — keep it as the standard "${{ \"$context\": $context, \"$workflow\": $workflow, \"$input\": $input }}" block for designer roundtrip; the runtime ignores it.
  15. Response activity shape — STRICT for StudioWeb roundtrip:
    • markJobAsFailed is a sibling of response, not nested inside it.
    • Always include "then": "end" — without it, the workflow does not terminate properly. then: "end" is for Response only; then: "exit" is for control-flow branches/loops.
    • Object-valued responses MUST use the single-expression form, NOT the JSON-object-with-${}-fields form. StudioWeb’s designer corrupts the latter on save (issue SW-28452 / UiPath/cli#1537).
      • ✗ Wrong (CLI runs but StudioWeb corrupts): "response": { "tier": "${$context.variables.tier}", "count": "${$context.variables.count}" }
      • ✓ Correct: "response": "${{ tier: $context.variables.tier, count: $context.variables.count }}" Inside the outer ${{ ... }} you are already in expression scope, so reference variables/outputs directly without an inner ${...} wrapper. JS object literal keys can be unquoted identifiers (tier:, count:); literal string values use single quotes (status: 'ok'); numbers/booleans/references are bare. The designer leaves an already-wrapped single expression alone; the JSON-object form gets flattened to a stringified expression where inner ${...} substitutions are inside JS double-quoted strings (which don’t interpolate), turning each field into the literal text of its expression.
      • Either "${ { ... } }" (single-brace, expression-of-object-literal) or "${{ ... }}" (double-brace, object-literal-expression form) is valid — both evaluate to the same JS object. Pick one and stay consistent within a workflow.
    • For single-value responses (returning one variable or one expression), the simple form is fine: "response": "${$context.outputs.Javascript_1}" or "response": "${'done'}".
    • On-disk is authoritative. Even with the single-expression workaround, every StudioWeb designer save can re-trigger normalization passes that may corrupt the Response shape. After any designer roundtrip, re-validate with uip api-workflow run --no-auth and re-apply the workaround if needed. Until SW-28452 ships a fix, treat the file on disk as truth, not what the designer renders.
  16. Connector activities (Http kind AND IntSvc kind) are produced by uip api-workflow registry resolve + stub, gated by a connection ping for vendor activities. The stub command combines TypeCache lookup (GUID + InstanceParameters) with Integration Service Elements metadata (full path, request/response fields, multipart signal) and emits the activity object — with unifiedTypesCompatible: true and savedJitInputFieldId so StudioWeb renders the unified activity card (the one on the current activity picker). Discovery flow:
    1. Resolveuip api-workflow registry resolve "<keyword>" --output json. Returns candidates with uiPathActivityTypeId, displayName, connectorKey, objectName, httpMethod, activityType. Pick the right GUID by displayName / connectorKey (filter by connector name when the operation name is ambiguous — e.g. “gmail” vs “outlook” for “send email”).
    2. (IntSvc kind / vendor only) Verify a working connection. uip is connections list <connector-key> --output json → pick Id. uip is connections ping <uuid> is REQUIRED — listing-state and runtime-state diverge (orphaned upstream element, expired OAuth, wrong tenant). On ConnectionNotEnabled / 404 "Connection […] is invalid or you do not have access to it", run the unfiltered fallback uip is connections list --output json and search for a different Id whose ConnectorKey matches. Re-ping. Abort only when no UUID for that connector pings cleanly. NEVER author against a connection that hasn’t pinged — it WILL 401 in cloud.
    3. Stubuip api-workflow registry stub <activity-type-id> [--connection-id <uuid>] [--inputs '<json>'] --output json. Returns: Activity (single-key object — drop directly into the root sequence’s do array), Kind ("Http" or "IntSvc"), SlotKey (the activity key in the do array, e.g. GetNewestEmail_1), ExportBucketKey (what export.as writes AND what $context.outputs.<X> reads as, e.g. getNewestEmail_1, http_request_1, ListEmails_1), ResponseFields (downstream binding targets), Warnings. The CLI builds metadata.configuration, picks Http kind vs IntSvc kind from connectorKey, computes both keys, hub-prefixes the endpoint when IS Elements requires it, and declares multipartParameters for multipart operations. NEVER hand-author metadata.configuration, invent a uiPathActivityTypeId, or reconstruct either key from objectName — use what the stub returned. --inputs values are passed through verbatim — pass bare strings for literals, ${...} for references (see field-shape rule (b) below).
    4. Required-field cross-check — MANDATORY after every stub call. The stub drops required: true request fields. Run uip is resources describe <connector-key> <object-name> --operation <op> --connection-id <uuid> --output json (or parse metadata.configuration.optionalConfiguration.fieldsContainer.inputFields from the stub’s own output) and confirm every required: true field appears in the corresponding queryParameters / pathParameters / bodyParameters block. Verified case: Outlook getNewestEmail requires parentFolderId (fieldLocation: "query"); the stub returns queryParameters: {} unless that field was passed via --inputs. Re-stub with --inputs '{"<field>":"<value>"}' or hand-edit the activity. Skipping this check is the second-most-common cause of “renders fine, runs fine, fails in cloud” connector activities (see troubleshooting.md).
    5. Kind-specific finalization.
      • Http kind (HTTP Request, connectorKey === uipath-uipath-http): the stub leaves <REPLACE_WITH_TARGET_URL> in bodyParameters.url (unless passed via --inputs). Replace with the target URL (literal or ${$workflow.input.<name>}). connectionId: "ImplicitConnection" is fine for inline-credential calls. See the verified template at assets/templates/connector-call-example.json (a real stub-generated workflow that fetches catfacts).
      • IntSvc kind (vendor curated, UiPath.IntSvc): if --connection-id was omitted, the stub contains <REPLACE_WITH_VENDOR_CONNECTION_UUID> placeholders — replace before running. Output payload is wrapped: read $context.outputs.<ExportBucketKey>.content.<field>, NOT .<field>. NEVER use Http kind with a vendor connection UUID — that produces 401 “Invalid Element token” in cloud.
    6. NEVER ship a workflow with <REPLACE_WITH_*> placeholders in with.connectionId or with.connectionResourceId. StudioWeb’s properties panel renders the placeholder string as if it were a real connection name — the connection pill shows <REPLACE_WITH_VENDOR_C...> with a red error, and the workflow 401s in cloud. The placeholder is a sentinel for “you forgot to run stub with --connection-id,” not a value the user fills in later. If you don’t have a pinged UUID, STOP authoring and ask the user — do not emit the placeholder. Same for <REPLACE_WITH_TARGET_URL> in Http kind. The vendor template at assets/templates/vendor-curated-call-example.json keeps the placeholder ONLY because it is a template; generated workflows MUST have real values.
    7. (Solutions-mode + IntSvc kind only) Sync the connection into the Solution catalogue. When the project lives under a Solution/ tree, every vendor connection used by an IntSvc kind activity MUST also be declared as a Solution resource (Solution/resources/solution_folder/connection/<connector-key>/<name>.json) AND registered in the per-user debug overwrites (Solution/userProfile/<guid>/debug_overwrites.json). Without both, StudioWeb’s properties panel marks the activity with “to debug this resource, select a connection for it from the resource definition page”, and clicking the activity overwrites with.connectionId with null in Workflow.json (the runtime resolves from Workflow.json; the panel resolves from the Solution resource tree + debug overwrites, which is why “Run” works but the panel breaks). The two-step CLI flow: (a) uip api-workflow bindings sync --workflow <Workflow.json> — generates bindings_v2.json next to the workflow (the input StudioWeb normally produces in-memory on open). (b) uip solution resource refresh --solution-folder <path> — reads every project’s bindings_v2.json, uses @uipath/resource-builder-sdk to write both the catalogue file and the per-user debug overwrites via editOverwritesAsync. Idempotent and additive: run after every Workflow.json edit that adds/changes a connection. SKIP this step entirely for: (a) Http kind activities (call: "UiPath.Http", connectionId: "ImplicitConnection" — literal sentinel, not a real connection), (b) non-connector activities (Sequence/Assign/If/ForEach/TryCatch/Wait/Response — no connectionId at all), (c) standalone projects with a top-level project.json and no Solution/ wrapper. See connector-activity-discovery.md — Step 5.

    Field-shape rules — must hold across edits, not just at stub time. Violating any silently drops data on the first StudioWeb save:

    • (a) Flat dotted keys. Connector field names like message.toRecipients are literal keys, NOT paths. "message.toRecipients": "...", NOT { message: { toRecipients: "..." } }. Nested objects are dropped.
    • (b) Literals are BARE in connector params. Plain "[email protected]", NOT "${'[email protected]'}". The Assign / Response wrap rule (rule 5) is inverted here — ${'...'} reads as a (broken) expression and the field clears on save. References (${$context.variables.X}) stay wrapped because they’re real expressions.
    • (c) Use Data.SlotKey and Data.ExportBucketKey from the stub verbatim. Connector activities are the only activity type where the slot key (in the do array) and the export-bucket key (what $context.outputs.<X> reads as) can differ. The stub computes both; never derive either by hand from objectName. The two are sometimes identical (e.g. Outlook ListEmails → both ListEmails_1) and sometimes different (e.g. HTTP → HttpRequest_1 slot vs http_request_1 bucket).
    • (d) Endpoint may include a hub prefix. Outlook send-mail-v2/hubs/productivity/send-mail-v2. The stub fills the full path from IS Elements; don’t truncate.

    See references/connector-activity-discovery.md for the full flow, sample stub output, and worked examples (Http kind HTTP Request, IntSvc kind GetNewestEmail, IntSvc kind multipart SendEmail).

  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.
  19. Build & publish goes through the solution packager. API workflows pack via uip solution pack <solutionDir> <outputDir> and publish via uip solution publish <package.zip>. There is no uip api-workflow build or uip api-workflow publish command. Project type must be "Api" in the solution .uipx.
  20. uip api-workflow validate <Workflow.json> is the autonomous closure step for every authoring or edit cycle. Run it as the LAST command before asking the user anything about runtime. It’s offline (no auth, no network, no side effects): JSON Schema + semantic checks on the static file. Output codes:
    • Result: "Success", Code: "ApiwfValidate", Data.Status: "Valid" (exit 0) — possibly with Data.Warnings. Proceed to rule 21 (ask the user whether to run).
    • Result: "Failure" (exit 1) — do NOT bother the user. Read Instructions, locate the offending activity by its JSON path (e.g. /do/0/Sequence_1/do/2/Mystery_1/metadata/activityType), edit Workflow.json to fix it, then re-validate. Loop until pass.

    Reading the error list. AJV schema errors from oneOf branches produce duplicate “Missing required property” noise (each unmatched variant lists all its required fields). Focus on the semantic-tail errors — the ones with prose messages like Unknown activityType 'X', must contain a 'do' with inner 'switch', is missing 'metadata.configuration', Variable must have a non-empty 'type'. Those uniquely identify the root cause. Fix one root cause, re-validate, repeat — don’t chase the schema-level fanout one by one.

    What validate catches: malformed JSON; unknown activityType values (see VALID_ACTIVITY_TYPES list in the validate source); per-activity required keys (If → do + inner switch, Sequence → do, Assign → set, ForEach → for + do, DoWhile → for + doWhile, Connector → call + metadata.configuration + essentialConfiguration, Response → response, etc.); missing metadata.activityType/displayName (warnings); bad evaluate.language/evaluate.mode; duplicate or empty-named workflow variables; empty task lists. What it does NOT catch: wrong selectedResourceId, broken connector connection IDs, runtime expression errors (ReferenceError: x is not defined), unwrapped string literals (rule 5), multi-key Assign.set (rule 6) — those still need runtime validation via uip api-workflow run once the user consents.

  21. Never run uip api-workflow run without an explicit user “yes.” Validation (rule 20) is autonomous; running is not. Once validate passes, ask the user: (a) run now or skip, (b) if running, with --no-auth (fast, structure-only — IntSvc kind vendor calls fail) or with auth (real Integration Service calls — vendor side effects WILL happen: emails sent, tickets created, files uploaded). Suggest a default based on workflow content (--no-auth for control-flow-only + Http kind ImplicitConnection; with-auth for any IntSvc kind vendor activity), but wait for the user’s answer. Never invoke uip api-workflow run with auth on speculation — once a vendor call goes out, it can’t be unsent.

Workflow Phases

Phase 0: Discovery

Before touching anything, understand what exists.

For edit requests:

  1. Read the existing workflow file with Read
  2. Identify activity keys already in use (avoid collisions)
  3. Identify variables, inputs, outputs already declared
  4. Identify export patterns in use (stay consistent)

For create requests:

  1. Read assets/templates/api-workflow-template.json for the empty skeleton
  2. Read a closer example based on need:
  3. For nested patterns specifically, read references/control-flow-patterns.md — pattern catalog for If-in-If, ForEach-with-If, TryCatch-around-loop, conditional Break, etc.

Phase 1: Plan

Decide which activities to use and in what order.

User wants Activity type Key points
Set/transform variables Assign Sets $context.variables; uses variables export pattern
Run custom logic JavaScript (JsInvoke) Inline JS; access context via $context / $workflow / $input globals (NOT arguments[0])
Branch on condition (2-way) If #Wrapper + #Then + #Else structure required
Branch on condition (3+ way) Chain of Ifs Each #Else holds the next If — see control-flow-patterns.md
Iterate over collection ForEach for.each/for.in/for.at; needs #Body
Repeat until condition DoWhile for.in: "${ [1] }"; needs #Body; must update condition variable
Handle errors (whole batch) TryCatch around loop One bad item kills the batch — see control-flow-patterns.md
Handle errors (skip & continue) TryCatch inside body One bad item skipped, loop continues — see control-flow-patterns.md
Return result and end Response then: "end"; markJobAsFailed sibling of response
Pause execution Wait wait.seconds/minutes/milliseconds
Exit loop early Break (in If) Wrap Break in an If — there’s no “break when” condition on Break itself. break: "true" (string!), then: "exit", set: "${$input}"
Exit nested loops Flag variable + Break twice Set a flag in inner loop, check + Break in outer — see control-flow-patterns.md
Call an arbitrary REST API (catfacts, stock prices, weather, any public/internal HTTP endpoint) Unified HTTP Request (call: "UiPath.Http", Http kind) uip api-workflow registry resolve "http request" → pick the HTTP Request GUID → registry stub <guid> --inputs '{"method":"GET","url":"<TARGET>"}' --output json. The stub emits call: "UiPath.Http" with unifiedTypesCompatible: true so StudioWeb renders the unified HTTP card. NEVER use call: "http" (deprecated simple form — renders as block icon). See connector-activity-discovery.md and the verified template at assets/templates/connector-call-example.json.
Call a vendor service via its UiPath connection (Gmail, Outlook, GitHub, Slack, etc.) Vendor curated activity (call: "UiPath.IntSvc", IntSvc kind) uip is connections list/ping for the UUID → uip api-workflow registry resolve "<keyword>"registry stub <guid> --connection-id <uuid>. Stub returns IntSvc kind with the full hub-prefixed endpoint. Never use UiPath.Http with a vendor connection UUID. See connector-activity-discovery.md

Before generating, determine:

  1. Which activities are needed and in what order
  2. What unique keys to assign (check existing keys to avoid collision)
  3. What variables to declare (in document.metadata.variables.schema.document.properties)
  4. What inputs/outputs to declare (in input.schema / output.schema)

Phase 2: Generate or Edit

For each activity, read its reference section in references/task-types.md, copy the minimal JSON, fill in values.

For CREATE: copy from a template, then add user activities AFTER WorkflowStart inside the root sequence (literally Sequence_1.do in the template skeleton).

For EDIT: read the file first, identify the exact insertion / replacement point, use Edit with sufficient context for unique matching.

Workflow skeleton:

{
  "document": { "dsl": "1.0.0", "name": "...", "version": "0.0.1", "namespace": "default", "metadata": { "variables": { "schema": { "format": "json", "document": { "type": "object", "properties": {...}, "title": "Variables" } } } } },
  "input":  { "schema": { "format": "json", "document": { "type": "object", "properties": {...}, "title": "Inputs" } } },
  "output": { "schema": { "format": "json", "document": { "type": "object", "properties": {...}, "title": "Outputs" } } },
  "do": [{ "Sequence_1": { "do": [ { "WorkflowStart": { /* system */ } }, /* user activities */ ], "metadata": {...} } }],
  "evaluate": { "mode": "strict", "language": "javascript" }
}

After authoring or editing, always validate first, autonomously (Critical Rule 20):

uip api-workflow validate ./my-workflow.json --output json

Once validate is green, ask the user before running anything (Critical Rule 21). The choice has real consequences:

Mode Flag What happens Use when
No-auth --no-auth Skips token loading. Structure / expressions / control flow validated. IntSvc kind vendor calls fail with a missing-token error. Workflow contains only Sequence / Assign / JS / If / ForEach / DoWhile / Break / TryCatch / Wait / Response, OR Http kind HTTP with connectionId: "ImplicitConnection". Default for most authoring iterations.
With auth (none) Uses the user’s uip login token. Real Integration Service calls — vendor APIs touched, side effects happen. Workflow contains a IntSvc kind vendor activity AND the user has confirmed it’s OK to send the real call (email actually sent, ticket actually created, file actually uploaded).

Phrase the question explicitly, with a recommended default. Examples:

Wait for the user’s reply. Then run the chosen command:

# User picked --no-auth:
uip api-workflow run ./my-workflow.json --no-auth --output json

# User confirmed running with auth (uip login required):
uip api-workflow run ./my-workflow.json --output json

If the user picks “skip,” tell them the exact command to run themselves and stop.

Read the failure output:

Fix in this order (higher categories often resolve lower ones):

  1. Structure — missing #Wrapper/#Body, duplicate keys, malformed JSON, missing WorkflowStart
  2. Expression — missing ${...}, unwrapped condition, undefined references
  3. Activity Config — wrong required fields, wrong export key casing, missing then: "end" on Response
  4. Logic — wrong behavior, infinite loops, unreachable code

See references/troubleshooting.md for the full pitfall catalog.

Phase 4: Package and Publish

Once the workflow runs locally, deploy via the solution packager.

Pack:

uip solution pack <solutionDir> <outputDir> \
  --name <PACKAGE_NAME> \
  --version 1.0.0 \
  --output json

The packager auto-detects Type: "Api" projects, validates structure, copies workflow files, generates operate.json + package-descriptor.json, and produces a .nupkg wrapped in a .zip.

Publish:

uip solution publish <outputDir>/<package>.zip \
  --tenant <TENANT_NAME> \
  --output json

Requires uip login.

Quick Start (CREATE from scratch)

# 1. Copy the empty template
cp ./.claude/plugins/uipath/skills/uipath-api-workflow/assets/templates/api-workflow-template.json \
   ./MyApiProject/main.json

# 2. Edit main.json to add user activities after WorkflowStart inside the root sequence

# 3. Validate (offline, autonomous — fix + re-validate until Status: Valid)
uip api-workflow validate ./MyApiProject/main.json --output json

# 4. Ask the user, then run (only on user "yes")
uip api-workflow run ./MyApiProject/main.json --no-auth --output json

# 5. Package
uip solution pack ./MySolution ./build --name MyApiSolution --version 1.0.0 --output json

# 6. Publish
uip login
uip solution publish ./build/MyApiSolution.zip --tenant MyTenant --output json

Reference Navigation

File Use when
references/workflow-file-format.md Authoring or editing the JSON skeleton: top-level keys, document.metadata.variables schema, input.schema/output.schema, WorkflowStart
references/http-retry-config.md Adding workflow-level HTTP retry policy (httpRetryConfig) — scope (GET-only), constant/linear/exponential backoff formulas, defaults, Retry-After handling, anti-patterns
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 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 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/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/cli-reference.md All uip commands — api-workflow run, solution pack, solution publish, solution new, login
references/troubleshooting.md Failed runs, structure/expression/loop/nesting/response/validation pitfalls, packaging errors, publish errors, debugging strategy

Templates

File Description
assets/templates/api-workflow-template.json Empty valid workflow with WorkflowStart and empty schemas — drop activities into the root sequence (Sequence_1.do in this template) after WorkflowStart
assets/templates/conditional-workflow-example.json If branching with TryCatch — input validation + classification + error fallback
assets/templates/loop-aggregation-example.json DoWhile + ForEach + Assign accumulation — pure-compute aggregation pattern
assets/templates/nested-control-flow-example.json Heavy nesting demo — TryCatch around DoWhile around If with conditional Break
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.
assets/templates/vendor-curated-call-example.json IntSvc kind — vendor curated activity (call: "UiPath.IntSvc") using Outlook GetNewestEmail as exemplar. The <REPLACE_WITH_VENDOR_CONNECTION_UUID> placeholder is a sentinel — replace it with a pinged UUID from uip is connections list/ping before writing the workflow to disk. StudioWeb renders the literal placeholder as a broken connection if it survives. See rule 16 step 6.
assets/templates/solution-connection-resource-template.json Solution connection resource — declares a IntSvc kind connection as a Solution resource. Write to Solution/resources/solution_folder/connection/<connector-key>/<connection-name>.json. Required for Solutions-mode projects; without it the StudioWeb properties panel flags the activity as having an invalid connection.

Anti-patterns

Infinite Loop Prevention

If a CLI command fails with the same error 2+ times, do NOT retry it. Investigate the root cause:

Maximum 3 attempts for any single operation. After 3 failures, stop and report what was tried.

Skill frontmatter

allowed-tools: Bash, Read, Write, Edit, Glob, Grep