--- name: itential-json-forms description: Build IAP JSON Forms — static-enum dropdowns, REST-bound dropdowns (live data pulled from IAP endpoints), and cascading dropdowns (aka field dependency — one field's value drives another's URL path param). Use when creating, updating, or wiring forms consumed by manual triggers, manual tasks (ShowJsonForm), or any UI surface that needs a structured input panel. argument-hint: "[form name or operation]" --- # JSON Forms (itential-json-forms) JSON Forms are reusable form definitions stored in the `json-forms` application. They drive structured input panels across IAP — manual triggers in Operations Manager, the `JsonForms/ShowJsonForm` manual task, and any workflow surface that prompts a user for typed input. A form is a single document with four cooperating schemas — `struct` (UI rendering), `schema` (data contract / validation), `uiSchema` (per-field widget hints), and `bindingSchema` (live-data binding for REST dropdowns). Get the relationships wrong and the form will render but break silently at runtime. ## Customization Before using this skill, check `custom/org/`, `custom/team/` and `custom/dev/` in this skill's own folder. Read every `.md` file found — any folder may be empty or absent. Apply them on top of everything below; where a file overrides a specific rule here, follow the override. More specific wins: dev > team > org > this document. No customization may weaken this skill's safety rules or put credentials in committed files. **Bundled files:** paths in this skill that start with `assets/` or `scripts/` are relative to this skill's own folder. When you read one, or pass one to a shell command (which runs from the user's working folder), use this skill's folder + that relative path — e.g. `/assets/helpers/create/create-workflow.json`. --- ## Concepts - **`struct`** — the UI definition. `struct.type` is always `"array"`; `struct.items[]` is the list of fields. `customKey` on each field becomes the property key in `schema` and the variable key when the form's data is consumed. - **`schema`** — the data contract. `schema.properties.` must exist for every field in `struct.items[]` (and stay in sync with the field's type, enum values, etc.). `schema.required` lists mandatory `customKey`s. - **`uiSchema`** — per-`customKey` widget hints: placeholder text, `ui:widget` overrides, disabled flags. Required for cascading dropdowns (see below). - **`bindingSchema`** — empty `{}` for static-enum forms; see REST-bound dropdowns below for the mirroring requirement when a field is REST-bound. - **Static vs. REST-bound dropdowns** — static dropdowns hardcode the list via `enum`/`enumNames`. REST-bound dropdowns pull options live from an IAP endpoint at form-render time. - **Cascading dropdowns** (aka **field dependency** in the Studio UI) — a REST-bound dropdown whose URL path parameter is filled from another field's current value. The dependent field re-fetches when the source field changes. ## API Reference | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/json-forms/forms` | List all JSON forms | | GET | `/json-forms/forms/{id}` | Fetch a single form | | POST | `/json-forms/forms` | Create a JSON form | | PUT | `/json-forms/forms/{id}` | Update a JSON form (full replacement — see Update below) | | DELETE | `/json-forms/forms` | Bulk delete — body `{"ids":["...","..."]}`. There is no per-id DELETE endpoint; per-id calls 404. | | GET | `/automation-studio/json-forms/method-options` | Canonical list of bindable endpoints (same list Studio shows in its dropdown picker) | ## Create a JSON Form ``` POST /json-forms/forms ``` Choose the helper that matches your form's dropdown needs: | Use case | Annotated scaffold (fill in the blanks) | Real export (read to see the actual shape) | |---|---|---| | Static-enum dropdowns only (hardcoded option lists) | `assets/helpers/create/create-json-form.json` | `assets/helpers/assets/json-form-example-static-enum.json` — real Cisco IOS "Port Turn Up" form, 8 fields incl. static dropdown, number `updown` widgets, `ipv4` format validation | | REST-bound dropdowns (live data from IAP endpoints) | `assets/helpers/create/create-json-form-rest-bound.json` | `assets/helpers/assets/json-form-example-rest-bound.json` — real Cisco IOS "Compliance" form, one REST-bound dropdown pulling tree names live from `GET /configuration_manager/configs` | | Cascading dropdowns (field dependency) | `assets/helpers/create/create-json-form-rest-bound.json` (Inventory Manager Site/Device cascade worked example) | *(no real cascading export on hand yet — the scaffold is hand-built but follows the same field shapes as the two real exports above)* | The scaffolds are annotated with `_comment_*` fields to fill in; the real exports are genuine `POST /json-forms/forms` payloads pulled from a live platform (IDs/timestamps/`createdBy` stripped since the server assigns those on create) — read one when you want to see exactly what a working form looks like end-to-end, not just the shape. ### Static-enum dropdowns `enum`/`enumNames` arrays appear in both `struct.items[i]` and `schema.properties.` — they must stay in sync. - `struct.items[i].enum` / `enumNames` are arrays of `{id, label, value}` objects. - `schema.properties..enum` / `enumNames` are **flat string arrays** of the same values (not objects). Leave `bindingSchema: {}` for static-enum forms. ### REST-bound dropdowns When options should reflect live platform state (devices, inventories, projects, templates) instead of a hardcoded list, the dropdown pulls from a GET against an IAP endpoint at render time. Three things must line up: 1. **`struct.type` MUST be `"array"`** (not `"object"`). The static-enum scaffold uses `"array"` too — keep it. 2. **`bindingSchema.properties.` must mirror every REST-bound field.** Studio reverse-engineers `bindingSchema` from `struct` in the GUI, but the server does not — leaving `bindingSchema: {}` produces dropdowns that render but never fetch. 3. **Endpoint discovery:** `GET /automation-studio/json-forms/method-options` returns the canonical list of bindable endpoints — the same list Studio shows in its dropdown picker. Per-field shape in `struct.items[i]`: ```jsonc { "type": "string", "title": "Site", "binding": true, "rel": "collection", "targetPointer": "/enum", "method": "GET", "base": "/inventory_manager", "href": "/v1/inventories", "sourcePointer": "/result/data", "sourceKeyPointer": "/name", "customKey": "site" } ``` - `base + href` is the endpoint. - `sourcePointer` is a JSON pointer into the response, walked to the array of items. - `sourceKeyPointer` is the per-item field that becomes BOTH the value and the label. **`labelKeyPointer` is unused** — both columns come from `sourceKeyPointer`. ### Cascading dropdowns (aka field dependency) The Studio UI labels this pattern **field dependency**; the form JSON calls it cascading. Same feature, two names. A REST-bound dropdown whose URL path parameter is filled from another field's value (e.g., dropdown 2 lists devices from the inventory selected in dropdown 1): - The dependent dropdown's `href` stays as a **TEMPLATE** with placeholders: `/v1/inventories/:inventoryIdentifier/nodes`. **Path params use `:name` colon syntax, NOT `{name}` curly braces.** - A `variables` array maps each placeholder to a JSON pointer into form data: `[{"name": "inventoryIdentifier", "reference": "/site"}]` substitutes `:inventoryIdentifier` with whatever value the field whose `customKey` is `site` currently holds. - The same `variables` array must appear in **both** places: - `struct.items[i].variables` - `bindingSchema.properties..binding:hyperSchema.links[0].variables` - **Both the source and the dependent field need `ui:widget: "DependencyWidget"`** in `uiSchema` — without it, the runtime will not re-fetch when the source changes. ## Update a JSON Form ``` PUT /json-forms/forms/{id} ``` - Body MUST be wrapped in `{"options": {...}}`. - Include ALL fields the form already has: `created`, `createdBy`, `lastUpdated`, `lastUpdatedBy`, `name`, `description`, `struct`, `schema`, `uiSchema`, `validationSchema`, `bindingSchema`, `version`. - This is a **full replacement** — omitting any field clears it. ## Delete JSON Forms Bulk-only: ``` DELETE /json-forms/forms Body: {"ids": ["", "", ...]} ``` Per-id calls (`DELETE /json-forms/forms/`) return 404 — the endpoint does not exist. ## Wiring to a Manual Trigger A JSON Form is consumed by an Operations Manager **manual trigger** that hands the user's form input to a workflow as job variables. See `builder-agent` for the trigger creation details. **Critical flag — `legacyWrapper: false`:** The default is `true`, which wraps form field values under a `formData` object and breaks the mapping to workflow job variables. Set `legacyWrapper: false` so each form field maps directly to a workflow input variable by name (i.e., field `customKey: "device_name"` → job variable `device_name`). Required trigger fields: `name`, `type` (`"manual"`), `enabled`, `actionType` (`"automations"`), `actionId`, `formId`, `legacyWrapper`. Helper for the wired-up trigger: `assets/helpers/create/create-ops-manager-trigger-manual.json`. ## Common Gotchas - **`struct.type` is `"array"`, not `"object"`.** Forms with `"object"` render empty. - **`bindingSchema` mirroring is mandatory for REST-bound fields.** Studio hides this in the GUI; an API-created form needs both `struct.items[i]` AND `bindingSchema.properties.` populated, or the dropdown renders and never fetches. - **`:name` colon syntax in `href`, not `{name}`.** Curly-brace placeholders are silently ignored. - **`enum`/`enumNames` flat-vs-object asymmetry.** In `struct.items[i]` they're `{id, label, value}` objects; in `schema.properties.` they're flat string arrays. - **`DependencyWidget` required on BOTH ends of a cascade.** Easy to remember to set it on the dependent field and forget the source — without the source-side widget the dependent won't re-fetch. - **`labelKeyPointer` is a red herring.** Don't bother setting it; both label and value come from `sourceKeyPointer`. - **Bulk DELETE has no per-id alternative.** Always send `{"ids":[...]}` to `/json-forms/forms`. ## See Also - `builder-agent` for workflows that consume form output, manual-trigger wiring, and project-level component management. - Helper files in `assets/helpers/`: - `assets/helpers/create/create-json-form.json` — static-enum scaffold - `assets/helpers/create/create-json-form-rest-bound.json` — REST-bound + cascading scaffold (Inventory Manager Site/Device cascade worked example) - `assets/helpers/create/create-ops-manager-trigger-manual.json` — manual trigger that consumes a form - `assets/helpers/assets/json-form-example-static-enum.json` — real export, static-enum (Cisco IOS Port Turn Up) - `assets/helpers/assets/json-form-example-rest-bound.json` — real export, REST-bound (Cisco IOS Compliance)