# OpenAI's Agents API Just Hit Public Beta — Here's What Your Express Backend Needs to Know

* * *

I've been building a small side project this week that wires an AI agent into a Node.js API, and timing couldn't have been better — OpenAI just dropped their Agents API into public beta. If you've ever tried to roll multi-step AI workflows by hand in Express (context management, tool calls, retry logic, the whole mess), you'll understand why this is a big deal.

Let me walk you through what it actually does, how it fits into a MERN backend, and where it falls short.

* * *

## What the Agents API Actually Is

Most of us have used OpenAI's Chat Completions API — you send a message, you get a message back. Simple. But building anything that resembles an *agent* — something that plans steps, calls tools, checks its own output, and loops until a task is done — means you end up writing a lot of scaffolding code yourself.

The Agents API is OpenAI taking that scaffolding and hosting it as a managed service. You define your agent (its instructions, its tools, its model), you create a **thread** (the conversation context), and then you **run** the thread. OpenAI's infrastructure handles:

*   **Orchestration** — deciding when to call a tool vs. generate text
    
*   **Session management** — keeping context across multiple turns without you re-sending the whole history
    
*   **Context compaction** — automatically summarizing old context so you don't blow past token limits
    
*   **Compute integrations** — native hooks for Cloudflare Workers, Vercel, Modal, and DigitalOcean
    

The pricing model is clean too: you pay for model tokens and tool usage only. No per-session tax.

![](https://cdn.hashnode.com/uploads/covers/69d007f5e466e2b7625cd1df/9ddb4c2a-150c-4dee-87b6-6e8876a88e0c.png align="center")

* * *

## Wiring It Into Your Express Backend

Here's a minimal but real example. You have a route that takes a user's question, runs it through an agent with a custom tool, and streams back the result.

```bash
npm install openai
```

```javascript
// agents/researchAgent.js
import OpenAI from "openai";

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// Define your agent once — reuse across requests
export async function createResearchAgent() {
  return client.beta.agents.create({
    name: "Research Assistant",
    model: "gpt-4o",
    instructions:
      "You are a helpful research assistant. When asked a question, " +
      "use the provided tools to gather context, then write a concise answer.",
    tools: [
      {
        type: "function",
        function: {
          name: "search_knowledge_base",
          description: "Search our internal MongoDB knowledge base",
          parameters: {
            type: "object",
            properties: {
              query: { type: "string", description: "The search query" },
            },
            required: ["query"],
          },
        },
      },
    ],
  });
}
```

```javascript
// routes/agent.js
import express from "express";
import OpenAI from "openai";
import { searchKnowledgeBase } from "../db/mongo.js"; // your Mongoose query

const router = express.Router();
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

router.post("/ask", async (req, res) => {
  const { question, agentId } = req.body;

  // Create a thread (one per user session or conversation)
  const thread = await client.beta.threads.create();

  await client.beta.threads.messages.create(thread.id, {
    role: "user",
    content: question,
  });

  // Run the agent and poll until done
  let run = await client.beta.threads.runs.create(thread.id, {
    assistant_id: agentId,
  });

  // Handle tool calls the agent wants to make
  while (run.status === "requires_action") {
    const toolOutputs = [];

    for (const call of run.required_action.submit_tool_outputs.tool_calls) {
      if (call.function.name === "search_knowledge_base") {
        const args = JSON.parse(call.function.arguments);
        const results = await searchKnowledgeBase(args.query); // Mongoose query
        toolOutputs.push({
          tool_call_id: call.id,
          output: JSON.stringify(results),
        });
      }
    }

    run = await client.beta.threads.runs.submit_tool_outputs_and_poll(
      thread.id,
      run.id,
      { tool_outputs: toolOutputs }
    );
  }

  // Grab the final message
  const messages = await client.beta.threads.messages.list(thread.id);
  const answer = messages.data[0].content[0].text.value;

  res.json({ answer, threadId: thread.id });
});

export default router;
```

A few things worth noticing here. The `thread` is reusable — you can store `thread.id` in your MongoDB session document and resume the exact conversation later without resending history. The agent calls your MongoDB tool naturally, which means your existing Mongoose queries plug in without any translation layer.

* * *

## How This Compares to LangChain / LangGraph

A lot of MERN devs have gone down the LangChain road. It's powerful, but the abstraction layers stack up fast, and debugging a broken chain three levels deep is genuinely unpleasant.

The Agents API trades flexibility for simplicity:

|  | OpenAI Agents API | LangGraph |
| --- | --- | --- |
| Hosting | Managed (OpenAI infra) | Self-hosted or cloud |
| Vendor lock-in | High (OpenAI models only) | Low (any model) |
| Context management | Automatic compaction | Manual |
| Multi-agent graphs | Basic orchestration | Full graph control |
| Open source | No | Yes |

If you're building a quick internal tool or a product feature on top of OpenAI models, the Agents API saves you two or three days of plumbing. If you need model-agnostic runs, or you're wiring together five specialized agents in a DAG, LangGraph still wins.

* * *

## The Python and Blockchain Angles

Worth mentioning if you're also working in Python: the OpenAI Python SDK has feature parity with the JS one here. The same thread/agent/run pattern works identically, which is nice if you're splitting work between a Python data-processing service and a Node.js API layer. You can share `thread_id`s across both SDKs — the thread lives on OpenAI's servers, not in your process.

For blockchain folks: agent + tool combos are a natural fit for on-chain data lookups. You can expose a `get_wallet_balance` or `get_transaction_history` tool backed by an Ethers.js call, and the agent decides when to invoke it based on the user's question. Multi-step DeFi workflows — "check my balance, estimate gas, then tell me if this swap makes sense" — map cleanly onto the agent-run loop without you managing the decision tree.

![](https://cdn.hashnode.com/uploads/covers/69d007f5e466e2b7625cd1df/92b4654d-7e88-4348-b527-b694fd6902e9.png align="center")

* * *

## Practical Takeaways

A few things I'd actually do before dropping this into production:

**Store thread IDs in MongoDB.** Threads persist on OpenAI's servers and have an ID. Save it to your user document so conversations are resumable. A user switching devices picks up exactly where they left off.

**Add timeout handling.** Runs can hang if a tool call throws or takes too long. Set a max-poll loop — don't let an agent run block your Express route indefinitely.

**Watch your token costs.** Context compaction is automatic but not free. The API charges for the tokens in that summary pass. For high-volume apps, benchmark your average run cost before you go live.

**Use** `run.status` **defensively.** Runs can end in `failed`, `cancelled`, `expired`, or `incomplete` states — not just `completed`. Handle all of them.

```javascript
if (!["completed", "requires_action"].includes(run.status)) {
  return res.status(500).json({ error: `Run ended with status: ${run.status}` });
}
```

* * *

## Wrapping Up

The Agents API isn't magic — it's managed plumbing. But for MERN developers who want to ship AI features without becoming AI infrastructure engineers, it removes a genuine chunk of the work. The thread persistence alone is worth the try.

Give the public beta a spin. If you build something cool with it — especially anything hitting a MongoDB backend or an Ethereum node — drop it in the comments. I'm curious what people are actually shipping with this.
