ChanlChanl
Technical Guide

The OpenAI Assistants API shuts down August 26

The OpenAI Assistants API sunsets on August 26, 2026. If you built a CX chatbot or support agent on it, here's what's actually changing, your migration options, and why this is the right time to build the architecture you should have had from the start.

DGDean GroverCo-founderFollow
June 2, 2026
16 min read
Architecture diagram showing migration path from OpenAI Assistants API to stateless Responses API with developer-owned state management layer

You have 85 days.

On August 26, 2026, the OpenAI Assistants API goes dark. Any application still calling the beta endpoints after that will get errors. OpenAI announced the sunset a year ago, and the deadline is now close enough that teams without a migration plan are starting to feel it.

If you built a CX chatbot, support agent, or internal assistant on the Assistants API, this is a real deadline. Missing it means customer-facing outages on a date you know in advance, which is arguably worse than an unexpected incident because there's no excuse.

This post covers what's actually changing, your migration options, and what the teams handling this well are using the forced migration to fix.

What the Assistants API was built to solve

The Assistants API launched in late 2023 as OpenAI's answer to a problem that every developer hit after function calling shipped: managing conversation state.

Function calling let your agent use tools. But tracking conversation history was still entirely your problem. You had to persist every message, pass the full conversation on every request, and build your own storage layer. For a two-turn demo, that was fine. For a production support agent handling 50-turn conversations with customers who come back days later, you were reinventing session management from scratch.

The Assistants API handled that for you. You created an Assistant (your system prompt, tools, and model configuration), created a Thread (a conversation), added Messages to it, and triggered a Run. OpenAI stored everything. You called client.beta.threads.runs.createAndPoll() and got responses back without managing any conversation state yourself.

That was genuinely useful. Teams shipped real products on it. The abstraction removed a category of infrastructure work that had nothing to do with building a good agent, and for many early-stage teams, that tradeoff made sense.

The problems showed up later, and they were mostly invisible until you looked for them.

What was actually happening inside your threads

The Assistants API managed your state in a way that hid exactly what was going into your prompts.

When you ran a thread, OpenAI decided how to handle the conversation history. If the thread got long, it truncated automatically. You didn't control what was included or excluded. Your agent might be responding without critical earlier context, and you would never know unless you inspected the run steps manually.

Some teams discovered this the hard way: customers would reference something they said 30 turns ago, the agent would have no idea what they were talking about, and the conversation would go badly. The support ticket would get escalated to a human, and the human would be confused too because they could only see the Thread, not what the model actually received as context.

The other issue was privacy surface area. Your conversation data, including anything customers told your agent, lived in OpenAI's infrastructure between calls. For a toy project, fine. For a healthcare provider or financial service, that is a compliance conversation you probably don't want to have.

The Responses API fixes both. You own state. You decide what goes into every prompt. And your data never lives in OpenAI's storage between requests.

Migration path 1: the Responses API

For most CX agents, the Responses API is the right migration target. It supports the same models, the same function calling, and the same file handling as the Assistants API. The difference is that you pass the conversation history with each request instead of a thread ID.

Here's the core pattern:

assistants-to-responses.ts·typescript
import OpenAI from "openai";
 
const client = new OpenAI();
 
// Assistants API: you pass a thread ID, OpenAI manages state
async function chatWithAssistant(userMessage: string, threadId: string) {
  await client.beta.threads.messages.create(threadId, {
    role: "user",
    content: userMessage,
  });
 
  const run = await client.beta.threads.runs.createAndPoll(threadId, {
    assistant_id: process.env.ASSISTANT_ID!,
  });
 
  const messages = await client.beta.threads.messages.list(threadId);
  return messages.data[0].content;
}
 
// Responses API: you pass the full history, you own state
interface Message {
  role: "user" | "assistant";
  content: string;
}
 
async function chatWithResponses(userMessage: string, history: Message[]) {
  const messages: Message[] = [
    ...history,
    { role: "user", content: userMessage },
  ];
 
  const response = await client.responses.create({
    model: "gpt-4o",
    input: messages,
    instructions: process.env.SYSTEM_PROMPT!,
    tools: getToolDefinitions(),
  });
 
  return {
    reply: response.output_text,
    updatedHistory: [
      ...messages,
      { role: "assistant" as const, content: response.output_text },
    ],
  };
}

The core change: you pass messages instead of a threadId. You own the conversation array, and you persist it between requests yourself.

You'll need a storage layer. For most CX applications, a Redis or DynamoDB key-value store keyed by session ID is all you need:

conversation-store.ts·typescript
import { Redis } from "@upstash/redis";
 
const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
 
const SESSION_TTL_SECONDS = 60 * 60 * 24; // 24-hour session window
 
