> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.itential.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.itential.com/_mcp/server.

# Validate and limit service inputs with decorators

> Use decorators to limit and validate the inputs passed to a service at execution time.

This topic describes how you can use decorators within Gateway to limit the inputs passed into a service that you execute using the `iagctl run service` command.

A decorator defines the inputs a service expects: which parameters it accepts, what type they must be, and which are required. When a user runs a service through a workflow, the decorator validates that the correct inputs were provided before execution begins. A single decorator can be shared across multiple services that accept the same inputs, reducing duplication. Think of a decorator as a form definition that you attach to one or more services.

## Decorators in playbooks

To best understand decorators, consider an Ansible Playbook service called `simple-ansible` that uses the following playbook.

```yaml
---
- name: A Simple Hello World Example
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Just Say Hello
      debug:
        msg: "Hello Mr. gateway this is from '{{ caller }}'"
```

Notice that the example playbook takes in a single variable called `caller`. When running the service, you can use the `--set` flag to pass a value for `caller` to the playbook.

```bash
iagctl run ansible-playbook simple-ansible --set caller=documentation
```

## JSON Schema in decorators

Suppose that you want to limit the types of inputs that can pass to the playbook using the `--set` flag. Decorators allow you to limit the available parameters by using defined [JSON Schema](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01) standards.

Consider the example JSON Schema shown below that limits the available inputs to a key of `caller` with values of `documentation` or `learner`.

```json
{
  "$id": "root",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "caller": {
      "type": "string",
      "enum": ["documentation", "learner"]
    }
  },
  "required": [
    "caller"
  ],
  "additionalProperties": false
}
```

