# IML Skill Reference IML skills are lightweight automation definitions that run inside InTouch without writing code. Each skill is a `skill.iml` file that declares inputs, a sequence of steps, and published outputs. --- ## Quick Start Create a directory under `INTOUCH_HOME/tools/installed/my-skill/` containing a `skill.iml`: ```json { "name": "my-skill", "displayName": "My First Skill", "version": "1.0.0", "vendor": "Your Name", "description": "Fetches a joke from an API", "input": { "category": { "type": "string", "description": "Joke category", "required": false, "default": "any" } }, "steps": [ { "name": "fetch-joke", "type": "http", "method": "GET", "url": "https://v2.jokeapi.dev/joke/{{input.category}}", "output": { "joke": "$.joke", "setup": "$.setup", "delivery": "$.delivery" } }, { "name": "format-result", "type": "log", "message": "Joke: {{fetch-joke.setup}} \u2014 {{fetch-joke.delivery}}" } ], "publish": { "joke": "{{fetch-joke.joke}}", "setup": "{{fetch-joke.setup}}", "delivery": "{{fetch-joke.delivery}}" } } ``` Restart the server (or call `POST /api/intouch-tool/reload`) to load it. --- ## File Structure ``` INTOUCH_HOME/ tools/ installed/ my-skill/ skill.iml # Skill definition (required) another-skill/ skill.yml # .yml extension also accepted ``` A directory containing `connector.json` is a JAR plugin, not an IML skill — it will be loaded by PluginLoader instead. --- ## Skill Definition Schema | Field | Type | Notes | |---|---|---| | **Required** | | | | `name` | string | Internal name (lowercase, no spaces) | | **Optional metadata** | | | | `displayName` | string | Human-readable name (defaults to name) | | `version` | string | Semantic version (default: "1.0.0") | | `vendor` | string | Author name | | `description` | string | What this skill does | | **Optional credential** | | | | `credentialBased` | boolean | Whether this skill requires a credential (default: false) | | `credential` | — | | | `properties` | — | | | `propertyName` | — | | | `type` | string | Property data type | | `description` | string | Help text | | `required` | boolean | Is this required? (default: false) | | `default` | string | Default value (default: "") | | `secret` | boolean | Mask in UI? (default: false) | | **Input parameters (what the user fills in)** | | | | `input` | — | | | `paramName` | — | | | `type` | string | string, integer, number, boolean | | `description` | string | Help text shown in UI | | `required` | boolean | Is this required? (default: false) | | `default` | string | Default value (default: "") | | `secret` | boolean | Mask in UI? (default: false) | | **Execution steps (run in order)** | | | | `steps` | — | | | `type` | string | Step type (see Step Types below) | | `onError` | string | Error handling: fail, skip, continue (default: fail) | | `output` | — | Custom output extraction | | `outputName` | expression | name → JSONPath or variable expression | | **... type-specific properties (see below)** | | | | **Published outputs (available to subsequent workflow tasks)** | | | | `publish` | — | | | `outputName` | expression | name → variable expression | --- ## Variable References Use `{{...}}` syntax anywhere in step properties. Variables are resolved just before each step executes. | Syntax | Source | Example | |--------|--------|---------| | `{{input.name}}` | Skill input parameters | `{{input.category}}` | | `{{credential.name}}` | Credential properties | `{{credential.jdbcUrl}}` | | `{{stepName.field}}` | Output from a previous step | `{{fetch-data._body}}` | | `{{env.VAR_NAME}}` | System environment variables | `{{env.INTOUCH_HOME}}` | Unresolved or malformed references FAIL the tool loud with a "here is what is available" error; they are never left as literal text. --- ## Step Types ### http — HTTP/REST API Calls Makes an HTTP request and captures the response. ```json [ { "name": "call-api", "type": "http", "method": "GET", "url": "https://api.example.com/data", "contentType": "application/json", "headers": { "X-Custom": "value" }, "body": "{\"key\": \"value\"}", "auth": { "type": "bearer", "token": "{{input.apiKey}}" }, "successCodes": [ 200, 201, 204 ], "output": { "userId": "$.data.id", "name": "$.data.name" } } ] ``` **Authentication types:** | Type | Properties | |------|-----------| | `none` | No authentication | | `basic` | `username`, `password` → Basic auth header | | `bearer` | `token` → Bearer token header | | `header` | `headerName`, `headerValue` → Custom auth header | **Auto-generated outputs:** - `_body` — raw response body - `_status` — HTTP status code **JSONPath support:** - `$.field` — top-level field - `$.field.subfield` — nested field - `$.array[0]` — array index - `$.array[*]` — all array elements (comma-separated) --- ### sql — Database Queries Executes a SQL query against a JDBC database. Uses credential properties from the skill's credential definition. ```json [ { "name": "get-users", "type": "sql", "query": "SELECT id, name, email FROM users WHERE active = 1" } ] ``` **Required credential properties** (set in the skill's `credential` section): - `jdbcUrl` — full JDBC URL (e.g., `jdbc:mysql://localhost:3306/mydb`) - `username` — database username - `password` — database password **Example with credential:** ```json { "name": "user-report", "credentialBased": true, "credential": { "properties": { "jdbcUrl": { "type": "string", "description": "JDBC credential URL", "required": true }, "username": { "type": "string", "description": "Database username", "required": true }, "password": { "type": "string", "description": "Database password", "required": true, "secret": true } } }, "steps": [ { "name": "count-users", "type": "sql", "query": "SELECT count(*) as total FROM users" }, { "name": "report", "type": "log", "message": "Total users: {{count-users.total}}" } ], "publish": { "totalUsers": "{{count-users.total}}" } } ``` **Auto-generated outputs:** - `_rowCount` — number of rows returned (SELECT) or affected (INSERT/UPDATE/DELETE) - `_columns` — comma-separated column names (SELECT only) - `_rows` — JSON array of row objects (SELECT only, max 10,000 rows) - First row columns are directly accessible: `{{stepName.columnName}}` **SELECT queries** return results as a JSON array. The first row's columns are accessible directly by name. **INSERT/UPDATE/DELETE** queries return the affected row count. --- ### shell — Run Local Commands Executes a command on the server's operating system and captures the output. ```json [ { "name": "list-files", "type": "shell", "command": "ls -la /data/incoming", "workingDir": "/data", "timeout": 30, "successExitCodes": [ 0 ] } ] ``` On Linux/macOS, commands run via `/bin/sh -c`. On Windows, via `cmd /c`. **Auto-generated outputs:** - `_stdout` — captured standard output (trimmed) - `_stderr` — captured standard error (trimmed) - `_exitCode` — process exit code **Example — run a script and use its output:** ```json { "steps": [ { "name": "check-disk", "type": "shell", "command": "df -h / | tail -1 | awk '{print $5}'", "output": { "usage": "{{check-disk._stdout}}" } }, { "name": "alert", "type": "condition", "condition": "{{check-disk._stdout}} > 90%", "thenSteps": [ { "name": "warn", "type": "log", "message": "Disk usage critical: {{check-disk._stdout}}" } ] } ] } ``` --- ### ai — AI Assistant Calls Sends a prompt to the InTouch AI assistant and captures the response. ```json [ { "name": "summarize", "type": "ai", "prompt": "Summarize this data: {{fetch-data._body}}" } ] ``` **Auto-generated outputs:** - `answer` — the AI response text - `model` — model used - `inputTokens` — tokens in the prompt - `outputTokens` — tokens in the response --- ### file — File Operations Reads, writes, copies, moves, or deletes files on the server filesystem. ```json [ { "name": "read-config", "type": "file", "operation": "read", "path": "/data/config.json" } ] ``` **Operations:** | Operation | Properties | Outputs | |-----------|-----------|---------| | `read` | `path` | `content`, `size` | | `write` | `path`, `content` | `path`, `size` | | `copy` | `path`, `destination` | `source`, `destination` | | `move` | `path`, `destination` | `source`, `destination` | | `delete` | `path` | `deleted` (true/false) | | `exists` | `path` | `exists` (true/false) | --- ### archive — Zip/Tar Operations Creates and extracts ZIP and tar/gzip archives. Uses Java's built-in `java.util.zip` for ZIP operations and Apache Commons Compress for tar. No external tools required — works cross-platform. ```json [ { "name": "extract-data", "type": "archive", "operation": "unzip", "path": "/data/download.zip", "destination": "/data/extracted/" } ] ``` **Operations:** | Operation | path (source) | destination (output) | Outputs | Notes | |-----------|--------------|---------------------|---------|-------| | `zip` | File or directory to compress | Output `.zip` file path | `path`, `fileCount`, `size` | Directories are zipped recursively | | `unzip` | `.zip` file to extract | Output directory | `path`, `fileCount` | Zip-slip protection built in | | `tar` | File or directory to archive | Output `.tar` or `.tar.gz`/`.tgz` file path | `path`, `fileCount`, `size` | Auto-gzips if destination ends in `.gz` or `.tgz` | | `untar` | `.tar` or `.tar.gz`/`.tgz` file to extract | Output directory | `path`, `fileCount` | Auto-detects gzip from file extension | **Examples:** ```json [ { "name": "package-reports", "type": "archive", "operation": "zip", "path": "/data/reports/2026-q1", "destination": "/data/exports/q1-reports.zip" }, { "name": "extract-backup", "type": "archive", "operation": "untar", "path": "/data/downloads/backup.tar.gz", "destination": "/data/restored/" }, { "name": "compress-export", "type": "archive", "operation": "zip", "path": "/data/exports/large-report.csv", "destination": "/data/exports/large-report.zip" } ] ``` --- ### transform — String Transformations Applies a transformation to a source string. ```json [ { "name": "clean-input", "type": "transform", "source": "{{fetch-data._body}}", "expression": "lowercase" } ] ``` **Available expressions:** | Expression | Description | Example | |-----------|-------------|---------| | `uppercase` | Convert to uppercase | `HELLO WORLD` | | `lowercase` | Convert to lowercase | `hello world` | | `trim` | Remove leading/trailing whitespace | `hello` | | `length` | String length | `11` | | `replace:old->new` | Replace substring | `replace:foo->bar` | | `regex:pattern` | Extract first regex match | `regex:\d+` | | `substring:start,end` | Extract substring | `substring:0,5` | | `split:delimiter` | Split into lines | `split:,` | | `contains:search` | Check if contains (true/false) | `contains:error` | **Auto-generated output:** `result` --- ### condition — Branching Evaluates a condition and runs different steps based on the result. ```json [ { "name": "check-status", "type": "condition", "condition": "{{call-api._status}} == 200", "thenSteps": [ { "name": "process", "type": "log", "message": "Success!" } ], "elseSteps": [ { "name": "handle-error", "type": "log", "message": "Failed with status {{call-api._status}}" } ] } ] ``` **Condition operators:** `==`, `!=`, `>`, `<`, `>=`, `<=` Numeric comparisons are used for `>`, `<`, `>=`, `<=`. String comparison for `==` and `!=`. A non-empty string is truthy; empty/blank is falsy. --- ### loop — Iteration Iterates over a comma-separated list of items, executing nested steps for each. ```json [ { "name": "process-files", "type": "loop", "items": "{{list-files._stdout}}", "itemVar": "file", "loopSteps": [ { "name": "process-one", "type": "log", "message": "Processing: {{process-files.file}}" } ] } ] ``` **Auto-generated outputs:** - `count` — total number of items processed - `index` — current iteration index (0-based, available inside loop) - The `itemVar` value — current item (available inside loop) --- ### sleep — Delay Execution Pauses execution for a specified number of seconds. ```json [ { "name": "wait-for-api", "type": "sleep", "duration": 5 } ] ``` Useful for: - Rate limiting API calls in a loop - Waiting for an external process to complete - Adding delays in polling patterns **Auto-generated output:** `_slept` (seconds actually slept) --- ### log — Activity Messages Posts a message to the live workflow activity log. ```json [ { "name": "status", "type": "log", "message": "Processing complete: {{count-users._rowCount}} users found" } ] ``` --- ## Error Handling Each step supports an `onError` property: | Value | Behavior | |-------|----------| | `fail` | Stop execution and fail the skill (default) | | `skip` | Log the error and skip to the next step | | `continue` | Log the error, set `_error` output, continue to next step | ```json [ { "name": "optional-cleanup", "type": "file", "operation": "delete", "path": "/tmp/workfile.dat", "onError": "skip" } ] ``` When `onError: continue` is used, the error message is available as `{{stepName._error}}`. --- ## Publishing Outputs The `publish` section defines what values are available to subsequent tasks in a workflow. Each entry maps a name to a variable expression: ```json { "publish": { "totalUsers": "{{count-users._rowCount}}", "reportPath": "{{write-report.path}}", "summary": "{{summarize.answer}}" } } ``` Published values become task properties in the InTouch workflow engine, accessible by any task that runs after this one. --- ## Complete Example — API Health Check ```json { "name": "api-health-check", "displayName": "API Health Check", "version": "1.0.0", "vendor": "InTouch", "description": "Checks multiple API endpoints and reports their status", "input": { "endpoints": { "type": "string", "description": "Comma-separated list of URLs to check", "required": true }, "timeout": { "type": "integer", "description": "Request timeout in seconds", "default": "10" } }, "steps": [ { "name": "check-endpoints", "type": "loop", "items": "{{input.endpoints}}", "itemVar": "url", "loopSteps": [ { "name": "ping", "type": "http", "method": "GET", "url": "{{check-endpoints.url}}", "successCodes": [ 200, 201, 202, 204, 301, 302 ], "onError": "continue" }, { "name": "log-result", "type": "log", "message": "{{check-endpoints.url}} \u2192 {{ping._status}}" } ] }, { "name": "done", "type": "log", "message": "Checked {{check-endpoints.count}} endpoint(s)" } ], "publish": { "endpointsChecked": "{{check-endpoints.count}}" } } ``` ## Complete Example — Database Backup Notification ```json { "name": "db-backup-notify", "displayName": "Database Backup with Notification", "version": "1.0.0", "vendor": "InTouch", "description": "Dumps a database table to CSV, uploads it, and sends a summary via AI", "credentialBased": true, "credential": { "properties": { "jdbcUrl": { "type": "string", "description": "JDBC URL", "required": true }, "username": { "type": "string", "required": true }, "password": { "type": "string", "required": true, "secret": true } } }, "input": { "tableName": { "type": "string", "description": "Table to export", "required": true }, "outputDir": { "type": "string", "description": "Directory for the CSV file", "default": "/tmp" } }, "steps": [ { "name": "export-data", "type": "sql", "query": "SELECT * FROM {{input.tableName}} LIMIT 1000" }, { "name": "write-csv", "type": "file", "operation": "write", "path": "{{input.outputDir}}/{{input.tableName}}_export.json", "content": "{{export-data._rows}}" }, { "name": "summarize", "type": "ai", "prompt": "I exported {{export-data._rowCount}} rows from table {{input.tableName}}. Columns: {{export-data._columns}}. Write a one-sentence summary of what this data contains.\n" }, { "name": "report", "type": "log", "message": "Export complete: {{export-data._rowCount}} rows \u2192 {{write-csv.path}}" } ], "publish": { "rowCount": "{{export-data._rowCount}}", "filePath": "{{write-csv.path}}", "summary": "{{summarize.answer}}" } } ``` --- ## Step Type Summary | Type | Purpose | Key Properties | Key Outputs | |------|---------|---------------|-------------| | `http` | REST API calls | `method`, `url`, `body`, `auth`, `headers` | `_body`, `_status`, JSONPath extractions | | `sql` | Database queries | `query` | `_rowCount`, `_columns`, `_rows`, column values | | `shell` | Local commands | `command`, `workingDir`, `timeout` | `_stdout`, `_stderr`, `_exitCode` | | `ai` | AI assistant | `prompt` | `answer`, `model`, `inputTokens`, `outputTokens` | | `file` | File operations | `operation`, `path`, `content`, `destination` | `content`, `size`, `exists`, `deleted` | | `archive` | Zip/tar operations | `operation`, `path`, `destination` | `path`, `fileCount`, `size` | | `transform` | String transforms | `source`, `expression` | `result` | | `condition` | If/else branching | `condition`, `thenSteps`, `elseSteps` | — | | `loop` | Iteration | `items`, `itemVar`, `loopSteps` | `count`, `index`, itemVar | | `sleep` | Delay execution | `duration` | `_slept` | | `log` | Activity messages | `message` | — |