export async function getConversationHistory(
  sessionId: string
): Promise<Message[]> {
  const history = await redis.get<Message[]>(`conv:${sessionId}`);
  return history ?? [];
}
 
export async function updateConversationHistory(
  sessionId: string,
  messages: Message[]
): Promise<void> {
  // Keep only the last 20 turns to control context size
  const trimmed = messages.slice(-20);
  await redis.set(`conv:${sessionId}`, trimmed, { ex: SESSION_TTL_SECONDS });
}

Two things to notice in that code: the TTL defines your session window (24 hours is a common choice for support chats; set it longer for async email-style workflows), and the slice keeps context bounded. Passing the last 20 turns covers almost every real conversation without sending unbounded history.

The Assistants API was probably truncating your threads at some threshold anyway. The difference is now you control the threshold and you can see exactly what the model receives.

Migration path 2: the Agents SDK

For CX deployments with multiple specialized agents that hand off between each other, the Agents SDK gives you the full toolkit: agent definitions, explicit handoffs, tool orchestration, and built-in tracing.

The mental model is different from the Assistants API. Instead of OpenAI orchestrating a Run against your Assistant, you write the orchestration logic in code and the SDK executes it:

support-agent.ts·typescript
import { Agent, run } from "@openai/agents";
import { getOrderStatus, processRefund, escalateToHuman } from "./tools";
 
const supportAgent = new Agent({
  name: "SupportAgent",
  instructions: `You are a customer support agent for Acme Corp.
    Help customers with order issues, returns, and account questions.
    Use escalateToHuman when a customer is frustrated or the issue needs human judgment.`,
  model: "gpt-4o",
  tools: [getOrderStatus, processRefund, escalateToHuman],
});
 
export async function handleCustomerMessage(
  sessionId: string,
  userMessage: string,
  history: Message[]
) {
  const result = await run(supportAgent, userMessage, {
    context: { sessionId, history },
  });
 
  return {
    reply: result.finalOutput,
    toolCallsMade: result.newItems.filter((item) => item.type === "tool_call"),
  };
}

Where the Agents SDK becomes genuinely better than any workaround in the Assistants API is the handoff pattern. If your support flow routes between a billing specialist, a technical specialist, and a triage agent, the SDK models this explicitly:

multi-agent-support.ts·typescript
import { Agent, handoff } from "@openai/agents";
 
const billingAgent = new Agent({
  name: "BillingAgent",
  instructions: "Handle payment issues, refund requests, and billing questions.",
  tools: [checkPaymentStatus, processRefund, issueCredit],
});
 
const technicalAgent = new Agent({
  name: "TechnicalAgent",
  instructions: "Handle product bugs, access issues, and technical troubleshooting.",
  tools: [checkSystemStatus, resetAccount, createSupportTicket],
});
 
const triageAgent = new Agent({
  name: "TriageAgent",
  instructions: `Determine what kind of help the customer needs and route accordingly.
    Send billing and payment issues to the billing specialist.
    Send technical and product issues to the technical specialist.`,
  handoffs: [
    handoff(billingAgent, "Customer has a billing or payment question"),
    handoff(technicalAgent, "Customer has a technical or product issue"),
  ],
});

The difference from the Assistants API version of this pattern: in the old world, routing logic lived in your application code. You'd make a classification call, check the result, and manually spin up a different assistant. Context transfer between assistants was also your problem. The Agents SDK makes routing declarative and handles context forwarding automatically.

Assistants API Agents SDK Customer message arrives Where does routing logic live? Your application code Agent handoff definitions Manual LLM call to classify intent Manual context copy to next assistant Manual error handling per path Triage agent decides autonomously Context transferred via SDK SDK handles retries and failures
Where orchestration logic lives: Assistants API vs Agents SDK

The context-sizing decision you've been avoiding

Here's something the migration guides don't make explicit enough: when you owned state and had to decide how much history to pass, most teams discovered they'd been sending way too much.

The Assistants API's auto-truncation was often masking this. Your thread would grow to 60 turns over a week, OpenAI would silently trim it to fit the context window, and your agent would work reasonably well because the most recent turns were what actually mattered.

Now that you own this decision, you have to make it intentionally. For a typical CX support conversation, the last 8-12 turns cover what the model actually needs. The customer's original complaint, whatever has been tried so far, and the current state of the conversation. Everything before that is mostly irrelevant.

But "most recent 12 turns" is a heuristic, not a rule. Some agent patterns need longer memory. An agent helping a customer through a multi-day return process needs to know what was agreed two days ago. A loan application agent needs the full session history from the start.

The right approach is to be intentional: decide what memory window your agent needs, build the context management to match, and test that your agent performs well with that window. If you've never thought carefully about this, the migration forces you to, which is actually good.

