> ## Documentation Index
> Fetch the complete documentation index at: https://developer.watson-orchestrate.ibm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAPI Plug-ins

## OpenAPI plug-ins

The plug-ins described so far run as Python tools inside watsonx Orchestrate. You can also implement a plug-in as a remote HTTP service described by an OpenAPI specification. An **OpenAPI plug-in** runs at the same two phases as a Python plug-in (`agent_pre_invoke` and `agent_post_invoke`) and returns the same result (`continue_processing` and an optional `modified_payload`), but the logic lives in a service that you host rather than in a Python tool.

Consider an OpenAPI plug-in when you want to:

* Reuse an existing guardrail, masking, or policy service that already runs as a web endpoint, in any language.
* Apply one guardrail consistently across many agents or tenants from a single service.
* Keep plug-in logic and its release cycle separate from your agents.

## How OpenAPI plug-ins are invoked

An OpenAPI plug-in is invoked like any other OpenAPI tool: the request is brokered through the Tool Runtime Manager rather than sent directly from the agent. When you import the tool, its `servers` URL is checked against your tenant's outbound policy allowlist, the same as for any OpenAPI tool. This keeps egress controls consistent and prevents a plug-in from reaching internal endpoints.

## Context and payload

Like a Python plug-in, an OpenAPI plug-in receives a **context** and a **payload**, and returns a result. watsonx Orchestrate sends a single `POST` to the plug-in operation with both values in the request body:

```json theme={null}
{
  "payload": { "...": "the agent payload for this phase" },
  "context": {
    "system_context": { "...": "user, tenant, run, and channel information" },
    "request_context": { "...": "request-scoped context variables" },
    "plugin_context": { "action": "ALL" }
  }
}
```

* **`payload`** carries the data the plug-in acts on: the incoming `messages` for `agent_pre_invoke`, or the generated response for `agent_post_invoke`. This is the same information as `AgentPreInvokePayload` and `AgentPostInvokePayload`.
* **`context`** is the same context a Python plug-in receives, organized into `system_context` (user, tenant, run, and channel), `request_context` (request-scoped context variables), and `plugin_context` (the plug-in action).

The plug-in returns an `AgentPostInvokeResult`:

```json theme={null}
{
  "continue_processing": true,
  "modified_payload": { "...": "the (optionally) modified payload" }
}
```

* `continue_processing` (required) — set to `false` to halt processing.
* `modified_payload` (optional) — the payload to use downstream. Omit it to leave the payload unchanged.

<Note>
  If the plug-in returns an error, times out, or omits a valid `continue_processing`, watsonx Orchestrate fails closed and stops processing, so unchecked content is never passed through.
</Note>

## Describe the plug-in in OpenAPI

Mark the operation that implements the plug-in with the `x-ibm-orchestrate-plugin` extension and set its `hook` to the phase. Declare a request body with `payload` and `context`, and a response with `continue_processing`:

```yaml YAML theme={null}
paths:
  /agent_pre_invoke:
    post:
      operationId: guardrail_pre
      description: Pre-invoke guardrail that masks prohibited words.
      x-ibm-orchestrate-plugin:
        hook: agent_pre_invoke          # or agent_post_invoke
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [payload, context]
              properties:
                payload: { type: object }
                context: { type: object }
      responses:
        "200":
          description: Plug-in result
          content:
            application/json:
              schema:
                type: object
                required: [continue_processing]
                properties:
                  continue_processing: { type: boolean }
                  modified_payload: { type: object }
```

When you import the tool, watsonx Orchestrate validates that the operation declares `payload` and `context` in its request body and `continue_processing` in its response. If the signature does not match, the import fails with a descriptive error, so problems are caught while authoring rather than at runtime.

## Import and attach the plug-in

Import the specification as an OpenAPI tool:

```bash BASH theme={null}
orchestrate tools import -k openapi -f path/to/guardrail-plugin.yaml
```

The `x-ibm-orchestrate-plugin.hook` extension marks the imported tool as a plug-in and records its phase.

Attach it to an agent the same way as a Python plug-in, under the section that matches its hook. Python and OpenAPI plug-ins can be mixed in the same agent:

```yaml YAML theme={null}
plugins:
  agent_pre_invoke:
      - plugin_name: guardrail_pre
  agent_post_invoke:
      - plugin_name: email_masking_plugin
```

<Warning>
  The plug-in's hook must match the section it is listed under, or the agent import fails with an error indicating which section to use.
</Warning>
