# How to Handle User Input and Parameters in OpenAI Plugins: Manifests, Validation, and Security

> Learn to handle user input and parameters in OpenAI plugins. Declare inputs in the manifest, validate payloads, and sanitize data for secure processing.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-06-14

---

**To handle user input and parameters in OpenAI plugins, declare expected inputs in the [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) manifest, validate the JSON payload inside your skill handlers, and sanitize all data before processing.**

The **openai/plugins** repository provides a framework for building extensions that interact with external APIs like Figma, Zoom, and Zotero. Properly handling user input and parameters requires understanding the contract defined in the plugin manifest and the transport layer that delivers data to your skills. This guide walks through the end-to-end flow—from schema declaration to runtime validation—using actual source files from the repository.

## Declare Input Schemas in the Plugin Manifest

Every plugin defines its interface through a [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) manifest file located at the plugin root. This manifest acts as the single source of truth for the platform, specifying the expected shape of incoming data via `inputSchema` or `parameters`. Declaring these schemas enables the runtime to validate arguments before they reach your code and automatically generate UI components like forms or autocomplete fields.

The Figma plugin demonstrates this pattern by defining a strict schema for node operations:

```json
{
  "name": "figma-use",
  "description": "Interact with the Figma design system",
  "inputSchema": {
    "type": "object",
    "properties": {
      "nodeId": { "type": "string", "description": "ID of the Figma node to target" },
      "action":  { "type": "string", "enum": ["select","delete"] }
    },
    "required": ["nodeId", "action"]
  }
}

```

This manifest resides in [`plugins/figma/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/figma/.codex-plugin/plugin.json). By marking `nodeId` and `action` as required and restricting `action` to specific enum values, the platform can reject malformed requests before invoking the skill logic.

## Transport Mechanisms: How Input Reaches Your Skills

The platform delivers user input through different transport mechanisms depending on the integration type. Understanding these delivery methods ensures you parse the payload correctly in your skill handlers.

### HTTP JSON Payloads

Most modern skills receive parameters via HTTP POST requests containing a JSON body. The payload typically nests user input under a `params` key. Python and Node.js skill handlers in `plugins/<name>/skills/<skill>/scripts/` should expect this structure and extract values accordingly.

### CLI Arguments

Command-line skills parse input from argument vectors. The Zotero plugin illustrates this approach in [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py), where the skill uses `argparse` to define mutually exclusive groups:

```python

# Simplified excerpt from zotero.py

parser.add_argument('--file', help='Path to a file to process')
parser.add_argument('--text', help='Raw text string to analyze')

# Logic enforces that only one of --file or --text is provided

```

This pattern ensures that users cannot supply conflicting inputs when invoking the plugin from a shell environment.

### Webhook Form Submissions

Zoom Team-Chat plugins receive interactive form data via webhook events. When a user submits a form, the platform sends a JSON payload to your endpoint where the typed text resides in the `payload.cmd` field. The reference implementation in [`plugins/zoom/skills/team-chat/examples/form-submissions.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/team-chat/examples/form-submissions.md) demonstrates parsing this field to extract user commands, while [`plugins/zoom/skills/team-chat/references/webhook-events.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/team-chat/references/webhook-events.md) documents the full contract and security considerations for treating these payloads as untrusted.

## Validate and Sanitize Inside the Skill Handler

Even when the platform validates the schema against your manifest, always re-validate inside the skill to prevent injection attacks from forged webhook requests or malicious HTTP calls. Use schema validation libraries followed by business-logic checks and output encoding.

The following Python example validates the Figma parameters using `jsonschema`, then applies HTML escaping to prevent XSS when rendering the node ID:

```python
import jsonschema
import html
from typing import Dict, Any

def handler(event: Dict[str, Any]):
    # Extract payload from the transport layer

    payload = event.get("params", {})
    
    # Schema validation (matches the manifest definition)

    schema = {
        "type": "object",
        "properties": {
            "nodeId": {"type": "string"},
            "action": {"type": "string", "enum": ["select", "delete"]}
        },
        "required": ["nodeId", "action"]
    }
    jsonschema.validate(payload, schema)  # Raises ValidationError if invalid

    
    # Sanitization: escape HTML and strip whitespace

    node_id = html.escape(payload["nodeId"].strip())
    action = payload["action"]
    
    # Business logic execution

    return {
        "type": "message",
        "content": f"Node **{node_id}** will be {action}ed."
    }

```

Never echo raw input directly into responses. Always pass user-generated content through an escaping function appropriate for your output format.

## Return Structured Responses

After processing validated input, return a structured JSON object that the platform can render as a chat message, interactive card, or file download. The response format varies by integration—Zoom expects card definitions while generic skills use simple message objects.

```json
{
  "type": "message",
  "content": "Operation completed successfully."
}

```

For Zoom-specific card responses, consult the schema definitions in the repository to ensure your payload renders correctly in the Team-Chat interface.

## Summary

- **Declare** expected parameters in [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) using JSON Schema to enable platform-level validation and UI generation.
- **Transport** layers vary by skill type: HTTP JSON for web skills, CLI arguments for shell scripts, and webhook payloads for Zoom integrations.
- **Validate** all incoming data inside the skill handler using libraries like `jsonschema` (Python) or `ajv` (Node) to protect against forged requests.
- **Sanitize** user content with HTML escaping and length restrictions before including it in responses or database queries.
- **Reference** implementation files like [`plugins/figma/.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/plugins/figma/.codex-plugin/plugin.json) and [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py) for production-ready patterns.

## Frequently Asked Questions

### Where do I define what parameters my plugin accepts?

Define them in the [`.codex-plugin/plugin.json`](https://github.com/openai/plugins/blob/main/.codex-plugin/plugin.json) manifest file using the `inputSchema` field. This JSON Schema acts as a contract that tells the platform what arguments to expect, enables automatic form generation, and triggers validation before your skill code executes.

### How does the Zoom Team-Chat plugin receive form submissions?

Zoom Team-Chat plugins receive form data via webhooks where user input resides in the `payload.cmd` field. Your skill handler must parse this JSON payload, validate the `cmd` string, and can then respond with an updated card or message as shown in [`plugins/zoom/skills/team-chat/examples/form-submissions.md`](https://github.com/openai/plugins/blob/main/plugins/zoom/skills/team-chat/examples/form-submissions.md).

### Should I validate input again if the platform already checks the schema?

Yes. Always re-validate inside your skill using libraries like `jsonschema` because webhooks and HTTP endpoints can receive forged requests that bypass platform checks. Additional sanitization with HTML escaping prevents XSS attacks when displaying user content in responses.

### How do I handle mutually exclusive CLI arguments in a plugin?

Use standard argument parsing libraries like Python's `argparse` with mutually exclusive groups. The Zotero plugin demonstrates this pattern in [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py), where `--file` and `--text` arguments cannot be used simultaneously, ensuring clean parameter handling.