Skip to main content

Overview

An OpenAI API endpoint gives your agent a base URL that speaks the OpenAI protocol. Anything that already knows how to talk to OpenAI can talk to your agent — you paste the URL where the tool asks for an OpenAI base URL, paste an API key, and your agent shows up as a model. That covers, among others:
  • n8n — the OpenAI credential’s Base URL field, then any OpenAI node
  • OpenRouter — adding your agent as a private / BYOK model
  • Cursor, Continue, LibreChat, Open WebUI — custom OpenAI-compatible provider
  • OpenAI SDKsOpenAI(base_url=…, api_key=…) in Python, JS, and the rest
  • LangChain / LlamaIndex — any ChatOpenAI-style client that accepts a base URL
Create as many endpoints as you need. Each has its own URL, its own settings, and its own processing pipeline — so a workflow automation and a customer-facing integration can point at the same agent with completely different instructions and limits. To add one, click Add Endpoint, give it a name, and click Save. Each card has an ON / OFF toggle, drag-to-reorder, and a delete button. Every endpoint speaks both protocol families, always — there is nothing to choose:
  • Chat Completions (/chat/completions, plus the legacy /completions) — the classic protocol most integrations use.
  • Responses (/responses) — OpenAI’s newer stateful protocol, with server-stored responses, previous_response_id chaining, background mode and caller-supplied function tools.
Point any client at the same base URL; whichever route it calls, the same agent answers.

Connecting a client

Two values go into the client: the Base URL from the card, and an API key from your API Keys page.
Subscribers see the same endpoints with their own personal Base URL — the same address with one extra segment, …/v1/agents/youragentid/yoursubscriberid/yourtokenhere — which works only with that subscriber’s own API key. Do not add /chat/completions to the base URL — the client appends the route itself. If a tool asks for the “full endpoint URL” rather than a base URL, that is the one case where you add /chat/completions yourself. The same call from the most common clients:

Streaming

Set "stream": true and the answer arrives as standard SSE chunks; add stream_options.include_usage for a final usage chunk:

Sending an image

Message content can be multi-part, exactly like OpenAI’s vision format — useful when the agent should read a photo, screenshot or scanned document:

Routes

Every endpoint answers all of these routes under its base URL:

Authentication

Use any API key from your API Keys page:
The key must belong to the same agent as the endpoint. A key for a different agent is rejected as an invalid key. Limits: 50 MiB per request, 60 requests per minute per key, and 5 concurrent requests per key. This budget is shared with your agent’s other API surfaces, so a key can’t get more by switching between them.

The model name

There is nothing to configure. The endpoint advertises your agent’s name in /models (for clients with a model picker), and the model a caller sends is recorded and then ignored — default, the agent name, a typo, anything at all still gets an answer from your agent. The model value does not pick a model. Which LLM actually answers is decided by your agent’s own LLM Routing settings.
The Advertised context window and Advertised max output fields are informational. Some clients (OpenRouter, LiteLLM) display them or use them to decide when to truncate. They don’t change what the model actually accepts — that comes from whichever model your agent routes to.

The Responses API

Every endpoint implements the stateful Responses protocol the way api.openai.com does:
  • Stored responses. Responses are stored by default ("store": false opts out) and stay retrievable for 30 days at GET /responses/{id}.
  • Conversation chaining. Pass previous_response_id and the agent sees the whole prior exchange — you send only the new input. Or pass conversation: "your-thread-id" and the endpoint threads turns implicitly under that id. A stored response is only visible to the API-key user that created it, on the endpoint that created it.
  • Caller function tools. Send tools: [{type: "function", …}] and the agent can call your functions: the turn ends with a function_call output item, you execute the function, and you continue with previous_response_id plus a function_call_output input item — the standard OpenAI tool loop. The agent’s own server-side tools keep running invisibly as usual; tool_choice (auto/none/required/a specific function) and parallel_tool_calls are honoured.
  • Background mode. "background": true returns immediately with "status": "queued"; poll GET /responses/{id} for the result, or cancel it with POST /responses/{id}/cancel.
  • Structured output. text.format with json_object or a json_schema instructs the agent to answer as pure JSON (best-effort — the schema is enforced by instruction, not by a grammar).
  • The rest of the surface. instructions (applied per call, never inherited across a chain), metadata (up to 16 keys, echoed back), max_output_tokens (a real ceiling for the turn), include, stream_options.include_obfuscation, truncation, service_tier, safety_identifier and user are all accepted; sampling knobs (temperature, top_p, top_logprobs) are echoed back but the agent’s own LLM Routing settings decide actual sampling. prompt templates and hosted tool types (web_search, file_search, …) are refused with a clear error — your agent brings its own tools.

Responses examples

Multi-turn with previous_response_id — send only the new input; the endpoint replays the rest:
Your own function tools — the standard OpenAI tool loop, with your function running on your side:
Background mode — kick off long work, poll for the result:
Structured output — ask for JSON your automation can parse directly:

Conversations

