> ## Documentation Index
> Fetch the complete documentation index at: https://dripart-nav-custom-nodes-v3.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Hidden and Flexible inputs (V3)

> ComfyUI hidden inputs (UNIQUE_ID, PROMPT, EXTRA_PNGINFO, DYNPROMPT) and flexible inputs, including custom datatypes and wildcard inputs for custom nodes (V3 schema).

## Hidden inputs

Alongside the inputs declared in the schema, which create corresponding inputs or widgets on the
client-side, custom nodes can request certain information from the server through *hidden* inputs. Hidden
inputs are not visible in the UI.

In the V3 schema, hidden inputs are requested by passing a list of `io.Hidden` enum values to the `hidden`
parameter of the schema. During execution, the values are available on the node class as `cls.hidden`, so
an `execute` method can read them by name:

```python theme={null}
from comfy_api.latest import io

class MyNode(io.ComfyNode):
    @classmethod
    def define_schema(cls) -> io.Schema:
        return io.Schema(
            node_id="MyNode",
            inputs=[io.String.Input("text")],
            outputs=[io.String.Output()],
            hidden=[
                io.Hidden.unique_id,
                io.Hidden.prompt,
                io.Hidden.extra_pnginfo,
            ],
        )

    @classmethod
    def execute(cls, text) -> io.NodeOutput:
        # hidden values are accessed via cls.hidden
        print(cls.hidden.unique_id)
        print(cls.hidden.prompt)
        print(cls.hidden.extra_pnginfo)
        return io.NodeOutput(text)
```

Available hidden values (see the [V3 Migration guide](/custom-nodes/v3_migration) for the full list):

### UNIQUE\_ID

`io.Hidden.unique_id` provides the unique identifier of the node, and matches the `id` property of the
node on the client side. It is commonly used in client-server communications (see
[messages](/development/comfyui-server/comms_messages#getting-node-id)).

### PROMPT

`io.Hidden.prompt` provides the complete prompt sent by the client to the server. See
[the prompt object](/custom-nodes/js/javascript_objects_and_hijacking#prompt) for a full description.

### EXTRA\_PNGINFO

`io.Hidden.extra_pnginfo` provides a dictionary that will be copied into the metadata of any `.png` files
saved. Custom nodes can store additional information in this dictionary for saving (or as a way to
communicate with a downstream node).

<Tip>Note that if Comfy is started with the `disable_metadata` option, this data won't be saved.</Tip>

### DYNPROMPT

`io.Hidden.dynprompt` provides an instance of `comfy_execution.graph.DynamicPrompt`. It differs from
`PROMPT` in that it may mutate during the course of execution in response to [Node Expansion](/custom-nodes/backend/expansion).

<Tip>`DYNPROMPT` should only be used for advanced cases (like implementing loops in custom nodes).</Tip>

## Flexible inputs

### Custom datatypes

If you want to pass data between your own custom nodes, you may find it helpful to define a custom
datatype. This is (almost) as simple as just choosing a name for the datatype, which should be a unique
string in upper case, such as `CHEESE`.

Create the type with the `io.Custom` helper (or the `@io.comfytype` decorator for a full class
definition), then use it for inputs and outputs. The Comfy client will only allow `CHEESE` outputs to
connect to a `CHEESE` input. A `CHEESE` value can be any Python object.

```python theme={null}
from comfy_api.latest import io

# Create the custom type once, then reuse it
Cheese = io.Custom("CHEESE")

class CheeseNode(io.ComfyNode):
    @classmethod
    def define_schema(cls) -> io.Schema:
        return io.Schema(
            node_id="CheeseNode",
            inputs=[Cheese.Input("my_cheese")],
            outputs=[Cheese.Output()],
        )
```

Because the Comfy client doesn't know anything about `CHEESE`, it can't display a widget for it. Custom
datatype inputs are therefore socket inputs: they must be connected to an upstream node, and the widget
conversion options (`force_input` and `socketless`) don't apply to them. If you want a widget fallback for
your custom type, you need to define a custom widget for it, which is a topic for another day.

### Wildcard inputs

The frontend allows `*` to indicate that an input can be connected to any source. In the V3 schema,
`io.AnyType` provides this wildcard type directly:

```python theme={null}
class AnyNode(io.ComfyNode):
    @classmethod
    def define_schema(cls) -> io.Schema:
        return io.Schema(
            node_id="AnyNode",
            inputs=[
                io.AnyType.Input("anything"),
            ],
            outputs=[io.AnyType.Output()],
        )
```

With `io.AnyType`, type validation accepts any input, so the legacy V1 workaround (adding an
`input_types` argument to `VALIDATE_INPUTS` in order to skip backend validation) is not needed. It's up to
the node to make sense of the data that is passed.

### Dynamically created inputs

If inputs are dynamically created on the client side, they can't be defined in the Python source code.

The V3 schema handles this with the `accept_all_inputs` flag. When it is `True`, all inputs from the
prompt that are not defined in the schema are passed through to `execute` as keyword arguments:

```python theme={null}
class FlexibleNode(io.ComfyNode):
    @classmethod
    def define_schema(cls) -> io.Schema:
        return io.Schema(
            node_id="FlexibleNode",
            inputs=[
                # inputs that are always present can still be declared here
            ],
            outputs=[io.Image.Output()],
            accept_all_inputs=True,
        )

    @classmethod
    def execute(cls, **kwargs) -> io.NodeOutput:
        # the dynamically created input data will be in the keyword arguments
        ...
```

Note that inputs declared in the schema are still validated and dispatched normally; `accept_all_inputs`
only affects the inputs that are *not* declared.
