What is ending, and what the date leaves you
OpenAI's reusable prompt objects and the v1/prompts API, Agent Builder and the Evals platform share one shutdown date: 30 November 2026.1 INFRO's timeline lists the three as a single shutdown entry and shows it 34 days out on 27 October.2 Today is 28 October, so you have 33 days. The Evals half of this is covered in OpenAI Evals shuts down: export your evals and rebuild them in CI; evals turn read-only on 31 October, three days from now.3
For prompts, the advice quoted in that article is to move their content into your application code.1 That sentence is the whole official migration path as far as INFRO's pages report it, and it leaves out most of the work: where the text lives, how you version it, how you call the model afterwards, and how you know the new path behaves like the old one. This guide covers those parts, and the workflows built in Agent Builder.
One limit on what follows. OpenAI's own pages would not load when this was written, so the exact export calls for hosted prompts and any code export in Agent Builder are not described here. Check OpenAI's current reference for those, and use the plan below for everything after the export.
Inventory before you export
You cannot migrate what you have not found. Start with a list of every place that depends on the hosted objects, because the dependency is often a string id buried in code or in a no-code tool.
- Search every repository and config store for the ids of stored prompts and for calls that reference a stored prompt by id and version.
- List every production path that runs an Agent Builder workflow, including scheduled jobs and anything a non-engineer wired up.
- For each prompt, record the owner, the model it was pinned to, its variables, which version production uses, and whether any evals reference it.
- Check your provider usage for traffic that does not appear in your own logs, since a prompt id can be called from a tool you do not run.
Write the result as a table, one row per prompt or workflow. The row that matters most is "which version is live". Hosted prompts keep a version history, and the live version is often not the newest, so exporting only the latest text ships an untested edit.
Prompts as versioned files
A hosted prompt is four things that belong together: text, a model choice, sampling and output parameters, and the variables the text expects. Keep all four in one file so a change to any of them is one reviewable diff. A plain Markdown file with a header works in any language and any code review tool.
---
id: support-triage
version: 3
route: support-triage # a route name, not a model id
max_tokens: 400
variables: [customer_name, ticket_text]
source: exported from hosted prompt, live version on 2026-10-28
---
You are a support triage assistant for $customer_name.
Classify the ticket below as one of: billing, bug, access, other.
Reply with the label only.
Ticket:
$ticket_textThree choices in that file are deliberate.
- A route name instead of a model id. The file says what job it does, and configuration says which model serves it. When a model is retired, the file does not change. This is the same reasoning as in model aliases vs pinned snapshots: the pin belongs in one place you can audit.
- Declared variables. The loader refuses to render if a variable is missing or unexpected, so a template typo fails in CI, not in a customer's session.
- A
sourceline. Record where the text came from and which hosted version it was, so a reviewer can tell exported text from text someone later edited.
Version files by adding v4.md beside v3.md instead of editing in place, and let the application read a small pointer file naming the live version. Rollback is then a one-line change, and a trace can record exactly which version answered.
A thin layer between code and the provider
Put one function between your application and the model API. It loads the file, renders it, sends it, and tags the request. Nothing else in the codebase should know a provider's SDK or a prompt's text.
import hashlib, os, pathlib
from string import Template
import yaml
from openai import OpenAI
client = OpenAI(
base_url="https://api.infro.io/v1",
api_key=os.environ["INFRO_API_KEY"],
)
def load(prompt_id: str, version: int):
path = pathlib.Path("prompts") / prompt_id / f"v{version}.md"
raw = path.read_text()
_, header, body = raw.split("---", 2)
meta = yaml.safe_load(header)
return meta, body.strip(), hashlib.sha256(raw.encode()).hexdigest()[:12]
def run(prompt_id: str, version: int, **variables):
meta, body, digest = load(prompt_id, version)
if set(variables) != set(meta["variables"]):
raise ValueError(f"variables for {prompt_id}: {meta['variables']}")
text = Template(body).substitute(**variables) # raises on a missing name
return client.chat.completions.create(
model=meta["route"],
max_tokens=meta.get("max_tokens"),
messages=[{"role": "user", "content": text}],
extra_body={"metadata": {
"workflow": prompt_id,
"prompt_version": str(version),
"prompt_hash": digest,
}},
)The base URL change is the only provider-specific line. INFRO's chat completions endpoint is wire-compatible with OpenAI's, so an SDK that speaks the OpenAI protocol works by changing the base URL.4 A route is a name your application sends as model, and it resolves to an ordered list of your own provider connections and the model each should serve, which keeps the vendor and model choice in configuration.5 The request log records the model, the connection that served it, timings, usage and estimated provider cost.6 If you tag requests with workflow and a prompt version, as above, you can later ask which prompt version produced a bad answer or a cost spike. Routes read feature, workflow and step from metadata, so the same tags can drive routing rules.5
You do not need a gateway for the files or the loader. The reason to route through one now is that the cutover below depends on being able to move a share of traffic without a deploy.
Rebuilding an Agent Builder workflow
A hosted visual workflow hides its control flow. Rebuild it as code you can read, and take the opportunity to add the limits the canvas may not enforce.
- Draw the graph first. For each node on the canvas, write one line: its input, its output, the model and prompt it uses, the tools it can call, and the condition for the next node. Screenshots of the canvas go into the repository next to the code.
- One function per node. Each takes typed input and returns typed output. Model calls go through the
runfunction above, so each node's prompt is a versioned file. - Make branching explicit. A branch node becomes an
ifon a structured output field. Ask the model for JSON with a schema and validate it, rather than parsing prose. - Bound the loop. Set a maximum step count, a wall-clock limit and a token ceiling for the whole run, and fail closed when any is hit. Bounding AI agent loops covers the four limits.
- Carry a run id. Pass one id through every node call as
session_idmetadata so a whole run groups in the request log. - Re-register tools. Tools that ran as hosted connectors need a new home: your own functions, or MCP servers. Re-check each tool's credentials and permissions instead of copying them across.
If you would rather use an agent SDK than hand-written loops, INFRO's agent endpoints accept OpenAI's Responses shape and Anthropic's Messages shape, for agent SDKs as well as coding agents.7 You point the SDK at the gateway by changing the base URL and the key.8 That keeps routing, budgets and the request log on the agent's calls, but it does not reproduce a hosted canvas. Whichever runtime you pick, the graph you wrote in step 1 is the spec.
The cutover test
The risk in this migration is not the export. It is a prompt that renders slightly differently, a parameter that is silently dropped, or a model that answers a little differently, with nothing erroring. Test for those three, in this order.
Render check. For each prompt, render it with a fixed set of variables and assert the messages are what you expect. This catches whitespace, a lost system message and variable typos. It needs no model call.
Parameter check. Some models refuse parameters that others accept. INFRO's chat endpoint does not send a model a parameter it refuses, and lists what it dropped in the X-INFRO-Params-Dropped response header; examples in its docs include temperature, top_p and top_k on Claude 4.7 and later.4 Assert on that header in the test, so a migrated prompt that relied on a low temperature is a visible finding and not a silent behaviour change.
Replay check. Collect real inputs, run each through the old hosted path and the new one, and compare. Do not compare text for equality. Compare what your application depends on: the label, the JSON fields, the tool called, the length bound.
import json
def agree(old: dict, new: dict) -> bool:
return old["label"] == new["label"]
def replay(cases, run_old, run_new, threshold=0.98):
disagreements = []
for case in cases:
old, new = run_old(case), run_new(case)
if not agree(old, new):
disagreements.append({"case": case, "old": old, "new": new})
rate = 1 - len(disagreements) / len(cases)
json.dump(disagreements, open("disagreements.json", "w"), indent=2)
return rate >= thresholdRead every disagreement by hand before you touch the threshold. A disagreement is either the new path being wrong or the old path having been wrong for a year, and only a person can tell which. Run the replay while the hosted path still answers: after 30 November there is no old side to compare against, so record old outputs now if you cannot run both later.
Ramping traffic and keeping a way back
Once the replay passes, move traffic in steps and watch the request log between each one. A weighted route is built for this: it picks the first target at random in proportion to weight, then still fails over through the others, and the docs name a gradual migration as a use.5 A first target can be the new model on your own connection, with the previous choice as a fallback target.
Failover only applies before the response starts: INFRO retries a request that fails before the first token on the next connection.9 It does not catch a worse answer. For quality, use your replay set as a recurring check, and watch the signals you already track, such as answer length, error mix and latency by prompt version. From direct OpenAI calls to a gateway has the full canary runbook.
A schedule that fits the time left:
| Window | What happens |
|---|---|
| By 31 Oct | Inventory done; Evals exported; hosted prompt edits frozen |
| By 4 Nov | Every prompt and its versions exported into files; render and parameter checks in CI |
| By 11 Nov | Workflows rebuilt; replay passing on recorded inputs |
| 11 to 20 Nov | Traffic ramp: a small share, then half, then all, with a day at each step |
| 20 to 29 Nov | Old path left in place but unused; remove hosted ids from code |
| 30 Nov | Hosted prompts and Agent Builder stop; delete the dead references |
Do not remove the old path the day you finish the ramp. Keep it until the shutdown so a rollback is a config change.
Where a gateway helps, and where it does not
A gateway does not export your prompts or rebuild your workflow; that work is yours. What it gives this migration is a route name that decouples files from models, a request log that tags every call with its prompt version, weighted targets for the ramp, and failover to a second connection if the first has a bad hour. With INFRO, those run over provider accounts you already hold, your provider bills you directly, and INFRO charges a subscription with 0% markup on model spend.7 INFRO also records vendor shutdown dates and warns organizations still sending a retiring model, which helps with the next deadline after this one.
It does not judge answer quality: that is what the replay set is for. And it does not replace the prompt files. The files are the source of truth, and the gateway is the place that source gets called from.
Checklist for this week
- Every stored prompt id and workflow found, with an owner and a live version
- Hosted edits frozen; the repository is the only place to change a prompt
- Every version you might roll back to exported into a file with a
sourceline - One function sends all prompt calls; no SDK calls with prompt text elsewhere
- Render and parameter checks run in CI
- Old outputs recorded for a replay set while the hosted path still works
- Each Agent Builder graph written down and rebuilt with step, time and token limits
- Traffic ramp scheduled with a rollback that needs no deploy
