Migrating from Chat Completions
Treat migration as three related changes: send requests to /v1/responses, read output from a typed output array, and choose how your application will carry state between turns.
1. Update generation endpoints
Start by updating your generation endpoints from post /v1/chat/completions to post /v1/responses.
If you are not using functions or multimodal inputs, simple message inputs are compatible from one API to the other:
Reuse simple message input
/** @type {OpenAI.ChatCompletionMessageParam[] & OpenAI.Responses.ResponseInput} */
const context = [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" },
];
const completion = await client.chat.completions.create({
model: "gpt-6-astra",
messages: context,
});
const response = await client.responses.create({
model: "gpt-6-astra",
input: context,
});context = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"},
]
completion = client.chat.completions.create(model="gpt-6-astra", messages=context)
response = client.responses.create(model="gpt-6-astra", input=context)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
completion, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: "gpt-6-astra",
Messages: []openai.ChatCompletionMessageParamUnion{
openai.SystemMessage("You are a helpful assistant."),
openai.UserMessage("Hello!"),
},
})
if err != nil {
panic(err)
}
fmt.Println(completion.Choices[0].Message.Content)
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfInputItemList: responses.ResponseInputParam{
responses.ResponseInputItemParamOfMessage("You are a helpful assistant.", responses.EasyInputMessageRoleSystem),
responses.ResponseInputItemParamOfMessage("Hello!", responses.EasyInputMessageRoleUser),
}},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import com.openai.models.responses.EasyInputMessage;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputItem;
import java.util.List;
var completion =
client
.chat()
.completions()
.create(
ChatCompletionCreateParams.builder()
.model("gpt-6-astra")
.addSystemMessage("You are a helpful assistant.")
.addUserMessage("Hello!")
.build());
completion.choices().stream()
.flatMap(choice -> choice.message().content().stream())
.forEach(System.out::println);
var response =
client
.responses()
.create(
ResponseCreateParams.builder()
.model("gpt-6-astra")
.inputOfResponse(
List.of(
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.SYSTEM)
.content("You are a helpful assistant.")
.build()),
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.USER)
.content("Hello!")
.build())))
.build());
response.output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));using OpenAI.Chat;
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-6-astra";
ChatClient chat = new(model, key);
ChatCompletion completion = await chat.CompleteChatAsync(
[
new SystemChatMessage("You are a helpful assistant."),
new UserChatMessage("Hello!"),
]
);
Console.WriteLine(completion.Content[0].Text);
ResponsesClient responses = new(key);
ResponseResult response = await responses.CreateResponseAsync(
model,
[
ResponseItem.CreateSystemMessageItem("You are a helpful assistant."),
ResponseItem.CreateUserMessageItem("Hello!"),
]
);
Console.WriteLine(response.GetOutputText());require "openai"
client = OpenAI::Client.new
messages = [
{role: :system, content: "You are a helpful assistant."},
{role: :user, content: "Hello!"}
]
completion = client.chat.completions.create(
model: "gpt-6-astra",
messages: messages
)
puts(completion.choices.fetch(0).message.content)
response = client.responses.create(
model: "gpt-6-astra",
input: messages
)
puts(response.output_text)INPUT='[
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "Hello!" }
]'
curl -s https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "{
\"model\": \"gpt-6-astra\",
\"messages\": $INPUT
}"
curl -s https://api.openai.com/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "{
\"model\": \"gpt-6-astra\",
\"input\": $INPUT
}"Chat Completions
With Chat Completions, you create a `messages` array and read the model text
from `completion.choices[0].message.content`.
Generate text from a model
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const completion = await client.chat.completions.create({
model: "gpt-6-astra",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" },
],
});
console.log(completion.choices[0].message.content);from openai import OpenAI
client = OpenAI()
completion = client.chat.completions.create(
model="gpt-6-astra",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"},
],
)
print(completion.choices[0].message.content)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
completion, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: "gpt-6-astra",
Messages: []openai.ChatCompletionMessageParamUnion{
openai.SystemMessage("You are a helpful assistant."),
openai.UserMessage("Hello!"),
},
})
if err != nil {
panic(err)
}
fmt.Println(completion.Choices[0].Message.Content)
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
ChatCompletionCreateParams params =
ChatCompletionCreateParams.builder()
.model("gpt-6-astra")
.addSystemMessage("You are a helpful assistant.")
.addUserMessage("Hello!")
.build();
client.chat().completions().create(params).choices().stream()
.flatMap(choice -> choice.message().content().stream())
.forEach(System.out::println);using OpenAI.Chat;
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-6-astra";
ChatClient client = new(model, key);
ChatCompletion completion = await client.CompleteChatAsync(
[
new SystemChatMessage("You are a helpful assistant."),
new UserChatMessage("Hello!"),
]
);
Console.WriteLine(completion.Content[0].Text);require "openai"
client = OpenAI::Client.new
completion = client.chat.completions.create(
model: "gpt-6-astra",
messages: [
{role: :system, content: "You are a helpful assistant."},
{role: :user, content: "Hello!"}
]
)
puts(completion.choices.fetch(0).message.content)curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
]
}'Responses
With Responses, you can separate `instructions` and `input` at the top level
and read generated text from `response.output_text`.
Generate text from a model
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const response = await client.responses.create({
model: "gpt-6-astra",
instructions: "You are a helpful assistant.",
input: "Hello!",
});
console.log(response.output_text);from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra", instructions="You are a helpful assistant.", input="Hello!"
)
print(response.output_text)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Instructions: openai.String("You are a helpful assistant."),
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Hello!")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Hello!")
.instructions("You are a helpful assistant.")
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new()
{
Model = "gpt-6-astra",
Instructions = "You are a helpful assistant.",
};
options.InputItems.Add(ResponseItem.CreateUserMessageItem("Hello!"));
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
instructions: "You are a helpful assistant.",
input: "Hello!"
)
puts(response.output_text)curl https://api.openai.com/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"instructions": "You are a helpful assistant.",
"input": "Hello!"
}'2. Map Messages to Items
Chat Completions uses messages as both input and output. Responses uses input and output arrays of typed Items. A message is one Item type, alongside Items such as reasoning, function_call, and function_call_output.
| Chat Completions concept | Responses mapping |
|---|---|
messages[] | input, as a string or an array of input Items |
| System or developer guidance | Top-level instructions, or compatible message Items when you need to preserve an existing transcript |
| User message | An input message Item with role: "user" |
| Assistant message | An output message Item in response.output; pass it back in input if you manually manage state |
| Tool or function call | A function_call output Item |
| Tool or function result | A function_call_output input Item linked to the call with call_id |
Multiple generations with n | Not available in Responses; make separate requests if you need multiple candidate outputs |
When you only need the final text, use the SDK output_text helper. When your flow uses reasoning, tools, or multimodal output, iterate over response.output and handle each Item by its type.
3. Update multi-turn conversations
If you have multi-turn conversations in your application, update your context logic. Responses gives you three common state-management options:
- Use
previous_response_idwhen you want OpenAI to manage prior response context. Resend stableinstructionson each request, becauseprevious_response_iddoes not carry over the previous response's top-levelinstructions. - Pass prior
outputItems back into the next request when you need to manage or trim context yourself. - Use the Conversations API when you need a persistent conversation object.
Chat Completions
In Chat Completions, you store the transcript and send the accumulated
`messages` array on each request.
Multi-turn conversation
/** @type {OpenAI.ChatCompletionMessageParam[]} */
let messages = [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is the capital of France?" },
];
const res1 = await client.chat.completions.create({
model: "gpt-6-astra",
messages,
});
messages = messages.concat([res1.choices[0].message]);
messages.push({ role: "user", content: "And its population?" });
const res2 = await client.chat.completions.create({
model: "gpt-6-astra",
messages,
});messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"},
]
res1 = client.chat.completions.create(model="gpt-6-astra", messages=messages)
messages += [res1.choices[0].message]
messages += [{"role": "user", "content": "And its population?"}]
res2 = client.chat.completions.create(model="gpt-6-astra", messages=messages)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
messages := []openai.ChatCompletionMessageParamUnion{
openai.SystemMessage("You are a helpful assistant."),
openai.UserMessage("What is the capital of France?"),
}
first, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{Model: "gpt-6-astra", Messages: messages})
if err != nil {
panic(err)
}
messages = append(messages, openai.AssistantMessage(first.Choices[0].Message.Content), openai.UserMessage("And its population?"))
second, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{Model: "gpt-6-astra", Messages: messages})
if err != nil {
panic(err)
}
fmt.Println(second.Choices[0].Message.Content)
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
var params =
ChatCompletionCreateParams.builder()
.model("gpt-6-astra")
.addSystemMessage("You are a helpful assistant.")
.addUserMessage("What is the capital of France?")
.build();
var first = client.chat().completions().create(params);
var second =
client
.chat()
.completions()
.create(
params.toBuilder()
.addAssistantMessage(first.choices().get(0).message().content().orElseThrow())
.addUserMessage("And its population?")
.build());
second.choices().stream()
.flatMap(choice -> choice.message().content().stream())
.forEach(System.out::println);using OpenAI.Chat;
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-6-astra";
ChatClient client = new(model, key);
List<ChatMessage> messages =
[
new SystemChatMessage("You are a helpful assistant."),
new UserChatMessage("What is the capital of France?"),
];
ChatCompletion first = await client.CompleteChatAsync(messages);
messages.Add(new AssistantChatMessage(first));
messages.Add(new UserChatMessage("And its population?"));
ChatCompletion second = await client.CompleteChatAsync(messages);
Console.WriteLine(second.Content[0].Text);require "openai"
client = OpenAI::Client.new
messages = [
{role: :system, content: "You are a helpful assistant."},
{role: :user, content: "What is the capital of France?"}
]
first = client.chat.completions.create(
model: "gpt-6-astra",
messages: messages
)
messages << {role: :assistant, content: first.choices.fetch(0).message.content}
messages << {role: :user, content: "And its population?"}
second = client.chat.completions.create(
model: "gpt-6-astra",
messages: messages
)
puts(second.choices.fetch(0).message.content)Responses
With Responses, you can manually pass outputs from one response into the
input of another.
Multi-turn conversation
import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";
/** @type {OpenAI.Responses.ResponseInput} */
let context = [{ role: "user", content: "What is the capital of France?" }];
const res1 = await client.responses.create({
model: "gpt-6-astra",
input: context,
});
// Append the first response’s output to context
context = context.concat(toResponseInputItems(res1.output));
// Add the next user message
context.push({ role: "user", content: "And its population?" });
const res2 = await client.responses.create({
model: "gpt-6-astra",
input: context,
});context = [{"role": "user", "content": "What is the capital of France?"}]
res1 = client.responses.create(
model="gpt-6-astra",
input=context,
)
# Append the first response's output to context
context += res1.output
# Add the next user message
context += [{"role": "user", "content": "And its population?"}]
res2 = client.responses.create(
model="gpt-6-astra",
input=context,
)package main
import (
"context"
"encoding/json"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
contextItems := responses.ResponseInputParam{
responses.ResponseInputItemParamOfMessage("What is the capital of France?", responses.EasyInputMessageRoleUser),
}
first, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfInputItemList: contextItems},
})
if err != nil {
panic(err)
}
contextItems = append(contextItems, outputAsInput(first.Output)...)
contextItems = append(contextItems, responses.ResponseInputItemParamOfMessage("And its population?", responses.EasyInputMessageRoleUser))
second, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfInputItemList: contextItems},
})
if err != nil {
panic(err)
}
fmt.Println(second.OutputText())
}
func outputAsInput(output []responses.ResponseOutputItemUnion) []responses.ResponseInputItemUnionParam {
input := make([]responses.ResponseInputItemUnionParam, 0, len(output))
for _, item := range output {
var converted responses.ResponseInputItemUnion
if err := json.Unmarshal([]byte(item.RawJSON()), &converted); err != nil {
panic(err)
}
input = append(input, converted.ToParam())
}
return input
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.EasyInputMessage;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputItem;
import java.util.ArrayList;
var history = new ArrayList<ResponseInputItem>();
history.add(
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.USER)
.content("What is the capital of France?")
.build()));
var first =
client
.responses()
.create(
ResponseCreateParams.builder().model("gpt-6-astra").inputOfResponse(history).build());
first.output().stream()
.map(item -> JsonValue.from(item).convert(ResponseInputItem.class))
.forEach(history::add);
history.add(
ResponseInputItem.ofEasyInputMessage(
EasyInputMessage.builder()
.role(EasyInputMessage.Role.USER)
.content("And its population?")
.build()));
client
.responses()
.create(ResponseCreateParams.builder().model("gpt-6-astra").inputOfResponse(history).build())
.output()
.stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
List<ResponseItem> history =
[
ResponseItem.CreateUserMessageItem("What is the capital of France?"),
];
ResponseResult first = await client.CreateResponseAsync("gpt-6-astra", history);
history.AddRange(first.OutputItems);
history.Add(ResponseItem.CreateUserMessageItem("And its population?"));
ResponseResult second = await client.CreateResponseAsync("gpt-6-astra", history);
Console.WriteLine(second.GetOutputText());require "openai"
client = OpenAI::Client.new
context = [{role: :user, content: "What is the capital of France?"}]
first = client.responses.create(
model: "gpt-6-astra",
input: context
)
context.concat(first.output)
context << {role: :user, content: "And its population?"}
second = client.responses.create(
model: "gpt-6-astra",
input: context
)
puts(second.output_text)You can also use `previous_response_id` to reference the previous response
and create response chains or forks.
Multi-turn conversation
const res1 = await client.responses.create({
model: "gpt-6-astra",
input: "What is the capital of France?",
store: true,
});
const res2 = await client.responses.create({
model: "gpt-6-astra",
input: "And its population?",
previous_response_id: res1.id,
store: true,
});res1 = client.responses.create(
model="gpt-6-astra", input="What is the capital of France?", store=True
)
res2 = client.responses.create(
model="gpt-6-astra",
input="And its population?",
previous_response_id=res1.id,
store=True,
)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
first, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Store: openai.Bool(true),
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("What is the capital of France?")},
})
if err != nil {
panic(err)
}
second, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Store: openai.Bool(true),
PreviousResponseID: openai.String(first.ID),
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("And its population?")},
})
if err != nil {
panic(err)
}
fmt.Println(second.OutputText())
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
var first =
client
.responses()
.create(
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("What is the capital of France?")
.store(true)
.build());
var second =
client
.responses()
.create(
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("And its population?")
.previousResponseId(first.id())
.store(true)
.build());
second.output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
ResponseResult first = await client.CreateResponseAsync(
"gpt-6-astra",
"What is the capital of France?"
);
ResponseResult second = await client.CreateResponseAsync(
"gpt-6-astra",
"And its population?",
previousResponseId: first.Id
);
Console.WriteLine(second.GetOutputText());require "openai"
client = OpenAI::Client.new
first = client.responses.create(
model: "gpt-6-astra",
input: "What is the capital of France?",
store: true
)
second = client.responses.create(
model: "gpt-6-astra",
previous_response_id: first.id,
input: "And its population?",
store: true
)
puts(second.output_text)Even when using previous_response_id, all previous input tokens for responses in the chain are billed as input tokens in the API.
4. Decide when to use statefulness
Responses are stored by default. Chat Completions are stored by default for new accounts. To disable storage in either API, set store: false.
Some organizations, such as those with Zero Data Retention (ZDR) requirements, cannot use the Responses API in a stateful way due to compliance or data retention policies. To support these cases, OpenAI offers encrypted reasoning items, allowing you to keep your workflow stateless while still benefiting from reasoning items.
To disable statefulness but still take advantage of reasoning:
- Set
store: falsein the store field. - Preserve and replay every returned reasoning item. Each item includes
encrypted_contentby default when you create a response.
The API will then return an encrypted version of the reasoning tokens, which you can pass back in future requests just like regular reasoning items. For ZDR organizations, OpenAI enforces store: false automatically. When a request includes encrypted_content, it is decrypted in memory, used for generating the next response, and then securely discarded. Any new reasoning tokens are immediately encrypted and returned to you, ensuring no intermediate state is persisted.
5. Update function definitions and outputs
There are two minor, but notable, differences in how functions are defined between Chat Completions and Responses.
- In Chat Completions, function definitions are externally tagged. In Responses, they are internally tagged.
- In Chat Completions, functions are non-strict by default. In Responses, omitting
strictattempts strict mode; if the schema cannot be made compatible, Responses falls back to non-strict, best-effort function calling and returns the resolved tool withstrict: false. To keep non-strict behavior in Responses explicitly, setstrict: false.
The Responses API function example on the right is functionally equivalent to the Chat Completions example on the left.
Chat Completions API
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Determine weather in my location",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"location"
]
}
}
}Responses API
{
"type": "function",
"name": "get_weather",
"description": "Determine weather in my location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"location"
]
}
}Follow function-calling best practices
In Responses, tool calls and their outputs are two distinct types of Items that are correlated using a call_id. See the function calling docs for more detail on how function calling works in Responses.
6. Update Structured Outputs definitions
In the Responses API, Structured Outputs definitions have moved from response_format to text.format:
Chat Completions
Structured Outputs
const completion = await openai.chat.completions.create({
model: "gpt-6-astra",
messages: [
{
role: "user",
content: "Jane, 54 years old",
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "person",
strict: true,
schema: {
type: "object",
properties: {
name: {
type: "string",
minLength: 1,
},
age: {
type: "number",
minimum: 0,
maximum: 130,
},
},
required: ["name", "age"],
additionalProperties: false,
},
},
},
reasoning_effort: "medium",
});from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-6-astra",
messages=[
{
"role": "user",
"content": "Jane, 54 years old",
}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "person",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"age": {"type": "number", "minimum": 0, "maximum": 130},
},
"required": ["name", "age"],
"additionalProperties": False,
},
},
},
reasoning_effort="medium",
)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/shared"
)
func main() {
client := openai.NewClient()
schema := map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]any{"type": "string", "minLength": 1},
"age": map[string]any{"type": "number", "minimum": 0, "maximum": 130},
},
"required": []string{"name", "age"},
"additionalProperties": false,
}
completion, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: "gpt-6-astra",
ReasoningEffort: openai.ReasoningEffortMedium,
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Jane, 54 years old"),
},
ResponseFormat: openai.ChatCompletionNewParamsResponseFormatUnion{
OfJSONSchema: &shared.ResponseFormatJSONSchemaParam{JSONSchema: shared.ResponseFormatJSONSchemaJSONSchemaParam{
Name: "person", Strict: openai.Bool(true), Schema: schema,
}},
},
})
if err != nil {
panic(err)
}
fmt.Println(completion.Choices[0].Message.Content)
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.ReasoningEffort;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.util.List;
import java.util.Map;
ChatCompletionCreateParams params =
ChatCompletionCreateParams.builder()
.model("gpt-6-astra")
.reasoningEffort(ReasoningEffort.MEDIUM)
.addUserMessage("Jane, 54 years old")
.putAdditionalBodyProperty(
"response_format",
JsonValue.from(
Map.of(
"type",
"json_schema",
"json_schema",
Map.of(
"name",
"person",
"strict",
true,
"schema",
Map.of(
"type",
"object",
"properties",
Map.of(
"name",
Map.of("type", "string", "minLength", 1),
"age",
Map.of("type", "number", "minimum", 0, "maximum", 130)),
"required",
List.of("name", "age"),
"additionalProperties",
false)))))
.build();
client.chat().completions().create(params).choices().stream()
.flatMap(choice -> choice.message().content().stream())
.forEach(System.out::println);using OpenAI.Chat;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-6-astra";
ChatClient client = new(model, key);
BinaryData schema = BinaryData.FromString(
"""
{
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"age": { "type": "number", "minimum": 0, "maximum": 130 }
},
"required": ["name", "age"],
"additionalProperties": false
}
"""
);
ChatCompletionOptions options = new()
{
ReasoningEffortLevel = ChatReasoningEffortLevel.Medium,
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
"person",
schema,
jsonSchemaIsStrict: true
),
};
ChatCompletion completion = await client.CompleteChatAsync(
[new UserChatMessage("Jane, 54 years old")],
options
);
Console.WriteLine(completion.Content[0].Text);require "openai"
client = OpenAI::Client.new
schema = {
type: "object",
properties: {
name: {type: "string", minLength: 1},
age: {type: "number", minimum: 0, maximum: 130}
},
required: ["name", "age"],
additionalProperties: false
}
completion = client.chat.completions.create(
model: "gpt-6-astra",
reasoning_effort: :medium,
messages: [{role: :user, content: "Jane, 54 years old"}],
response_format: {
type: :json_schema,
json_schema: {name: "person", strict: true, schema: schema}
}
)
puts(completion.choices.fetch(0).message.content)curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"messages": [
{
"role": "user",
"content": "Jane, 54 years old"
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"age": {
"type": "number",
"minimum": 0,
"maximum": 130
}
},
"required": [
"name",
"age"
],
"additionalProperties": false
}
}
},
"reasoning_effort": "medium"
}'Responses
Structured Outputs
const response = await openai.responses.create({
model: "gpt-6-astra",
input: "Jane, 54 years old",
text: {
format: {
type: "json_schema",
name: "person",
strict: true,
schema: {
type: "object",
properties: {
name: {
type: "string",
minLength: 1,
},
age: {
type: "number",
minimum: 0,
maximum: 130,
},
},
required: ["name", "age"],
additionalProperties: false,
},
},
},
});response = client.responses.create(
model="gpt-6-astra",
input="Jane, 54 years old",
text={
"format": {
"type": "json_schema",
"name": "person",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"age": {"type": "number", "minimum": 0, "maximum": 130},
},
"required": ["name", "age"],
"additionalProperties": False,
},
}
},
)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
schema := map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]any{"type": "string", "minLength": 1},
"age": map[string]any{"type": "number", "minimum": 0, "maximum": 130},
},
"required": []string{"name", "age"},
"additionalProperties": false,
}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Jane, 54 years old")},
Text: responses.ResponseTextConfigParam{Format: responses.ResponseFormatTextConfigUnionParam{
OfJSONSchema: &responses.ResponseFormatTextJSONSchemaConfigParam{Name: "person", Schema: schema, Strict: openai.Bool(true)},
}},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseFormatTextJsonSchemaConfig;
import com.openai.models.responses.ResponseTextConfig;
import java.util.List;
import java.util.Map;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Jane, 54 years old")
.text(
ResponseTextConfig.builder()
.format(
ResponseFormatTextJsonSchemaConfig.builder()
.name("person")
.strict(true)
.schema(
ResponseFormatTextJsonSchemaConfig.Schema.builder()
.putAdditionalProperty("type", JsonValue.from("object"))
.putAdditionalProperty(
"properties",
JsonValue.from(
Map.of(
"name",
Map.of("type", "string", "minLength", 1),
"age",
Map.of(
"type", "number", "minimum", 0, "maximum",
130))))
.putAdditionalProperty(
"required", JsonValue.from(List.of("name", "age")))
.putAdditionalProperty(
"additionalProperties", JsonValue.from(false))
.build())
.build())
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
BinaryData schema = BinaryData.FromString(
"""
{
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"age": { "type": "number", "minimum": 0, "maximum": 130 }
},
"required": ["name", "age"],
"additionalProperties": false
}
"""
);
CreateResponseOptions options = new()
{
Model = "gpt-6-astra",
TextOptions = new ResponseTextOptions
{
TextFormat = ResponseTextFormat.CreateJsonSchemaFormat(
"person",
schema,
jsonSchemaIsStrict: true
),
},
};
options.InputItems.Add(
ResponseItem.CreateUserMessageItem("Jane, 54 years old")
);
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());require "openai"
client = OpenAI::Client.new
schema = {
type: "object",
properties: {
name: {type: "string", minLength: 1},
age: {type: "number", minimum: 0, maximum: 130}
},
required: ["name", "age"],
additionalProperties: false
}
response = client.responses.create(
model: "gpt-6-astra",
input: "Jane, 54 years old",
text: {
format: {
type: :json_schema,
name: "person",
strict: true,
schema: schema
}
}
)
puts(response.output_text)curl https://api.openai.com/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"input": "Jane, 54 years old",
"text": {
"format": {
"type": "json_schema",
"name": "person",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"age": {
"type": "number",
"minimum": 0,
"maximum": 130
}
},
"required": [
"name",
"age"
],
"additionalProperties": false
}
}
}
}'7. Update streaming consumers
Chat Completions streaming returns incremental chunks with a delta field. Responses streaming uses typed server-sent events. Update stream consumers to branch on each event's type and handle the events your UI or orchestration layer needs.
For text streaming, listen for events such as:
response.createdresponse.output_text.deltaresponse.completederror
Function-calling streams can also emit events such as response.function_call_arguments.delta and response.function_call_arguments.done. See the streaming Responses guide and Responses streaming events reference.
8. Upgrade to native tools
If your application has use cases that would benefit from OpenAI's native tools, you can update your tool calls to use OpenAI's tools out of the box.
Chat Completions
With Chat Completions, you cannot use OpenAI-hosted tools natively and have
to write your own tool integration.
This example uses GPT-5.6 because GPT-6 Astra requires the Responses API
for tool calling.
Web search tool
async function web_search(query) {
const res = await fetch(`https://api.example.com/search?q=${query}`);
const data = await res.json();
return data.results;
}
const completion = await client.chat.completions.create({
model: "gpt-5.6",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Who is the current president of France?" },
],
functions: [
{
name: "web_search",
description: "Search the web for information",
parameters: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
},
],
});import requests
def web_search(query):
r = requests.get(f"https://api.example.com/search?q={query}")
return r.json().get("results", [])
completion = client.chat.completions.create(
model="gpt-5.6",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who is the current president of France?"},
],
functions=[
{
"name": "web_search",
"description": "Search the web for information",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
}
],
)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/shared"
)
func main() {
client := openai.NewClient()
completion, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: "gpt-5.6",
Messages: []openai.ChatCompletionMessageParamUnion{
openai.SystemMessage("You are a helpful assistant."),
openai.UserMessage("Who is the current president of France?"),
},
Functions: []openai.ChatCompletionNewParamsFunction{{
Name: "web_search",
Description: openai.String("Search the web for information"),
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{"query": map[string]any{"type": "string"}},
"required": []string{"query"},
},
}},
ReasoningEffort: shared.ReasoningEffortNone,
})
if err != nil {
panic(err)
}
fmt.Println(completion.Choices[0].Message)
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.FunctionParameters;
import com.openai.models.ReasoningEffort;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.util.List;
import java.util.Map;
ChatCompletionCreateParams params =
ChatCompletionCreateParams.builder()
.model("gpt-5.6")
.reasoningEffort(ReasoningEffort.NONE)
.addSystemMessage("You are a helpful assistant.")
.addUserMessage("Who is the current president of France?")
.addFunction(
ChatCompletionCreateParams.Function.builder()
.name("web_search")
.description("Search the web for information")
.parameters(
FunctionParameters.builder()
.putAdditionalProperty("type", JsonValue.from("object"))
.putAdditionalProperty(
"properties",
JsonValue.from(Map.of("query", Map.of("type", "string"))))
.putAdditionalProperty("required", JsonValue.from(List.of("query")))
.build())
.build())
.build();
client.chat().completions().create(params).choices().stream()
.map(choice -> choice.message())
.forEach(System.out::println);require "openai"
client = OpenAI::Client.new
completion = client.chat.completions.create(
model: "gpt-5.6",
reasoning_effort: :none,
messages: [
{role: :system, content: "You are a helpful assistant."},
{role: :user, content: "Who is the current president of France?"}
],
functions: [
{
name: "web_search",
description: "Search the web for information",
parameters: {
type: "object",
properties: {query: {type: "string"}},
required: ["query"]
}
}
]
)
puts(completion.choices.fetch(0).message)curl https://api.example.com/search \
-G \
--data-urlencode "q=your+search+term" \
--data-urlencode "key=$SEARCH_API_KEY"\Responses
With Responses, you can specify the tools that you want the model to use.
Web search tool
const answer = await client.responses.create({
model: "gpt-6-astra",
input: "Who is the current president of France?",
tools: [{ type: "web_search" }],
});
console.log(answer.output_text);answer = client.responses.create(
model="gpt-6-astra",
input="Who is the current president of France?",
tools=[{"type": "web_search"}],
)
print(answer.output_text)package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Who is the current president of France?")},
Tools: []responses.ToolUnionParam{
responses.ToolParamOfWebSearch(responses.WebSearchToolTypeWebSearch),
},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.WebSearchTool;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Who is the current president of France?")
.addTool(WebSearchTool.builder().type(WebSearchTool.Type.WEB_SEARCH).build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new() { Model = "gpt-6-astra" };
options.Tools.Add(ResponseTool.CreateWebSearchTool());
options.InputItems.Add(
ResponseItem.CreateUserMessageItem("Who is the current president of France?")
);
ResponseResult response = await client.CreateResponseAsync(options);
Console.WriteLine(response.GetOutputText());require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Who is the current president of France?",
tools: [{type: :web_search}]
)
puts(response.output_text)curl https://api.openai.com/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"input": "Who is the current president of France?",
"tools": [{"type": "web_search"}]
}'9. Check common migration errors
Watch for these issues when moving code from Chat Completions to Responses:
- Reading
choices[0].message.contentinstead ofresponse.output_textorresponse.output. - Treating every
outputentry as a message. Reasoning, tool, and function calls are separate Item types. - Dropping reasoning, function call, or function call output Items when manually carrying context into the next response.
- Sending a function result without the matching
call_id. - Using
response_formatin a Responses request instead oftext.format. - Reusing Chat Completions streaming chunk handlers without handling typed Responses events.
- Assuming
previous_response_idremoves billing for prior context. Previous input tokens in the response chain are still billed as input tokens.