Agent frameworks

LangGraph, OpenAI Agents, CrewAI, Mastra, Vercel AI SDK, LlamaIndex, AutoGen, Semantic Kernel, Pydantic AI, Haystack, and n8n against Intertrace /v1, plus tool wrappers from the public SDK.

Let your coding agent set it up

Copy one prompt into Claude Code, Cursor, Codex or Copilot and it points your framework's model client at Intertrace, handles its answers and checks the result: Set up with AI.

Two paths

Mint itr_rt_* in Settings → Runtime API keys. Keep upstream secrets in the Provider vault; clients should not need vendor sk- keys.

LLM plane: point any OpenAI-compatible client at https://intertrace.fly.dev/v1. Tool plane: wrap tools with @intertrace-runtime/runtime so authorize + permits run before the body.

# LangGraph / LangChain: base_url="https://intertrace.fly.dev/v1"
# OpenAI Agents: set_default_openai_client + chat_completions
# CrewAI: LLM(..., base_url="https://intertrace.fly.dev/v1")
# LlamaIndex: api_base="https://intertrace.fly.dev/v1"
# Mastra / Vercel AI / Pydantic AI: OpenAI provider baseURL

TypeScript adapters: protectLangChainTool, protectOpenAIAgentsTool, protectVercelAiTool, protectMastraTool, protectCrewAiTool, protectLlamaIndexTool, protectAutoGenTool, protectSemanticKernelTool, protectPydanticAiTool. Google ADK / Claude Agent SDK / .NET use the /v1, /anthropic, /mcp, or /a2a planes, not npm wrappers yet.

LiteLLM · OpenAI SDK · SDKs.

Multi-step tasks: approve the plan

Whatever the framework, when one task has two or more steps that would each ask a person, wrap the task in runtime.plan(): one approval for the task, then the wrapped tools run as approved steps in this session. Steps outside the plan are decided as usual; payments, admin changes and outbound messages still ask each time.

await runtime.plan(
  {
    summary: "Refund order 1042 and tell the customer",
    steps: [
      { tool: "refunds_create", arguments: { order_id: "1042", amount: 40 } },
      { tool: "tickets_reply", arguments: { ticket_id: "T-77" } },
    ],
    ttlMinutes: 15,
  },
  () => agent.invoke({ input: "Refund order 1042 and tell the customer" })
);

The step names are your wrapped tools' names. Details and the Python form: SDKs.

A held call resumes once a person approves

When a model proposes a tool call that needs a person, the gateway removes the call from the answer and says so in words. Most frameworks read that as the final answer and end the run. The answer also carries the held call for software, in a top-level intertrace.held_calls field (call id, tool, exact arguments, approval id and link, expiry, session). The SDKs use it to give the call back to the framework's own approval step, wait for the person, and then run exactly that call through authorize with the approval id. Changed arguments never reuse the approval.

// Vercel AI SDK (6+): needsApproval → tool-approval-response
const held = new IntertraceHeldCalls(runtime, { tools: [refundDefinition] });
const model = createOpenAICompatible({ name: "intertrace", baseURL, apiKey, fetch: held.fetch() });
const tools = withIntertraceApprovals(held, { issue_refund: refundTool });
const first = await generateText({ model: model("gpt-4o-mini"), tools, messages });
messages.push(...first.response.messages);
const answer = await answerIntertraceApprovals(held, first.content); // waits for the person
if (answer) messages.push(answer);
const done = await generateText({ model: model("gpt-4o-mini"), tools, messages });
# OpenAI Agents SDK (Python): needs_approval → RunState.approve / reject
held = IntertraceHeldCalls(runtime, {"issue_refund": {"category": "payment", "action_type": "write", "data_boundary": "external"}})
client = AsyncOpenAI(base_url="https://intertrace.fly.dev/v1", api_key=key, http_client=held.async_http_client())
agent = Agent(..., tools=[guard_openai_agents_tool(held, issue_refund)],
              model=OpenAIChatCompletionsModel(model="gpt-4o-mini", openai_client=client))
result = await Runner.run(agent, task)
state = await answer_openai_agents_interruptions(held, result)  # waits for the person
if state is not None:
    result = await Runner.run(agent, state)

# LangGraph: interrupt() → Command(resume=...)
graph = create_react_agent(ChatOpenAI(..., http_client=held.http_client()),
                           [guard_langchain_tool(held, issue_refund)], checkpointer=InMemorySaver())
out = graph.invoke({"messages": [("user", task)]}, config)
resume = answer_langgraph_interrupts(held, out.get("__interrupt__"))
if resume:
    out = graph.invoke(Command(resume=resume), config)

Haystack

Point OpenAIChatGenerator at the edge with api_base_url and pass the runtime key as its API key. Every model call the pipeline or agent makes then goes through Intertrace.

import os
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.utils import Secret

llm = OpenAIChatGenerator(
    model="gpt-4o-mini",
    api_key=Secret.from_env_var("INTERTRACE_RUNTIME_API_KEY"),  # itr_rt_…
    api_base_url="https://intertrace.fly.dev/v1",
)

There is no Haystack tool wrapper. The tool calls the model proposes are still checked by the gateway's tool-call gate on the way back.

n8n

  1. 1

    Create an OpenAI credential

    In n8n, open Credentials, add an OpenAI credential and use your itr_rt_… runtime key as the API key.
  2. 2

    Set the Base URL

    Set Base URL to https://intertrace.fly.dev/v1 and save.
  3. 3

    Use it for the model

    Select the credential in the OpenAI Chat Model attached to your AI Agent node, and pick a model that GET /v1/models lists.

Make and Zapier are not covered: their OpenAI steps can't point at a custom base URL.