For agents that need longer-term memory without sending megabytes of history, consider summarization. Summarize older conversation chunks into a single context entry, then pass recent turns verbatim. This keeps context bounded while retaining the information that matters.

You can read more about the practical patterns in building an AI agent memory system or the session vs. long-term memory breakdown.

Using the migration to add what you should have had already

Most teams that shipped on the Assistants API never built real test coverage. The API made it easy to get something working, and once it worked, it went to production. You had customer conversations as a quality signal, not a systematic eval suite.

Migration is the right moment to fix that, because you're testing behavioral equivalence anyway. You have to validate that the new setup produces comparable output to the old one, so you're building test infrastructure regardless.

The most useful thing to do: pull 200-300 real conversations from your Thread history before the shutdown. Export them. Use them as your baseline test set. Run them through the Responses API with your migrated state management, and compare the outputs.

Look for divergence in three specific places:

Tool call patterns are the most diagnostic signal. If the new agent makes different tool calls in the same conversations, your context structure is different from what the Assistants API was passing. This isn't necessarily bad, but you need to understand why the difference exists and whether it changes the quality of the outcome.

Escalation decisions are the highest-stakes signal for CX agents. If escalation rates change significantly after migration, something in the agent's reasoning has shifted. Higher escalation could mean the agent is less confident, lower could mean it's being inappropriately optimistic about its ability to resolve issues. Neither is automatically wrong, but both deserve investigation.

Response tone and length often reflect changes in effective context. A more verbose agent usually means it's seeing more history than before. A terser agent may be seeing less. Compare against your pre-migration behavior to establish what's expected.

Once you've validated behavioral consistency, set up ongoing quality monitoring so you have a baseline before the first drift happens. The Build work is nearly done; now you need to Monitor it. Chanl's scorecards can grade conversations across task completion, policy compliance, and tone automatically. And scenarios let you run your exported test set against the migrated agent on every deploy, catching regressions before they reach production.

Here's what a quick Chanl SDK integration looks like for validating migration quality:

migration-validation.ts·typescript
import Chanl from "@chanl/sdk";
 
const chanl = new Chanl({ apiKey: process.env.CHANL_API_KEY });
 
// Validate migrated agent against exported Thread history
async function validateMigration(exportedConversations: Conversation[]) {
  const results = await Promise.all(
    exportedConversations.map(async (conv) => {
      // Run the conversation through your new agent
      const agentOutput = await runMigratedAgent(conv.messages);
 
      // Grade using the same scorecard you'll use in production
      const score = await chanl.scorecards.evaluate({
        scorecardId: "cx-support-quality-v1",
        conversation: agentOutput.fullConversation,
        criteria: ["task_completion", "tone", "policy_compliance"],
      });
 
      return {
        conversationId: conv.id,
        score,
        toolCallDelta: diffToolCalls(conv.originalToolCalls, agentOutput.toolCalls),
      };
    })
  );
 
  // Flag conversations where quality dropped significantly
  const regressions = results.filter(
    (r) => r.score.overall < MINIMUM_ACCEPTABLE_SCORE
  );
 
  return { results, regressions };
}

Running this before your cutover date gives you a quantified answer to "is the migrated agent as good as the old one?" instead of a vague feeling that it seems okay.

What happens if you miss the deadline

August 26 is a hard date. There's no grace period mentioned in the OpenAI documentation, and precedent from other API sunsets suggests there won't be one.

If your app is still calling the Assistants API on August 27, the requests will fail. Your support agent will stop responding. Customers will hit error states mid-conversation. Depending on how your application handles API failures, this could surface as timeouts, 500 errors, or silent failures where the agent appears to receive a message but doesn't respond.

The fix will be urgent at that point, and you'll be making architectural decisions under pressure. That's the version of this situation you want to avoid.

There's no reason to rush through the migration in the next two weeks, but there's also no reason to wait until August. The work is well-defined. The migration path is documented. The testing approach is straightforward. Teams that start now have time to do this carefully, validate thoroughly, and use the migration to add the monitoring and test coverage their agents need.

Validate your migration before August 26

Export your Thread history, run it against your migrated agent, and catch regressions before the deadline. Chanl makes it straightforward to build the test suite your agent should have had from the start.

Start testing free
DG

Co-founder

Building the platform for AI agents at Chanl — tools, testing, and observability for customer experience.

El briefing de Signal

Un email por semana. Cómo los equipos líderes de CS, ingresos e IA están convirtiendo conversaciones en decisiones. Benchmarks, playbooks y lo que funciona en producción.

500+ líderes de CS e ingresos suscritos

Frequently Asked Questions