Controller API
The Controller API is the main integration point for LLM applications. It accepts message conversations and returns analysis results from the configured modules.
A live OpenAPI reference is available at api.link2.ai/docs.
Integration modes
As a separate service
Your application calls the LLM as usual and sends a copy of the messages to the LINK2AI.Trust Controller API in a separate request. Results are available synchronously or stored for later review. See the Analysis API.
As an OpenAI proxy
LINK2AI.Trust acts as a drop-in replacement for the OpenAI base URL. It forwards requests to OpenAI, analyzes both the input and output, and returns the LLM response alongside analysis results. Modules configured in blocking mode can reject requests before they ever reach the LLM. See the OpenAI Proxy API.
Authentication
Every request to the Controller API must include a valid API key. Keys are created in the Portal under Settings → API Keys and are scoped to a project.
Two authentication methods are supported:
Bearer token (standard — used with the Analysis API):
Authorization: Bearer l2-<key_id>-<secret>
Custom header (used alongside OpenAI's Authorization header in proxy mode):
LINK2AI_API_KEY: l2-<key_id>-<secret>
Analysis API
Use the Analysis API when your application calls the LLM directly and you want to send the messages to LINK2AI.Trust separately.
POST /v1/analyzeInput
Analyzes the user-facing side of a conversation. Runs input-focused modules (malicious_intent, guardrails, harmful_content, pii, secrets, usage).
Call this before sending the request to the LLM.
Headers
| Header | Value |
|---|---|
Authorization |
Bearer <api_key> |
Content-Type |
application/json |
Request body
{
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "How do I reset my password?" }
],
"model": "gpt-4o-mini",
"configuration": { ... },
"cache": {
"no_cache": false,
"no_store": false
}
}
| Field | Type | Required | Description |
|---|---|---|---|
messages |
array | Yes | Full conversation in OpenAI message format (system, user, assistant, tool roles). |
model |
string | Yes | The model identifier used for the LLM call (e.g. gpt-4o-mini). |
configuration |
object | No | Per-module configuration that overrides the project's default settings. See Module Configuration. If omitted, the project's saved configuration is used. |
cache.no_cache |
bool | No | Skip reading from the analysis cache. Default: false. |
cache.no_store |
bool | No | Skip writing results to the analysis cache. Default: false. |
Response body
{
"results": {
"malicious_intent": {
"duration_ms": 45.2,
"status": "success",
"secure": true,
"result": {
"message_labels": [
{ "label": "SAFE", "score": 0.998 }
]
},
"cache_hit": null,
"error_message": null
},
"usage": {
"duration_ms": 1.1,
"status": "success",
"secure": null,
"result": {
"input_tokens": 24,
"output_tokens": null,
"messages": [ ... ],
"model": "gpt-4o-mini"
},
"cache_hit": null,
"error_message": null
}
},
"module_errors": false,
"secure": true
}
| Field | Type | Description |
|---|---|---|
results |
object | Map of module ID → ModuleResult. Only modules in Response or Blocking mode appear here. Modules in Monitoring mode run in the background and are never in this map. |
module_errors |
bool | true if any module returned a failure status. |
secure |
bool | false if any security module flagged the interaction. |
ModuleResult fields
| Field | Type | Description |
|---|---|---|
status |
"success" | "failure" |
Whether the module ran without error. |
duration_ms |
float | null | Module execution time in milliseconds. |
secure |
bool | null | false if this module flagged the interaction. null for non-security modules (e.g. usage). |
result |
object | null | Module-specific result payload. See Modules. null on failure. |
cache_hit |
object | null | Present if the result was served from cache. Contains event_id (string) and similarity (float | null). |
error_message |
string | null | Error details when status is "failure". |
Status codes
| Code | Meaning |
|---|---|
200 |
Analysis completed. |
400 |
No modules are configured for this project. |
401 |
Missing or invalid API key. |
POST /v1/analyzeOutput
Analyzes the LLM response side of a conversation. Runs output-focused modules (adherence, guardrails, harmful_content, pii, secrets, usage).
Call this after receiving the LLM response. Include the assistant message in the messages array.
Request and response structure are identical to /v1/analyzeInput.
Python example
import os
import httpx
API_KEY = os.environ["LINK2AI_API_KEY"]
BASE_URL = "https://api.link2.ai"
headers = {"Authorization": f"Bearer {API_KEY}"}
messages = [
{"role": "system", "content": "You are a helpful assistant for a car rental company."},
{"role": "user", "content": "What cars do you have available?"},
]
# Analyze input before calling the LLM
input_analysis = httpx.post(
f"{BASE_URL}/v1/analyzeInput",
json={"messages": messages, "model": "gpt-4o-mini"},
headers=headers,
).json()
if not input_analysis["secure"]:
raise ValueError("Input blocked by security modules")
# ... call the LLM, get assistant_response ...
# Analyze output after receiving the LLM response
messages_with_response = messages + [{"role": "assistant", "content": assistant_response}]
output_analysis = httpx.post(
f"{BASE_URL}/v1/analyzeOutput",
json={"messages": messages_with_response, "model": "gpt-4o-mini"},
headers=headers,
).json()
OpenAI Proxy API
The proxy API is a drop-in replacement for OpenAI's API. It forwards your request to OpenAI, analyzes both the input and output, and returns the normal OpenAI response with input_analysis and output_analysis fields appended.
Modules configured in blocking mode will reject the request before it reaches OpenAI and return HTTP 422.
POST /openai/chat/completions
Proxies to the OpenAI Chat Completions API.
Headers
| Header | Value |
|---|---|
Authorization |
Bearer <openai_api_key> |
LINK2AI_API_KEY |
<link2ai_api_key> |
Content-Type |
application/json |
The request body is passed through to OpenAI unchanged. The response is the standard OpenAI ChatCompletion object with two additional fields:
{
"id": "chatcmpl-...",
"object": "chat.completion",
"choices": [ ... ],
"usage": { ... },
"input_analysis": {
"results": { ... },
"module_errors": false,
"secure": true
},
"output_analysis": {
"results": { ... },
"module_errors": false,
"secure": true
}
}
Streaming
Streaming requests ("stream": true) are supported. When streaming, only input_analysis is included (attached to the first SSE chunk). Output analysis is not available in streaming mode.
Status codes
| Code | Meaning |
|---|---|
200 |
LLM call succeeded and analysis is included. |
422 |
A blocking module flagged the input. The response body is the AnalyzeResponse for the blocked analysis. The LLM is never called. |
401 |
Missing or invalid LINK2AI API key. |
POST /openai/responses
Proxies to the OpenAI Responses API. Same authentication, extension fields, and blocking behavior as /openai/chat/completions.
Python example (OpenAI SDK)
The OpenAI Python SDK works without any changes — just point base_url at the Controller and add the LINK2AI_API_KEY header.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.link2.ai/openai",
api_key=os.environ["OPENAI_API_KEY"],
default_headers={"LINK2AI_API_KEY": os.environ["LINK2AI_API_KEY"]},
)
response = client.chat.completions.create(
messages=[
{"role": "system", "content": "Always speak like a pirate."},
{"role": "user", "content": "Hello!"},
],
model="gpt-4o-mini",
)
print(response.choices[0].message.content)
# Access analysis results
input_results = response.input_analysis["results"]
output_results = response.output_analysis["results"]
# Check if a guardrail was violated
if not response.output_analysis["secure"]:
print("Output blocked by a security module")
Service endpoints
GET /status
Returns the health and availability of all configured modules. Useful for monitoring and debugging.
{
"modules": {
"malicious_intent": {
"module_id": "malicious_intent",
"available": true,
"response_time": 38.5
},
"adherence": {
"module_id": "adherence",
"available": false,
"response_time": null
}
},
"time": "2026-05-04T12:00:00Z"
}
response_time is the measured latency in milliseconds from a health-check probe, or null if the module could not be reached.
GET /info
Returns basic service metadata.
{
"controller_id": "abc123",
"loglevel": "INFO",
"version": "1.4.0"
}