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.

Deployment as separate service

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.

Deployment as LLM proxy


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"
}