> For the complete documentation index, see [llms.txt](https://ai.ortusbooks.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ai.ortusbooks.com/main-components/gateways.md).

# Gateways

A **gateway** is a bidirectional human-interaction adapter. It translates platform events (a CLI keystroke, an HTTP webhook, a chat-platform button click) into normalized agent input, and translates agent events — including a suspended [human-in-the-loop](/main-components/human-in-the-loop.md) approval — back into a platform-native experience.

It is more than a message transport: a gateway also handles identity, threads, interactive actions, streaming, approvals, and resuming suspended runs.

## 🚀 Resolving a Gateway

```javascript
cli  = aiGateway( "cli" )
http = aiGateway( "http", { secret: "shared-hmac-secret" } )
mock = aiGateway( "mock" )
```

| BIF                          | Purpose                                                        |
| ---------------------------- | -------------------------------------------------------------- |
| `aiGateway( name, options )` | Resolve a gateway — a core name, or one registered by a module |
| `aiGatewayRegistry()`        | The singleton registry external gateway modules register into  |

Attach one to HITL middleware and approvals are presented there:

```javascript
import bxModules.bxai.models.middleware.core.HumanInTheLoopMiddleware;

agent = aiAgent(
    tools       : [ deployTool ],
    middleware  : [ new HumanInTheLoopMiddleware(
        toolsRequiringApproval: [ "deploy" ],
        gateway               : aiGateway( "http", { secret: getSecret() } )
    ) ],
    checkpointer: aiMemory( "cache" )
)
```

## 📦 Core Gateways

| Gateway | Capabilities                                           | Use it for                                                             |
| ------- | ------------------------------------------------------ | ---------------------------------------------------------------------- |
| `cli`   | `humanApproval`                                        | The reference implementation — a blocking stdin/stdout approval prompt |
| `http`  | `inboundMessages`, `outboundMessages`, `humanApproval` | Network-reachable approvals via signed webhooks                        |
| `mock`  | `inboundMessages`, `outboundMessages`, `humanApproval` | Tests and examples — scriptable, fully offline                         |

### CliGateway

Prints an approval banner and reads a decision from stdin. It resolves **synchronously**, so the run blocks rather than suspending. Offers approve / approve-always / approve-for-session / reject, plus quit as an always-available escape hatch, and re-prompts up to three times on unrecognised input before cancelling.

```javascript
// Attached automatically when no gateway is supplied
agent = aiAgent( middleware: [ new HumanInTheLoopMiddleware( toolsRequiringApproval: [ "deleteRecord" ] ) ] )
```

### HttpGateway

The first gateway with real network exposure, so every inbound mutation is signed (HMAC-SHA256), timestamp-bounded and nonce-deduplicated; every pending interaction has a TTL; and resolving a decision is an **atomic claim**, so a duplicate decision POST for an already-resolved interaction is rejected rather than silently overwriting it.

```javascript
http = aiGateway( "http", {
    secret           : getSecret(),   // shared HMAC secret
    callbackUrl      : "https://ops.example.com/bxai-events",  // optional
    toleranceSeconds : 300,           // max clock skew / request age
    requestTTLSeconds: 900            // how long an interaction stays open
} )
```

It exposes three endpoints through the `gateway.bxm` front controller:

| Method | Route                                      | Purpose                    |
| ------ | ------------------------------------------ | -------------------------- |
| `POST` | `/bxai/gateways/{gatewayName}/events`      | Inbound platform event     |
| `GET`  | `/bxai/interactions/{requestID}`           | Poll a pending interaction |
| `POST` | `/bxai/interactions/{requestID}/decisions` | Submit a human's decision  |

{% hint style="warning" %}
`HttpGateway` does **not** call `agent.resume()` itself — that would require knowing which agent and checkpointer an interaction belongs to, which is application state this module doesn't own. Set `GatewayContext.threadID` when presenting a request; it is persisted alongside the interaction and returned by both the GET and the decision POST, so your application can correlate the decision back to `agent.resume( decision, threadID )`.
{% endhint %}

### MockGateway

The reference implementation for tests — records what was delivered and lets you script decisions, with no I/O at all.

```javascript
mock = aiGateway( "mock" )
mock.setScriptedDecisions( [ "approve", "reject" ] )

// Or resolve a pending interaction out-of-band, like an async gateway would
mock.simulateDecision( requestID, "approve" )
```

## 🧩 Capabilities

A gateway declares only what it actually supports. Every capability method on `IGateway` has a safe default — a failed `GatewayDeliveryResult`, or a thrown `GatewayCapabilityNotSupported` — so implementations stay small.

```javascript
gateway = aiGateway( "http" )

gateway.getCapabilities()             // [ "inboundMessages", "outboundMessages", "humanApproval" ]
gateway.supports( "humanApproval" )   // true
gateway.supports( "streaming" )       // false
```

| Capability           | Meaning                                            |
| -------------------- | -------------------------------------------------- |
| `inboundMessages`    | Can parse platform payloads into agent input       |
| `outboundMessages`   | Can deliver agent events to the platform           |
| `streaming`          | Can deliver response deltas as they arrive         |
| `threads`            | Understands platform-native threading              |
| `attachments`        | Can send/receive files                             |
| `messageEditing`     | Can edit a message it already sent                 |
| `interactiveActions` | Supports native buttons/actions                    |
| `humanApproval`      | Can present a HITL approval and collect a decision |
| `argumentEditing`    | Can collect corrected tool arguments               |
| `authentication`     | Verifies inbound payload authenticity              |

Check `supports()` before calling a capability method rather than relying on the fallback.

## ⚙️ Configuration

Per-gateway settings live under `settings.gateways`, keyed by gateway name:

```json
{
  "modules": {
    "bxai": {
      "settings": {
        "gateways": {
          "http": { "secret": "...", "requestTTLSeconds": 900 }
        }
      }
    }
  }
}
```

## 🌍 External Gateway Modules

Platform gateways — Slack, Discord, Teams, Telegram — ship as **their own modules**, not in bx-ai core. A module registers its gateway when it loads, and `aiGateway()` finds it by name:

```javascript
// Inside the gateway module's ModuleConfig onLoad()
aiGatewayRegistry().register( new MyPlatformGateway(), "bx-ai-gateway-myplatform" )

// Anywhere in your application
gateway = aiGateway( "myplatform" )
```

Registry keys are `name` or `name@module`, so two modules can provide same-named gateways without colliding:

```javascript
aiGatewayRegistry().has( "myplatform" )
aiGatewayRegistry().get( "myplatform@bx-ai-gateway-myplatform" )
aiGatewayRegistry().listGateways()
aiGatewayRegistry().unregisterByModule( "bx-ai-gateway-myplatform" )
```

If nothing is registered under the name, `aiGateway()` falls back to treating it as a directly-instantiable class path, and throws `GatewayNotSupported` if that fails too.

## 🛠️ Building a Custom Gateway

Extend `BaseGateway`, declare your capabilities, and override only the methods you support.

```javascript
import bxModules.bxai.models.gateway.BaseGateway;
import bxModules.bxai.models.gateway.contracts.GatewayDeliveryResult;

class extends="BaseGateway" {

    function init() {
        variables.name        = "myplatform"
        variables.description = "Presents approvals as MyPlatform cards"
        return this
    }

    array function getDeclaredCapabilities() {
        return [ "humanApproval", "outboundMessages", "interactiveActions" ]
    }

    GatewayDeliveryResult function requestHumanInteraction( required humanRequest, required context ) {
        // Shared title/body/button vocabulary — don't re-derive it per platform
        var presentation = this.presentInteraction( humanRequest )

        var messageId = myPlatformClient.postCard(
            title  : presentation.getTitle(),
            body   : presentation.getBody(),
            buttons: presentation.getButtons()
        )

        // Async: return once accepted; the decision arrives later as an inbound event
        return new GatewayDeliveryResult( deliveryID: messageId )
    }

    function parseHumanDecision( required event ) {
        // Turn the platform's button-click payload into a HumanInteractionDecision
    }
}
```

**`presentInteraction( humanRequest, maxBodyLength )`** gives you a normalized title, body, and one button per allowed decision — including labels and styles for `approve_always`/`approve_session` — so every chat-native gateway renders consistently. **`presentResolution( decision )`** gives you a short summary ("Approved by alice", "Rejected: not now") for the follow-up message.

For a gateway whose pending state must outlive the process, override `setCheckpointer()`; `HumanInTheLoopMiddleware.onAttach()` passes the owning agent's checkpointer automatically.

## 🎯 Events

| Event                         | Fired when                                                          |
| ----------------------------- | ------------------------------------------------------------------- |
| `onGatewayCreate`             | `aiGateway()` resolves or creates a gateway                         |
| `onGatewaySessionCreate`      | `aiGatewaySession()` constructs a session                           |
| `onGatewayRegistryRegister`   | A gateway is registered into the registry                           |
| `onGatewayRegistryUnregister` | A gateway is removed                                                |
| `onGatewayConnect`            | A gateway's `start()` makes a real not-running → running transition |
| `onGatewayDisconnect`         | A gateway's `stop()` makes a real running → not-running transition  |
| `onGatewayMessageReceived`    | A gateway's `parseInbound()` parses an inbound message              |
| `onGatewayMessageSent`        | A gateway's `deliver()` sends an outbound message                   |

See [Event System](/advanced/events.md).

## Related Pages

* [Gateway Sessions](/main-components/gateway-sessions.md) — wiring an agent to inbound gateway traffic
* [Human-in-the-Loop](/main-components/human-in-the-loop.md) — approvals, policies, durable grants
* [aiGateway()](/advanced/reference/built-in-functions/aigateway.md) · [aiGatewayRegistry()](/advanced/reference/built-in-functions/aigatewayregistry.md) · [aiGatewaySession()](/advanced/reference/built-in-functions/aigatewaysession.md)
* [Security Guide](/advanced/security.md) — signing, secrets, and network exposure


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ai.ortusbooks.com/main-components/gateways.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