With Chat Completions the protocol is stateless: the client sends the whole conversation on every call, and that history is what the agent reads. So multi-turn works out of the box — keep appending to messages the way you would with OpenAI. With the Responses API, use previous_response_id or conversation instead (see above). For the Inbox, consecutive calls that begin with the same first message are grouped into one thread, so a conversation appears as one entry instead of one per turn. To control that grouping explicitly, send a chat id: An id may be up to 128 characters of letters, digits and . _ : @ + -. This is useful when each run of a workflow should be its own thread — n8n’s execution id makes a good chat id. Two calls with the same id land in the same conversation:

Tool activity in responses

Your agent runs its tools server-side; a caller never sees tool_calls. Show tool activity in the answer decides whether that work is visible:
  • On — tool use appears inline as italic lines (_searching the knowledge base…_), the way a chat UI shows it. Good for anything a person reads.
  • Off — the response is only the final answer. Usually what you want for an automation that parses the output.

Processing pipeline

Each endpoint card exposes the shared incoming-event pipeline. Every request flows through these stages before the agent sees it. By default every request reaches the agent. The stages below exist to change that. Because the caller is waiting on an open connection, an endpoint always answers: if a stage decides not to run the agent, the caller receives a standard OpenAI error rather than an empty completion or a hang.

Event pre-processor

Optional code that runs in a sandbox before the classifier and the agent. Define handle(event, ctx) and return a verdict — pass_to_agent, drop, run_tool, or respond. event.payload is the parsed request body ({ model, messages, … }), so you can inspect or rewrite the whole conversation. A pass_to_agent verdict returning { messages: [...] } replaces the history the agent sees — that is how you redact, truncate or rewrite an incoming conversation. A respond verdict answers the caller directly without an agent run, which is a cheap way to serve canned replies. See the Event pre-processor guide for the full contract and worked examples.

Interpreters

When one endpoint serves several different callers or workflows, Interpreters recognise each kind of request and inject a plain-language hint so the agent knows what it’s looking at. Each interpreter matches on JSON field presence (dot-paths, e.g. metadata.workflow) or regex against the raw request body, tested in order — first match wins. See the Interpreters guide for the matching rules and examples.

Event classifier

Optional. When enabled, an LLM reads each request before the agent runs and picks React (answer it) or Drop (refuse it). Off → every request goes straight to the agent. Give it a prompt describing what this endpoint is for, e.g. “Only answer questions about our products; drop anything else.” A dropped request gets HTTP 403 with "code": "request_blocked", so the caller knows it was refused rather than failing silently.

Context and Notes

  • Context — instructions for the agent that apply to every call on this endpoint (e.g. “Callers here are n8n workflows. Answer in one short paragraph, no markdown.”). Added to the agent’s context for every request.
  • Notes — private operator notes, never seen by the agent.
Context is the field that makes two endpoints on the same agent behave differently: one can answer conversationally for a chat UI while another returns terse, parseable output for an automation.

Spend budget

Owner-only advanced control.
Each endpoint can carry its own AI spend cap so a runaway workflow can’t drain your credits. Set a Spend cap (credits) and a Budget periodDaily, Weekly (Mon–Sun), or Monthly (calendar month). Once the cap is reached, callers receive HTTP 429 with "code": "endpoint_budget_exceeded" until the period resets at UTC boundaries. Leave the cap at 0 for no limit.

Errors

Errors use the standard OpenAI envelope, so client libraries raise their normal exceptions:

Testing your endpoint

Each card has a Test section. Pick a route, paste a request body, add an optional chat id, and click Run Test. The request goes through the real pipeline — interpreters, pre-processor and classifier all run — and the panel shows:
  • which interpreter matched, what the pre-processor and classifier decided, and the resulting outcome;
  • the exact response the caller would receive, including the HTTP status;
  • the exact prompt and data the agent receives — the context block added to its system prompt, any pipeline hint, and the full message array.
The agent itself is not run and nothing is written to the conversation. Two things do happen for real, so the result is a rehearsal rather than a guess: your pre-processor executes (any side effects it has will occur) and the classifier is a real LLM call (it costs credits). Recent traffic appears in Recent API Requests on this page — time, endpoint, route, requested model, source IP, status, outcome and detail — and in the Inbox, as the conversation each request belonged to.

Setting it up in n8n

  1. In n8n, add an OpenAI credential.
  2. Set API Key to your Agentheya API key.
  3. Set Base URL to the endpoint’s Base URL (no /chat/completions).
  4. In any OpenAI node, pick your agent from the model dropdown (n8n loads the list from the endpoint) — or type any value; the model name never changes which agent answers.
For a chat-style workflow, send n8n’s execution id as X-Chat-Id so each run is its own conversation in the Inbox.

Adding it to OpenRouter

Use OpenRouter’s BYOK / private model flow, pointing it at the endpoint’s Base URL with your Agentheya API key. Use your agent’s name as the model id (any value works). OpenRouter probes /models to validate the provider, which the endpoint answers with your single model and its advertised context window.