# Connector Trigger Nodes — Implementation

How to configure connector trigger nodes: connection binding, enriched metadata, event parameter resolution, and trigger-specific `node configure` fields. This replaces the IS activity workflow (Steps 1-6 in [connector/impl.md](../connector/impl.md)) — trigger nodes have different metadata and configuration.

> **This plugin covers two node families that share the same configuration mechanism:**
>
> - **Connector triggers** (`uipath.connector.trigger.<key>.<trigger>`) — start node; the event starts the flow. The default subject of every step below.
> - **Wait for events** (`uipath.connector.event.<key>.<event>`) — a mid-flow event node; the flow pauses and waits for the event before continuing. Same connection binding, enriched metadata, event parameter resolution, filter trees, and `node configure` fields. Only the placement and node lifecycle differ. See [Wait for events](#wait-for-events-uipathconnectoreventkeyevent) for the deltas, then apply Steps 1-6 below unchanged.

## Configuration Workflow

Follow these steps for every IS trigger node.

### Step 1 — Fetch a preliminary connection and query trigger objects

Extract the connector key from the node type (`uipath.connector.trigger.<connector-key>.<trigger-name>`) and the operation name from the `registry get` response (`model.context[].operation`).

**1a. Get any enabled connection** — needed as `--connection-id` for `triggers objects` below.

Discovery call is **always**:

```bash
uip is connections list "<connector-key>" --all-folders --output json
```

`--all-folders` is mandatory. Without it the CLI returns the active folder only and hides connections in other folders the user can see. Plain `uip is connections list "<connector-key>"` is forbidden for discovery.

> **MUST READ before any `uip is connections ...` call:** [/uipath:uipath-platform — connections.md](../../../../../uipath-platform/references/integration-service/connections.md). Single source of truth for selection rules, BYOA filtering, empty-result recovery, ping verification.

Pick any enabled connection (prefer `IsDefault: Yes`) and capture its `Id` for Step 1b.

**1b. Query trigger objects** — **mandatory** for every trigger node. Do NOT skip:

```bash
uip is triggers objects "<connector-key>" "<OPERATION>" \
  --connection-id "<id>" --output json
```

Save the response. It carries three pieces per event object that drive later steps:

| Field | Drives | Meaning |
|------|--------|---------|
| `byoaConnection` | Step 1c | If `true`, only BYOA connections are valid for this event |
| `isWebhookUrlVisible` | Step 6b | If `true`, retrieve and present the webhook URL |
| **`parameters[]`** | Steps 3 & 4 | Object-, query-, and path-scoped input parameters (e.g. shared mailbox, repo, owner). **NOT the complete event-parameter set** — merge with `EventParameters` from `triggers describe` (Step 1b-2). The full input set is the **union** of both; a field is required if either source marks it `required`. |

It may also include `design.textBlocks` with connector-specific user-facing instructions (e.g., "Add this URL to your Slack app's Event Subscriptions"). Surface that text verbatim when applicable — do not invent service-specific guidance.

> `byoaConnection` and `isWebhookUrlVisible` are per event object, not per connector — some connectors require BYOA for some events but not others. `parameters[]` is per event object too: each operation has its own input fields.

#### Reading `parameters[]`

Each entry is one input field the user supplies when configuring the trigger (e.g., GitHub `repo`, Outlook `parentFolderId`, Slack `channelId`). For each:

| Key | Use |
|---|---|
| `name` | Key inside the `--detail` bucket selected by `type` — see below |
| `displayName` | Label when prompting the user for a missing value |
| `dataType` | Value type (`string`, `number`, `boolean`, …) |
| `required` | Drives Step 4's required-field check |
| `reference` | Present → resolve to an ID in Step 3 (`uip is resources run list`) before configure |
| `description` | Field hint — surface verbatim when prompting |
| `design.position` | `"primary"` indicates a top-level input field; other positions are layout hints — safe to ignore for configure |
| `type` | Bucket selector in Step 6's `--detail`: `"query"` → `queryParameters`, `"path"` → `pathParameters`, else → `eventParameters` |

> **Source of truth — the UNION of two calls, never one alone.** The complete event-input set = `triggers objects → parameters[]` (object/query/path scope) **∪** `triggers describe → EventParameters` (event-config scope). Configure every field that **either** marks `required: true`. `triggers describe → FilterFields` feeds the `filter` tree; `OutputFields` / `outputResponseDefinition` feed downstream `$vars`. `flow registry get`'s `eventParameters.fields` mirrors `describe` and is the offline fallback. A required field can appear in `describe → EventParameters` while absent from `parameters[]`, so **both calls are mandatory and you must merge them**. Full field semantics: [/uipath:uipath-platform — triggers.md — `parameters[]`](../../../../../uipath-platform/references/integration-service/triggers.md#parameters--event-parameter-input-fields).

**1b-2. Resolve the object name and fetch field metadata** — **mandatory** for every trigger node.

> **MUST READ before this step:** [/uipath:uipath-platform — triggers.md](../../../../../uipath-platform/references/integration-service/triggers.md). Single source of truth for fetching trigger parameters and fields — object name resolution, describe call, source-of-truth contract.

1. Resolve `<objectName>` from the Step 1b `triggers objects` response per [/uipath:uipath-platform — triggers.md — Object Name Resolution](../../../../../uipath-platform/references/integration-service/triggers.md#object-name-resolution). If the object name is not recognizable from the user's prompt, **ask the user** — present candidates by `displayName`. Do NOT guess.
2. Fetch field metadata:

```bash
uip is triggers describe "<connector-key>" "<OPERATION>" "<objectName>" \
  --connection-id "<id>" --output json
```

> **Source of truth for node configuration — merge both responses.** Event-parameter inputs = the **union** of `triggers objects → parameters[]` (Step 1b) and `triggers describe → EventParameters` (this step). `triggers describe` also returns `FilterFields` (→ `filter` tree) and `OutputFields` (→ downstream `$vars`). Never treat `triggers describe` as "only field metadata" — its `EventParameters` are first-class event parameters and frequently carry required fields that `parameters[]` omits. Do NOT skip this call, and do NOT invent parameters or fields.

### Step 1c — Select the final connection

**If `byoaConnection: true`** — the Step 1a connection is not usable. Follow the BYOA selection workflow in [/uipath:uipath-platform — connections.md — For BYOA Connections](../../../../../uipath-platform/references/integration-service/connections.md#for-byoa-connections-webhook-triggers) (filter with `--byoa`, `--refresh` retry, stop-and-ask if none exist).

**If `byoaConnection: false`** — use the Step 1a connection. Verify health:

```bash
uip is connections ping "<connection-id>" --output json
```

### Step 2 — Get enriched trigger metadata

`--connection-id` is **required** for trigger nodes. Without it, the command fails.

```bash
uip maestro flow registry get <trigger-node-type> --connection-id <connection-id> --output json
```

The response contains three trigger-specific sections:

**`eventParameters.fields`** — non-authoritative. Often empty (see Step 1b). Iterate [`parameters[]`](#reading-parameters) from Step 1b in Steps 3 and 4 — do not iterate this block.

Shape only — do not iterate:

```json
{
  "eventParameters": {
    "fields": [
      {
        "name": "parentFolderId",
        "displayName": "Email folder",
        "type": "string",
        "required": true,
        "reference": {
          "objectName": "MailFolder",
          "lookupValue": "id",
          "lookupNames": ["displayName"],
          "path": "/MailFolders"
        }
      }
    ]
  }
}
```

**`filterFields`** — fields used to narrow *which* events fire the trigger (e.g., only emails from a specific sender). These are optional filter criteria and are passed as a structured tree under the `filter` key of `--detail` (see [Filter Trees](#filter-trees)).

```json
{
  "filterFields": {
    "fields": [
      {
        "name": "fromAddress",
        "displayName": "From address",
        "type": "string",
        "required": false
      }
    ]
  }
}
```

**`outputResponseDefinition`** — the event payload schema (all fields the trigger outputs when it fires). **Save this** — you need it in Step 4b to know the exact field paths for downstream `$vars` expressions (e.g., `$vars.{nodeId}.output.text`, `$vars.{nodeId}.output.channel`). Do not guess output field names.

**`eventMode`** — `"webhooks"` or `"polling"`.

The response (which becomes the `definitions[]` entry verbatim) includes `model.context` with:
- `connectorKey` — the connector identifier
- `operation` — the event operation name (e.g., `"EMAIL_RECEIVED"`, `"ISSUE_CREATED"`)
- `objectName` — the IS object (e.g., `"Message"`, `"Issue"`)

These live in the **definition**, not on the node instance. The instance carries only `inputs` (event filter fields) and `outputs`.

### Step 3 — Resolve reference fields in event parameters

Iterate the **union** of `parameters[]` (Step 1b) and `EventParameters` (Step 1b-2). For each entry with a `reference` key, run an ID lookup — same mechanism as IS activity nodes. A referenced field can live in `describe → EventParameters` while absent from `parameters[]` — resolving only `parameters[]` misses it.

> **Resolve every reference field freshly, against the current `--connection-id`, immediately before `node configure` (Step 6)** — even if you think you already know the ID from a previous flow. Reference IDs are connection-scoped and reused values fault silently at runtime. See [Reference IDs Are Connection-Scoped (CRITICAL)](../../../../../uipath-platform/references/integration-service/reference-resolution.md#reference-ids-are-connection-scoped-critical) for the full mechanism and failure mode, and the top-level Anti-Patterns in [SKILL.md](../../../../SKILL.md).

```bash
# Example: resolve Outlook mail folder "Inbox" to its ID
uip is resources run list "<connector-key>" "<reference.objectName>" \
  --connection-id "<id>" --output json
```

The `<id>` in `--connection-id "<id>"` MUST be the connection bound to **this** flow (the final connection from Step 1c), not any other connection you've used in another flow. Use the resolved IDs — from this very `run list` call — in the trigger's event parameter configuration.

> **If the `run list` call fails, you have no ID.** Do not fall back to the display name, a vendor well-known alias, or an ID from memory — each validates clean and faults at runtime. Stop and report the failed resolve. See [reference-resolution.md — When the Lookup Call Fails](../../../../../uipath-platform/references/integration-service/reference-resolution.md#when-the-lookup-call-fails-critical).

> **Paginate when looking up by name.** Use `Data.Pagination.HasMore` / `NextPageToken` with `--query "nextPage=<token>"`. Short-circuit on match. Do NOT conclude "not found" until `HasMore` is `"false"`. See [resources.md#pagination](../../../../../uipath-platform/references/integration-service/resources.md#pagination).

**Read [/uipath:uipath-platform — Integration Service — resources.md](../../../../../uipath-platform/references/integration-service/resources.md) for the full reference-resolution workflow** (pagination, describe failures, fallbacks).

### Step 4 — Validate required event parameters

Check every entry in the **union** of `parameters[]` (Step 1b) and `EventParameters` (Step 1b-2). Treat a field as required if **either** source marks `required: true`. All required event parameters must have values before building the flow.

1. Collect all required entries from `parameters[]` **and** `describe → EventParameters` (dedupe by `name`); required = required in either
2. For each, check if the user's prompt provides a value
3. If any required field is missing, **ask the user** — list the missing fields with their `displayName` (and `description` if useful). Free-form input is appropriate when the value space is open-ended; when a finite set of sensible values exists, present them as options per the dropdown question rule in [SKILL.md](../../../../SKILL.md).
4. Only proceed after all required event parameters are resolved

> Step 6 buckets each resolved value into `eventParameters`, `queryParameters`, or `pathParameters` based on the entry's `type` — see Step 6's `--detail` table.

### Step 4b — Map trigger output fields for downstream nodes

Before wiring downstream nodes, check `outputResponseDefinition` from Step 2 to know the exact field names available in `$vars.{triggerId}.output`. Do NOT guess field names — different triggers output different schemas.

Each trigger type has a different output schema — field names like `.text`, `.subject`, or `.body.content` vary by connector. Use the actual field names from `outputResponseDefinition` when writing expressions in downstream nodes.

### Step 5 — Replace the manual trigger with the connector trigger node

Follow the [CLI: Replace manual trigger with connector trigger](../../editing-operations-cli.md#replace-manual-trigger-with-connector-trigger) procedure. The CLI handles edge cleanup, orphaned definition removal, and `variables.nodes` regeneration automatically. Note the generated node ID from the `node add` response — you need it for Step 6.

### Step 6 — Configure the trigger node

> **MUST READ before `node configure`:** [/uipath:uipath-platform — triggers.md](../../../../../uipath-platform/references/integration-service/triggers.md). The parameters fetched there (`triggers objects` → `parameters[]`, Step 1b) and fields (`triggers describe`, Step 1b-2) are the source of truth for everything in `--detail`. If you have not run both calls, go back to Step 1b.

**Read the `--detail` field table below before calling `node configure`.** The fields and types are strict — unknown keys or wrong types cause validation errors. Do not guess field names from other node types (e.g., activity nodes use `method`/`endpoint`/`bodyParameters`; triggers use `eventMode`/`eventParameters`/`filter`, plus `objectName` for generic triggers).

Use `node configure` with trigger-specific `--detail` fields:

```bash
uip maestro flow node configure <PROJECT>.flow <triggerId> --output json --detail '{
  "connectionId": "<CONNECTION_ID>",
  "folderKey": "<FOLDER_KEY>",
  "eventMode": "<EVENT_MODE>",
  "objectName": "<OBJECT_NAME>",
  "eventParameters": { "<paramName>": "<RESOLVED_VALUE>" },
  "filter": {
    "groupOperator": 0,
    "index": 0,
    "filters": [
      {
        "id": "subject",
        "operator": "Contains",
        "value": { "value": "urgent", "rawString": "\"urgent\"", "isLiteral": true }
      }
    ]
  }
}'
```

**`--detail` fields for triggers:**

| Field | Required | Description |
|---|---|---|
| `connectionId` | Yes | Connection UUID from Step 1c (the final connection — BYOA if required) |
| `folderKey` | Yes | Orchestrator folder key for the connection |
| `eventMode` | Yes | `"webhooks"` or `"polling"` — from `registry get` response |
| `objectName` | Generic triggers only | The IS object the event subscribes to (e.g. the Data Fabric entity name) — the object name resolved in Step 1b-2. **Required for generic triggers** (`activityType: "GenericTrigger"` in the definition's `connectorDetail.configuration` — one manifest templated across every object, so its `objectName` context entry ships empty). **Omit for curated triggers** (`activityType: "CuratedTrigger"`) — the manifest's baked-in objectName always wins. |
| `eventParameters`, `queryParameters`, `pathParameters` | No | JSON objects of resolved values from Steps 3-4. Bucket each `parameters[]` entry by its `type`: `"query"` → `queryParameters` (e.g. GitHub `{"repo":"cli"}`, Outlook `{"sharedMailboxAddress":"..."}`), `"path"` → `pathParameters` (e.g. GitHub `{"owner":"uipath"}`), otherwise → `eventParameters`. Within each bucket, the key is `parameters[].name`. Each must be a JSON object. |
| `filter` | No | Structured filter tree — see [Filter Trees](#filter-trees) below. Omit to trigger on all events |

The CLI computes the runtime JMESPath `filterExpression` from `filter` automatically and persists both into the workflow so Studio Web can re-open the trigger without losing the filter configuration. **Do not pass `filterExpression` directly — the validator rejects it.**
The command populates `inputs.detail` (including the internal `configuration` blob with the `filter` tree and combined `filterExpression`) and creates workflow-level connection bindings.

> **Shell quoting tip:** For complex `--detail` JSON, write it to a temp file: `uip maestro flow node configure <file> <nodeId> --detail "$(cat /tmp/detail.json)" --output json`

### Step 6b — Retrieve and display webhook URL (webhooks only)

Applies when `eventMode` is `"webhooks"`, regardless of `byoaConnection`.

> **Guard:** If the trigger object's `isWebhookUrlVisible` is `false` (from Step 1b), skip this step — the connector manages webhook registration automatically and does not expose a URL. If you do not have `triggers objects` output, you skipped Step 1b — go back.

Follow [/uipath:uipath-platform — triggers.md — Webhook URL Retrieval](../../../../../uipath-platform/references/integration-service/triggers.md#webhook-url-retrieval) for the exact commands (`uip is connections list` → `ElementInstanceId` → `uip is webhooks config`).

**Presenting the URL:** If the Step 1b `triggers objects` response included `design.textBlocks`, use that text verbatim (substituting `{webhookUrl}`) — it carries connector-specific registration instructions. Otherwise, use a generic message instructing the user to register the URL in their external service's app settings (e.g., Slack Event Subscriptions, Salesforce Outbound Messages). The trigger will not fire until the URL is registered and verified.

**On failure:** if `ElementInstanceId` is empty, the connection is the wrong type — verify the `byoaConnection` flag from Step 1b and switch to a BYOA connection if required. If `webhooks config` itself fails, ping the connection (`uip is connections ping`) and re-authenticate via `uip is connections edit` if unhealthy.

---

## Wait for events (`uipath.connector.event.<key>.<event>`)

A **Wait for events** node waits for an external event **mid-flow** instead of starting the flow ("wait for the approval reply", "wait until the Jira issue is Done"). Same connector event as a trigger — identical `eventParameters`, `filterFields`, `outputResponseDefinition`, `eventMode`, connection binding, reference resolution, filter trees. Apply Steps 1-6 above unchanged; only the deltas below differ.

| Aspect | Connector **trigger** | **Wait for events** (mid-flow) |
|---|---|---|
| Node type | `uipath.connector.trigger.<key>.<trigger>` | `uipath.connector.event.<key>.<event>` |
| Position | Start node — replaces the manual trigger | Mid-flow — has an `input` port |
| Add | Step 5: [replace the manual trigger](../../editing-operations-cli.md#replace-manual-trigger-with-connector-trigger) | `node add` like an action node; do **NOT** remove the start node |
| Ports | in: — · out: `output` | in: `input` · out: `output`, `error` |
| Start trigger | Is the flow's trigger | Not a trigger — flow still needs its own start trigger |
| Discovery search | `... search "<svc>" trigger` | `... search "<svc>" event` |
| Config (Steps 1-4, 6) | Identical | Identical |

Add and wire:

```bash
uip maestro flow node add <PROJECT>.flow uipath.connector.event.<key>.<event> \
  --label "<LABEL>" --position 400,144 --output json
```

Wire an incoming edge into `input` and an outgoing edge from `output` per [editing-operations-json.md — Insert a node between two existing nodes](../../editing-operations-json.md#insert-a-node-between-two-existing-nodes). Downstream reads `$vars.{eventNodeId}.output` (payload) and `$vars.{eventNodeId}.error`. Wire `error` only when the requirements say what an event-wait failure should do — otherwise leave it unwired and let the failure fault the flow ([file-format.md — Default: off](../../../shared/file-format.md#default-off--enable-only-for-a-failure-the-flow-actually-handles)).

> **The flow still needs a separate start trigger.** A mid-flow event node does **not** start the flow. Do not remove `core.trigger.manual`/`scheduled` or the connector trigger that begins the flow.

---

## Filter Trees

Filters are authored as a **structured tree**, not a string expression. The CLI compiles the tree into a JMESPath `filterExpression` using the same logic Studio Web does, and writes both forms into the flow so the trigger round-trips cleanly when re-opened in SW.

### Tree shape

```jsonc
{
  "groupOperator": 0,             // 0 = And, 1 = Or — combines sibling filters/groups
  "index": 0,                     // ordering index within parent (root is 0)
  "filters": [                    // leaf conditions at this level
    {
      "id": "<fieldName>",        // from filterFields.fields[].name
      "operator": "<Operator>",   // see operator table below
      "value": {
        "value": <typed value>,   // string / number / boolean / ISO-8601 date-time
        "rawString": "\"...\"",   // verbatim user-entered text (with quotes for strings)
        "isLiteral": true         // false for a dynamic operand; eventParameters stay literal
      }
    }
  ],
  "groups": []                    // optional: nested subgroups (same shape as root)
}
```

A no-op filter — used when the user wants all events to fire the trigger — is `null` or `{"groupOperator": null, "index": 0, "filters": []}`. Prefer **omitting** the `filter` field entirely.

### Supported operators

| Operator | Meaning | Typical field types |
|---|---|---|
| `Equals` / `NotEquals` | Exact (in)equality | string, number, boolean |
| `LessThan` / `LessThanOrEqual` / `GreaterThan` / `GreaterThanOrEqual` | Numeric comparison | number, integer |
| `Contains` / `NotContains` | Substring match | string |
| `StartsWith` / `NotStartsWith` / `EndsWith` / `NotEndsWith` | Prefix / suffix match | string |
| `IsEmpty` / `IsNotEmpty` | Value is / is not empty string | string (no `value` needed) |
| `IsNull` / `IsNotNull` | Value is / is not null | any type (no `value` needed) |
| `Is` / `IsNot` | Boolean is true / false | boolean (no `value` needed) |
| `In` / `NotIn` / `IsOneOf` / `IsNotOneOf` | Membership — pass comma-separated values in `value.value` | string, number |
| `Before` / `BeforeOrEqual` / `After` / `AfterOrEqual` / `DateTimeEquals` / `DateTimeNotEqual` | Date-time comparison (ISO-8601 strings) | date-time |

### Examples

**Emails containing "urgent" in the subject:**

```json
{
  "groupOperator": 0,
  "index": 0,
  "filters": [
    { "id": "subject", "operator": "Contains",
      "value": { "value": "urgent", "rawString": "\"urgent\"", "isLiteral": true } }
  ]
}
```

**Emails from a specific sender AND subject contains "good day":**

```json
{
  "groupOperator": 0,
  "index": 0,
  "filters": [
    { "id": "from", "operator": "Equals",
      "value": { "value": "boss@example.com", "rawString": "\"boss@example.com\"", "isLiteral": true } },
    { "id": "subject", "operator": "Contains",
      "value": { "value": "good day", "rawString": "\"good day\"", "isLiteral": true } }
  ]
}
```

**Multiple senders (OR) nested inside an outer AND with a subject match:**

```json
{
  "groupOperator": 0,
  "index": 0,
  "filters": [
    { "id": "subject", "operator": "Contains",
      "value": { "value": "urgent", "rawString": "\"urgent\"", "isLiteral": true } }
  ],
  "groups": [
    {
      "groupOperator": 1,
      "index": 1,
      "filters": [
        { "id": "from", "operator": "Equals",
          "value": { "value": "a@ex.com", "rawString": "\"a@ex.com\"", "isLiteral": true } },
        { "id": "from", "operator": "Equals",
          "value": { "value": "b@ex.com", "rawString": "\"b@ex.com\"", "isLiteral": true } }
      ]
    }
  ]
}
```

### How to build a filter tree from `filterFields`

1. Run `flow registry get` with `--connection-id` (Step 2) and read the `filterFields.fields` array. Each entry has a `name` (use as the leaf `id`), a `type` (drives operator selection), and an optional `description`.

Then follow [/uipath:uipath-platform — triggers.md > Building Filter Trees from filterFields](../../../../../uipath-platform/references/integration-service/triggers.md#building-filter-trees-from-filterfields) for the rest of the procedure (operator selection, leaf composition, value wrapping), the mandatory-filter contract (connector-mandated values like Gmail folder go on `eventParameters`, never the freeform `filter` tree), and array-shaped field handling.

### What NOT to generate

| Invalid input | Why it fails | Valid replacement |
|---|---|---|
| `"filterExpression": "(contains(subject, 'x'))"` | Legacy format — the CLI now rejects `filterExpression` as an input field. It is *only* an output in the generated `.flow`. | Build a `filter` tree with a `Contains` leaf. |
| `"filter": "(subject == 'x')"` | `filter` must be an object, not a string. | Structured tree with `filters: [...]`. |
| `{ "id": "fields.subject", ... }` | `fields.` prefix — use the bare field name from `filterFields.fields[].name`. | `{ "id": "subject", ... }` |
| `{ "id": "subject", "operator": "contains", ... }` | Operator is case-sensitive — use PascalCase. | `"operator": "Contains"` |
| `{ "value": "urgent" }` on a leaf | Bare string — must be wrapped in the `WorkflowValue` object. | `{ "value": { "value": "urgent", "rawString": "\"urgent\"", "isLiteral": true } }` |
| `{ "isLiteral": false, "value": "${var}" }` on an `eventParameters` value | Filter-tree leaf operands may be dynamic (`isLiteral: false`, compiled to a `{var_…}` placeholder plus `filterVariables`), but `eventParameters` must be literals — a trigger is evaluated before any flow run exists. | Resolve the value first and pass it as a literal; use a dynamic operand only inside the `filter` tree. |
| Adding a freeform leaf for a connector-mandated field (e.g. Gmail folder, Slack channelId) | Mandatory filters derived from connector event metadata are emitted automatically by the CLI from `eventParameters` — duplicating them in the freeform tree double-applies the clause. | Set the value through `eventParameters` only; the CLI AND-joins the mandatory JMES clause into the top-level `inputs.detail.filterExpression` (matching SW's `combinedFilterExpression`). It is *not* persisted on `essentialConfiguration` — SW classifies it optional and rebuilds it from input field values on restore. |

---

## Bindings

Trigger nodes require more binding resources than activity nodes: `Connection` + `EventTrigger` + `Property` resources. **`node configure` and the packaging pipeline handle all of these automatically:**

- **Connection bindings** — created in the `.flow` file by `node configure` (Step 6)
- **EventTrigger + Property bindings** — generated into `bindings_v2.json` during `flow debug` or packaging from the trigger node's `inputs.detail`

You do **not** need to manually create or edit `bindings_v2.json` for trigger nodes.

---

## CLI Commands

```bash
# Discovery
uip maestro flow registry search trigger --output json               # find trigger node types
uip maestro flow registry pull --force                                # refresh registry (requires login)

# Enriched trigger metadata (--connection-id REQUIRED)
uip maestro flow registry get <trigger-node-type> --connection-id <connection-id> --output json

# Node lifecycle
uip maestro flow node remove <PROJECT>.flow start --output json       # remove manual trigger
uip maestro flow node add <PROJECT>.flow <trigger-node-type> --label "<LABEL>" --position 200,144 --output json
uip maestro flow node configure <PROJECT>.flow <nodeId> --detail '<TRIGGER_DETAIL_JSON>' --output json

# Trigger object metadata (MANDATORY — Steps 1b and 1b-2)
uip is triggers objects "<connector-key>" "<operation>" --connection-id "<id>" --output json   # Step 1b: objects + parameters[]
uip is triggers describe "<connector-key>" "<operation>" "<objectName>" --connection-id "<id>" --output json  # Step 1b-2: fields

# Connections — see /uipath:uipath-platform — connections.md for selection rules (Native, BYOA, --refresh)
uip is connections list "<connector-key>" --all-folders --output json         # discover connections (--all-folders is mandatory)
uip is connections list "<connector-key>" --byoa --all-folders --output json  # BYOA only (Step 1c)
uip is connections ping "<connection-id>" --output json               # verify health

# Reference resolution (same as IS activity)
uip is resources run list "<connector-key>" "<resource>" \
  --connection-id "<id>" --output json

# Webhook URL retrieval — see /uipath:uipath-platform — triggers.md (Step 6b, webhooks only)
uip is webhooks config "<connector-key>" \
  --connection-id "<connection-guid>" \
  --element-instance-id <number> --output json
```

---

## Testing Trigger Flows

`uip maestro flow debug` works with trigger-based flows. Debug does **not** wait for a live event — it **pulls the most recent matching event** from the connector's lookback window and executes immediately.

### How debug works for triggers

1. Debug calls the connector's `/events/debug` endpoint with `maxResults=5` and a `startDate` (default: 1 hour ago)
2. The connector returns up to 5 matching events from that window, sorted most-recent-first
3. The runtime uses `FilterMatches[0]` (the most recent match) as the trigger input
4. The flow executes immediately with that event data
5. If **no matching events** exist in the lookback window, debug fails with error code `3005` (TriggerNoMatches)

```bash
uip maestro flow debug . --output json
# → Fetches most recent matching event from the past ~1 hour
# → Flow executes immediately with that event data
```

### Polling vs webhook triggers in debug

| Trigger mode | Debug support | Behavior |
|---|---|---|
| `polling` | Supported | Pulls recent events via debug API, executes immediately |
| `webhooks` | **Not supported** | Webhook triggers cannot be tested in Studio debug mode — debug requires Orchestrator |

> **If the trigger uses `webhooks` event mode**, tell the user that debug is not available for webhook triggers. They must deploy to Orchestrator and test with a real webhook event.

### Key differences from manual-trigger debug

| Aspect | Manual trigger | Connector trigger (polling) |
|---|---|---|
| Execution start | Immediate with user-provided inputs | Immediate with most recent matching event |
| User action needed | Provide input values | Ensure a matching event exists in the past ~1 hour |
| Failure mode | Missing required inputs | No matching events in lookback window (error 3005) |

### Pre-debug checklist

1. **Verify the connection is healthy** — `uip is connections ping "<id>"`
2. **Confirm a matching event exists** — the user should have produced the event (e.g., sent an email, created a Jira issue) within the past hour
3. **Check event mode** — if `webhooks`, debug is not supported; inform the user

---

## Debug

### Common Errors

| Error | Cause | Fix |
|---|---|---|
| `Trigger nodes require --connection-id` | Ran `registry get` without `--connection-id` | Re-run with `--connection-id <id>` — required for all trigger nodes |
| `Invalid --detail` saying `objectName is required for the trigger` — or, on older CLIs, an opaque `Error configuring node` / `Response returned an error code` on every `--detail` shape | The trigger is **generic** (`activityType: "GenericTrigger"`, e.g. Data Fabric `record-created`): its manifest ships the `objectName` context entry without a value, and configure needs the object to fetch trigger metadata. Older CLIs rejected `objectName` as a `--detail` key and called the metadata API with an empty object name — hence the opaque server error. | Pass the object in `--detail.objectName` (resolve it per Step 1b-2). On an older CLI that rejects the key, `Edit` the trigger **definition's** `model.context` `objectName` entry to add `"value": "<object>"`, then re-run `node configure`. Curated triggers never need this — their manifest carries the objectName. |
| No trigger nodes in registry | Not authenticated or registry not pulled | Run `uip login` then `uip maestro flow registry pull --force` |
| Connection not found in bindings | `node configure` not run or connection expired | Re-run `node configure` with valid `connectionId` and `folderKey` |
| Event parameter missing at runtime | Required event parameter not configured (commonly one returned by `triggers describe` but absent from `triggers objects → parameters[]`) | Re-run **both** `uip is triggers objects` (Step 1b) **and** `uip is triggers describe` (Step 1b-2). Configure every field marked `required` in **either** `parameters[]` or `EventParameters` (resolving references) under the correct `--detail` bucket. |
| `filterExpression is derived from the filter tree and cannot be provided directly` | Passed `filterExpression` string instead of a `filter` tree | Build a structured `filter` tree — see [Filter Trees](#filter-trees) |
| Trigger fires on events the filter should exclude; `node configure` reported Success | A leaf `id` does not match any `filterFields.fields[].name` (matching is case-sensitive). The CLI drops the unmatched leaf from the compiled `filterExpression` instead of failing | Re-run `registry get --connection-id <id>`, compare every leaf `id` against `filterFields.fields[].name`, then re-run `node configure` with the exact names |
| Trigger not firing | Event parameters point to wrong resource (e.g., wrong folder ID) | Re-resolve reference fields with `uip is resources run list` |
| Trigger faults immediately with no visible error after a clean build | Event parameter uses a reference ID scoped to a **different** connection (common when copying from a prior flow in the same session — e.g., a `parentFolderId` for mailbox A pasted into a trigger bound to mailbox B's connection) | Re-run `uip is resources run list "<connector-key>" "<objectName>" --connection-id <CURRENT_CONNECTION_ID>`, extract the fresh ID, update `eventParameters` in `--detail`, re-run `node configure`, re-debug. See Step 3 and the top-level Anti-Pattern on reference-ID reuse in [SKILL.md](../../../../SKILL.md). |
| Definition's `model.context` missing operation | Definition not copied correctly, or node added before registry pull | Re-run `uip maestro flow registry pull --force`, then verify the `definitions[]` entry contains `model.context` with `connectorKey`/`operation`/`objectName` as returned by `registry get` |
| Trigger faults at runtime with webhook-related error | Standard (non-BYOA) connection used for a trigger that requires `byoaConnection: true` | Run `uip is triggers objects` (Step 1b) to check `byoaConnection` flag, then switch to a BYOA connection with `uip is connections list "<connector-key>" --byoa --output json`. If no BYOA connections exist, user must create one. |
| `connections list` returns empty but connections exist in the IS portal | CLI is using cached connection data that is stale | Retry with `--refresh` flag: `uip is connections list "<connector-key>" --refresh --output json` |
| `ElementInstanceId` is empty on the selected connection | Connection is not a BYOA connection, or connector does not support webhooks on this connection type | Verify the trigger requires BYOA (Step 1b `byoaConnection` flag). If `true`, switch to a BYOA connection. |

### Debug Tips

1. **Always verify the connection is healthy** before debugging trigger issues — run `uip is connections ping "<id>"`
2. **`flow validate` does NOT catch trigger-specific issues** — missing event parameters, wrong reference IDs, and expired connections are caught only at runtime
3. **Event parameters with `reference` objects** need resolved IDs, not display names — same as IS activity fields
4. **Filters are optional** — omit `filter` from `--detail` if the user wants all events to trigger the flow. Do not invent an "empty" expression.
5. **Bindings are auto-managed** — `node configure` creates flow-level bindings; `flow debug`/packaging generates `bindings_v2.json` from them
6. **Use `uip maestro flow node remove` to remove the manual trigger** — do NOT use `Edit` to delete the start node. The CLI automatically removes associated edges, orphaned definitions, and regenerates `variables.nodes`. Hand-editing skips these cleanup steps and can leave orphaned references.
7. **Check `outputResponseDefinition` before writing downstream expressions** — trigger output field names vary by connector. Do not assume field names like `.text` or `.subject` — verify from the enriched `registry get` response (Step 2)
8. **Validate filter field names against `filterFields` yourself** — only field names returned in `filterFields.fields[].name` are valid leaf `id`s in the filter tree, and matching is case-sensitive. The CLI no longer rejects an unknown field at configure time: it drops that leaf from the compiled `filterExpression` and reports Success. A guessed name therefore surfaces as a trigger that fires on events the filter should exclude, not as an error.