You can explore all available options within JSON Schema by referencing the [official spec](https://json-schema.org/draft/2020-12/json-schema-validation).

## Create a decorator resource

First, save the JSON schema above as a file called `simple-deco.json`.

Next, create a decorator resource called `simple-deco` within Gateway using the `json` schema file that you created in the previous step.

```bash
iagctl create decorator simple-deco --schema @./simple-deco.json
```

## Create a gateway service with decorators

Once you create the decorator resource, create a service that uses it. The following example creates an Ansible Playbook service with the `simple-deco` decorator.

```bash
iagctl create ansible-playbook ansible-with-deco --repository gateway-resources --working-dir ansibleplaybook --playbook hello-world.yml --decorator simple-deco
```

```bash
Output:

Successfully created the Ansible playbook(s)
Name:        ansible-with-deco
Repo Name:   gateway-resources
Working Dir: ansibleplaybook
Playbook(s): hello-world.yml
Decorator:   simple-deco
Description:
Tags:
Runtime Arguments:
```

Set the `--use` flag when using the service `run` command to get basic, high-level information about the decorator.

```bash
iagctl run service ansible-playbook ansible-with-deco --use
```

```bash
Output:

Decoration Usage:
-----------------------------
The following keys can be set on the command line via the set command.

+----------+--------+-------------+--------------------+
|   NAME   |  TYPE  | DESCRIPTION |      EXAMPLE       |
+----------+--------+-------------+--------------------+
| caller** | string |             | --set caller=value |
+----------+--------+-------------+--------------------+

** is Required
```

### Successful decoration

When you pass in a valid value for `caller` and run the Ansible playbook service, the playbook succeeds.

```bash
iagctl run service ansible-playbook ansible-with-deco --set caller=documentation
```

### Decoration error

If you try to pass in a value for `caller` that is not `documentation` or `learner`, you receive an error.

```bash
iagctl run service ansible-playbook ansible-with-deco --set caller=someWrongInput
```

```bash
Output:

Error: failed to run ansible playbook 'ansible-with-deco': decoration errors have been encountered: should be one of ["documentation", "learner"] /caller
```

Additionally, if you try to set any value for a key that is not `caller`, an error returns.

```bash
iagctl run service ansible-playbook ansible-with-deco --set caller=documentation --set someBadKey=value
```

```bash
Output:

Error: failed to run ansible playbook 'ansible-with-deco': decoration errors have been encountered: extra input found someBadKey
```

## Boolean properties in Python script services

Decorators support `boolean` as a property type for Python script services. Boolean properties enable flag-style argument passing — instead of `--set verbose=true`, you pass `--set verbose` to set the flag, and omit it entirely to leave it unset.

### Define a boolean property

To define a boolean property, set `"type": "boolean"` in the decorator schema:

```json
{
  "$id": "root",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "verbose": {
      "type": "boolean"
    },
    "host": {
      "type": "string"
    }
  },
  "required": [
    "host"
  ]
}
```

### Pass boolean arguments at runtime

When a property is defined as `boolean` in the decorator, use bare `--set key` syntax to pass the flag. Omit the key entirely to leave it unset.

```bash
# Sets --verbose flag; script receives: python main.py --verbose --host='10.0.0.1'
iagctl run service python-script my-script --set verbose --set host=10.0.0.1
 
# Omits --verbose flag; script receives: python main.py --host='10.0.0.1'
iagctl run service python-script my-script --set host=10.0.0.1
```

How Gateway passes the flag to your script depends on whether the service has a decorator with the property typed as `boolean`:

**With a decorator**, Gateway passes the flag as `--verbose`. Parse it in your script using `action='store_true'`:

```python
import argparse
 
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--verbose', action='store_true', help="Enable verbose output")
    parser.add_argument('--host', required=True, help="Target host")
    args = parser.parse_args()
 
    if args.verbose:
        print(f"Connecting to {args.host} with verbose output enabled")
    else:
        print(f"Connecting to {args.host}")
```

**Without a decorator**, Gateway passes the flag as `--verbose=true`. Parse it as a string value:

```python
import argparse
 
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--verbose', default='false', help="Enable verbose output")
    parser.add_argument('--host', required=True, help="Target host")
    args = parser.parse_args()
 
    if args.verbose.lower() == 'true':
        print(f"Connecting to {args.host} with verbose output enabled")
    else:
        print(f"Connecting to {args.host}")
```

## Pass large values as files in Python script services

Gateway 5.5.2+

Gateway passes decorator property values to a Python script as CLI arguments by default. Linux limits the total size of CLI arguments to roughly 128 KB, so a property with a large value—a full running-config, device session details, or query results—can exceed that limit and cause the script to fail.

To avoid this, mark a decorator property as file-backed. Instead of passing the value inline as a CLI argument, Gateway writes it to a temporary file at execution time and passes your script an environment variable that contains the file's path. Gateway deletes the temporary file after the script finishes running, whether it succeeds or fails.

### Define a file-backed property

File-backed properties are supported for Python script services only.

To mark a property as file-backed, add two keywords to it in the decorator schema:

* `x-itential-payload-type: "file"` tells Gateway to write the value to a temporary file instead of passing it inline.
* `x-itential-payload-target` sets the name of the environment variable that Gateway uses to pass the file path to your script.

The property keeps `"type": "string"` because the underlying content is still text, typically JSON. The `x-itential-payload-*` keywords only change how Gateway delivers the value to your script — they don't change the property's type or validation.

```json
{
  "properties": {
    "pre_check_result_json": {
      "type": "string",
      "description": "Pre-check results, written to a temp file by Gateway rather than inlined",
      "x-itential-payload-type": "file",
      "x-itential-payload-target": "PRE_CHECK_RESULT_FILE"
    }
  }
}
```

### Read a file-backed value in your script

At runtime, check for the environment variable you set in `x-itential-payload-target`. If it's present, open the file at that path to read the value.

```python
import os

path = os.environ.get("PRE_CHECK_RESULT_FILE")
if path:
    with open(path, "r") as f:
        pre_check_result_json = f.read()
```

Because a file-backed property is still a normal decorator property, you can still pass it inline with `--set pre_check_result_json=<value>` for local testing or smaller payloads. Gateway only redirects the value to a file when needed. Write your script to check for the CLI argument first, then fall back to the file path, so the same script works whether the value arrives inline or as a file:

```python
def load_pre_check_result(arg_value):
    if arg_value:
        return arg_value  # passed inline via --set pre_check_result_json
    path = os.environ.get("PRE_CHECK_RESULT_FILE")
    if not path:
        raise RuntimeError("pre_check_result_json was not provided as an argument or via PRE_CHECK_RESULT_FILE")
    with open(path, "r") as f:
        return f.read()
```

## Learn more

For more information on decorator operations, see the following iagctl CLI commands:

* [iagctl create decorator](./iagctl/create-decorator)
* [iagctl get decorators](./iagctl/get-decorators)
* [iagctl describe decorator](./iagctl/describe-decorator)
* [iagctl delete decorator](./iagctl/delete-decorator)