> 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/readme/release-history/3.5.0.md).

# 3.5.0

BoxLang AI Module 3.5.0. A transport-agnostic gateway processing API, webhook verification handshakes, and a new minimum BoxLang runtime of 1.14.0.

**Released:** September 11, 2026

BoxLang AI 3.5.0 makes the gateway layer usable from any host framework. The request processing that sat behind `public/gateway.bxm` is now a set of **transport-agnostic statics** that take the pieces of a request and return a normalized result, so a framework with its own router (ColdBox, for example) can mount the gateway surface wherever it likes. It also adds the **verification handshake** that chat platforms perform before they start posting webhooks.

***

## ⚠️ Migration

**The minimum BoxLang runtime is now 1.14.0** (`box.json` `minimumVersion`, previously 1.8.0). BoxLang 1.13 and earlier resolve unqualified identifiers inside a `catch` body against the class `variables` scope before locals and `arguments`. BoxLang 1.14.0 fixed this. The module still qualifies its own catch reads, but middleware, tools and interceptors you write should not rely on older runtimes. The module now compiles and tests against BoxLang 1.17.0.

***

## ✨ New Features

### 🔌 Transport-Agnostic Gateway Processing

`GatewayRequestProcessor` now exposes four statics that touch no transport. Each takes the parts of a request and returns `{ statusCode, body, contentType, headers }`.

| Static                                                     | Purpose                                                                                            |
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `processInbound( gatewayName, rawBody, headers, session )` | Verify the signature, parse the body, and optionally dispatch each message into a `GatewaySession` |
| `processHandshake( gatewayName, params )`                  | Answer a platform's URL verification request                                                       |
| `readInteraction( requestID )`                             | Read one pending human-in-the-loop interaction                                                     |
| `submitDecision( requestID, rawBody, headers )`            | Verify the signature and submit the human decision for a pending interaction                       |

`processHttp()` is now a thin layer over the same statics, so the existing `/~bxai/gateway.bxm` surface is unchanged.

```javascript
import bxModules.bxai.models.gateway.http.GatewayRequestProcessor;

// In your own router or handler
result = GatewayRequestProcessor::processInbound(
    gatewayName: "my-platform",
    rawBody    : getHTTPRequestData().content,
    headers    : getHTTPRequestData().headers,
    session    : mySession
)
// result.statusCode, result.body, result.contentType, result.headers
```

**Pass the raw body unchanged.** Signatures are computed over the exact body string, so it must not be re-serialized before it reaches `processInbound()`.

**Dispatch, not just parse:** when you pass a `GatewaySession`, every parsed message is handed to `handleInbound()` and acknowledged with `202` immediately. The returned futures are deliberately never joined, because a platform webhook times out in seconds and an agent turn does not. The response reports which thread each message landed on:

```javascript
{ accepted: true, messages: [ { id: "...", threadId: "..." } ] }
```

A single-message event also sets an `X-Thread-Id` header, so a caller can correlate the reply that arrives later. Without a session, `processInbound()` parses only and returns `200` with the normalized messages, exactly as before.

**Documentation**: [Gateways](/main-components/gateways.md)

***

### 🤝 Verification Handshakes: `IGateway.verifyHandshake()`

Many platforms send a GET to your webhook URL before they start posting to it, to prove the URL is yours. Meta's WhatsApp Cloud `hub.challenge` echo is the classic example.

* Override `verifyHandshake( params )` in a gateway and declare the new `"verifyHandshake"` capability.
* The method returns a response (`statusCode`, `body`, `contentType`) rather than a boolean, because most handshakes echo a challenge back as plain text.
* The default throws `GatewayCapabilityNotSupported`, so only gateways whose platform performs a handshake need to override it.
* `processHandshake()` answers `405` for any gateway that does not declare the capability.
* `MockGateway` is the reference implementation.

***

### 🧵 `GatewaySession.resolveThreadId()` Is Public

`resolveThreadId( message, sourceGateway )` is pure and side-effect free, and it is now public. A caller that dispatches without waiting on the turn needs it to report where the work landed, and `getQueueDepth()` and `cancelRun()` are addressed by a thread id the caller has to be able to compute.

***

## 🪲 Fixed

* An inbound event whose body is not valid JSON is now rejected with `400` and a message naming the parse failure, instead of surfacing as a generic `500`.
* OpenAI compatible chat providers now fail with a `ProviderError` when a response omits `choices`, and use top-level `message` when present. This prevents intermittent Mistral tool-call crashes from `KeyNotFoundException`.

***

## 📖 Documentation

* [Gateways](/main-components/gateways.md)
* [Middleware: Human-in-the-Loop](https://github.com/ortus-boxlang/bx-ai-docs/tree/v2.x/main-components/middleware.md)


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://ai.ortusbooks.com/readme/release-history/3.5.0.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
