When to use proxy?
Proxying your requests to Contextual AI is highly recommended for production deployments to protect your account.
- Secure your API key — proxying keeps your API key (secret) out of the browser.
- Enforce authentication — validate user sessions before forwarding requests.
- Route dynamically — hardcode or select agent IDs on server side, dynamically.
- Rate limit on your end — add additional layers of rate limiting using your tech stack.
The only exception is when using OIDC (enterprise plans only, contact us). In that case, tokens can be passed directly in the client fetcher — though a proxy still works if you prefer centralized control.
Transforming the URL
Use transform to rewrite the target path based on the incoming request, such as when agent ID needs to be extracted from the query string, or when you need to set agent ID dynamically based on the user's authentication state.
ts
import { createOryxSSEProxy } from "@contextualai/oryx-proxy-node";
export const POST = createOryxSSEProxy({
baseUrl: "https://api.contextual.ai",
transform: (request) => {
const url = new URL(request.url);
const agentId = url.searchParams.get("agentId");
if (!agentId) throw new Error("Missing agentId");
// /api/chat?agentId=123 -> /v1/agents/123/query.
return { url: `/v1/agents/${agentId}/query` };
},
extendHeaders: () => ({
Authorization: `Bearer ${process.env.CONTEXTUAL_API_KEY}`,
}),
});
Extending headers
extendHeaders injects headers into the upstream request. You can use this function to validate your user's authentication state, or inject Contextual AI API key conditionally.
ts
import { createOryxSSEProxy } from "@contextualai/oryx-proxy-node";
export const POST = createOryxSSEProxy({
baseUrl: "https://api.contextual.ai",
transform: (request) => ({
url: `/v1/agents/${process.env.CONTEXTUAL_AGENT_ID}/query`,
}),
extendHeaders: async (request) => {
// Example: inject user's OIDC token.
const userToken = request.headers.get("x-user-token");
if (!userToken) throw new Error("Unauthorized");
return {
Authorization: `Bearer ${userToken}`,
};
},
});
Custom error mapping
Override mapErrorBody to customize error event payloads.
ts
import { createOryxSSEProxy } from "@contextualai/oryx-proxy-node";
export const POST = createOryxSSEProxy({
baseUrl: "https://api.contextual.ai",
transform: (request) => ({
url: `/v1/agents/${process.env.CONTEXTUAL_AGENT_ID}/query`,
}),
extendHeaders: () => ({
Authorization: `Bearer ${process.env.CONTEXTUAL_API_KEY}`,
}),
mapErrorBody: (rawBody, status) => {
const parsed = typeof rawBody === "string" ? JSON.parse(rawBody) : {};
return {
status,
message: parsed.reason ?? "Something went wrong.",
error_code: parsed.code,
};
},
});
Full control with createOryxSSEProxyHandler
createOryxSSEProxy handles most use cases out of the box. But you can still fully customize when you need:
- Dynamic URL routing based on request parameters.
- Custom authentication or header injection.
- Non-standard error response formatting.
- Full control over the upstream request.
ts
import { createOryxSSEProxyHandler } from "@contextualai/oryx-proxy-node";
export async function POST(request: Request): Promise<Response> {
const body = await request.json();
const oidcToken = request.headers.get("x-oidc-token");
const agentId = request.headers.get("x-agent-id");
const handler = createOryxSSEProxyHandler({
buildUpstreamRequest: async () => ({
url: `https://api.contextual.ai/v1/agents/${agentId}/query`,
method: "POST",
headers: {
Authorization: `Bearer ${oidcToken}`,
"Content-Type": "application/json",
Accept: "text/event-stream",
},
data: {
...body,
// Inject additional fields with your specific needs.
metadata: { source: "web" },
},
}),
});
return handler(request);
}
API reference: createOryxSSEProxy
-
baseUrl — Base URL for the upstream API.
string
-
transform — Rewrite the target path.
(request) => { url: string }
-
method — Override HTTP method. Defaults to request method.
string | (request) => string
-
extendHeaders — Inject headers into the upstream request.
(request) => HeadersInit | undefined
-
mapErrorBody — Customize error event shape.
(rawBody, status) => SSEErrorPayload
API reference: createOryxSSEProxyHandler
-
buildUpstreamRequest — Full axios config builder.
(request) => AxiosRequestConfig
-
mapErrorBody — Customize error event shape.
(rawBody, status) => SSEErrorPayload
© 2025 Contextual AI, Inc.
Proudly open sourced under the Apache 2.0 license.