SpyBara
Go Premium

Documentation 2026-08-24 22:00 UTC to 2026-08-25 18:59 UTC

20 files changed +1,168 −199. View all changes and history on the product overview
2026
Mon 31 23:00 Fri 28 18:00 Thu 27 22:01 Wed 26 22:57 Tue 25 18:59 Mon 24 22:00 Fri 21 19:59 Thu 20 23:59 Wed 19 18:02 Tue 18 04:58 Mon 17 22:57 Sat 15 01:01 Fri 14 20:01 Thu 13 22:00 Wed 12 02:57 Tue 11 19:59 Mon 10 19:00 Fri 7 00:58 Thu 6 21:58 Wed 5 18:01 Tue 4 22:59 Mon 3 18:01

guides/audio.md +64 −0

Details

202message.content().ifPresent(System.out::println);202message.content().ifPresent(System.out::println);

203```203```

204 204 

205```csharp

206using OpenAI.Chat;

207#pragma warning disable OPENAI001

208 

209string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

210ChatClient client = new("gpt-audio-1.5", key);

211 

212ChatCompletionOptions options = new()

213{

214 ResponseModalities = ChatResponseModalities.Text | ChatResponseModalities.Audio,

215 AudioOptions = new(ChatOutputAudioVoice.Alloy, ChatOutputAudioFormat.Wav),

216 StoredOutputEnabled = true,

217};

218 

219ChatCompletion completion = await client.CompleteChatAsync(

220 [new UserChatMessage("Is a golden retriever a good family dog?")],

221 options

222);

223 

224if (completion.OutputAudio is not ChatOutputAudio audio)

225{

226 throw new InvalidOperationException("No audio output was returned.");

227}

228 

229Console.WriteLine(audio.Transcript);

230await File.WriteAllBytesAsync("dog.wav", audio.AudioBytes.ToArray());

231```

232 

205```ruby233```ruby

206require "base64"234require "base64"

207require "openai"235require "openai"


409 .forEach(System.out::println);437 .forEach(System.out::println);

410```438```

411 439 

440```csharp

441using OpenAI.Chat;

442#pragma warning disable OPENAI001

443 

444string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

445ChatClient client = new("gpt-audio-1.5", key);

446 

447BinaryData audio = BinaryData.FromBytes(

448 await File.ReadAllBytesAsync("audio.wav")

449);

450UserChatMessage message = new(

451 [

452 ChatMessageContentPart.CreateTextPart("What is in this recording?"),

453 ChatMessageContentPart.CreateInputAudioPart(

454 audio,

455 ChatInputAudioFormat.Wav

456 ),

457 ]

458);

459ChatCompletionOptions options = new()

460{

461 ResponseModalities = ChatResponseModalities.Text | ChatResponseModalities.Audio,

462 AudioOptions = new(ChatOutputAudioVoice.Alloy, ChatOutputAudioFormat.Wav),

463 StoredOutputEnabled = true,

464};

465 

466ChatCompletion completion = await client.CompleteChatAsync([message], options);

467 

468if (completion.OutputAudio is not ChatOutputAudio audioOutput)

469{

470 throw new InvalidOperationException("No audio output was returned.");

471}

472 

473Console.WriteLine(audioOutput.Transcript);

474```

475 

412```ruby476```ruby

413require "base64"477require "base64"

414require "openai"478require "openai"

Details

122 .forEach(text -> System.out.println(text.text()));122 .forEach(text -> System.out.println(text.text()));

123```123```

124 124 

125```csharp

126using OpenAI.Responses;

127#pragma warning disable OPENAI001

128 

129string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

130ResponsesClient client = new(key);

131 

132ResponseResult response = await client.CreateResponseAsync(

133 "gpt-5.6",

134 [

135 ResponseItem.CreateUserMessageItem("Knock knock."),

136 ResponseItem.CreateAssistantMessageItem("Who's there?"),

137 ResponseItem.CreateUserMessageItem("Orange."),

138 ]

139);

140 

141Console.WriteLine(response.GetOutputText());

142```

143 

125```ruby144```ruby

126require "openai"145require "openai"

127 146 


331 .forEach(text -> System.out.println(text.text()));350 .forEach(text -> System.out.println(text.text()));

332```351```

333 352 

353```csharp

354using OpenAI.Responses;

355#pragma warning disable OPENAI001

356 

357string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

358ResponsesClient client = new(key);

359 

360List<ResponseItem> history =

361[

362 ResponseItem.CreateUserMessageItem("Tell me a joke."),

363];

364 

365CreateResponseOptions options = new("gpt-5.6", history)

366{

367 StoredOutputEnabled = false,

368 IncludedProperties =

369 {

370 IncludedResponseProperty.ReasoningEncryptedContent,

371 },

372};

373ResponseResult first = await client.CreateResponseAsync(options);

374Console.WriteLine(first.GetOutputText());

375 

376history.AddRange(first.OutputItems);

377history.Add(ResponseItem.CreateUserMessageItem("Tell me another."));

378 

379options = new("gpt-5.6", history)

380{

381 StoredOutputEnabled = false,

382 IncludedProperties =

383 {

384 IncludedResponseProperty.ReasoningEncryptedContent,

385 },

386};

387ResponseResult second = await client.CreateResponseAsync(options);

388Console.WriteLine(second.GetOutputText());

389```

390 

334```ruby391```ruby

335require "openai"392require "openai"

336 393 


581 .forEach(text -> System.out.println(text.text()));638 .forEach(text -> System.out.println(text.text()));

582```639```

583 640 

641```csharp

642using OpenAI.Responses;

643#pragma warning disable OPENAI001

644 

645string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

646ResponsesClient client = new(key);

647 

648ResponseResult first = await client.CreateResponseAsync(

649 "gpt-5.6",

650 "Tell me a joke."

651);

652Console.WriteLine(first.GetOutputText());

653 

654ResponseResult second = await client.CreateResponseAsync(

655 "gpt-5.6",

656 "Explain why this is funny.",

657 previousResponseId: first.Id

658);

659Console.WriteLine(second.GetOutputText());

660```

661 

584```ruby662```ruby

585require "openai"663require "openai"

586 664 


720 .forEach(text -> System.out.println(text.text()));798 .forEach(text -> System.out.println(text.text()));

721```799```

722 800 

801```csharp

802using OpenAI.Responses;

803#pragma warning disable OPENAI001

804 

805string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

806ResponsesClient client = new(key);

807 

808ResponseResult first = await client.CreateResponseAsync(

809 "gpt-5.6",

810 "Tell me a joke."

811);

812Console.WriteLine(first.GetOutputText());

813 

814ResponseResult second = await client.CreateResponseAsync(

815 "gpt-5.6",

816 "Explain why this is funny.",

817 previousResponseId: first.Id

818);

819Console.WriteLine(second.GetOutputText());

820```

821 

723```ruby822```ruby

724require "openai"823require "openai"

725 824 

Details

92System.out.println(embedding.data().get(0).embedding());92System.out.println(embedding.data().get(0).embedding());

93```93```

94 94 

95```csharp

96using OpenAI.Embeddings;

97 

98string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

99string model = "text-embedding-3-small";

100EmbeddingClient client = new(model, key);

101 

102OpenAIEmbedding embedding = await client.GenerateEmbeddingAsync(

103 "The food was delicious and the waiter was friendly."

104);

105 

106Console.WriteLine($"Dimensions: {embedding.ToFloats().Length}");

107```

108 

95```ruby109```ruby

96require "openai"110require "openai"

97 111 


307System.out.println(normalizeL2(shortened));321System.out.println(normalizeL2(shortened));

308```322```

309 323 

324```csharp

325using OpenAI.Embeddings;

326 

327string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

328string model = "text-embedding-3-small";

329EmbeddingClient client = new(model, key);

330 

331OpenAIEmbedding embedding = await client.GenerateEmbeddingAsync("Testing 123");

332 

333float[] shortened = embedding.ToFloats().Span[..256].ToArray();

334double magnitude = Math.Sqrt(shortened.Sum(value => value * value));

335float[] normalized =

336 magnitude == 0

337 ? shortened

338 : shortened.Select(value => (float)(value / magnitude)).ToArray();

339 

340Console.WriteLine($"Dimensions: {normalized.Length}");

341Console.WriteLine($"First value: {normalized[0]:F6}");

342Console.WriteLine(

343 $"L2 norm: {Math.Sqrt(normalized.Sum(value => value * value)):F3}"

344);

345```

346 

310 347 

311Dynamically changing the dimensions enables very flexible usage. For example, when using a vector data store that only supports embeddings up to 1024 dimensions long, developers can now still use our best embedding model `text-embedding-3-large` and specify a value of 1024 for the `dimensions` API parameter, which will shorten the embedding down from 3072 dimensions, trading off some accuracy in exchange for the smaller vector size.348Dynamically changing the dimensions enables very flexible usage. For example, when using a vector data store that only supports embeddings up to 1024 dimensions long, developers can now still use our best embedding model `text-embedding-3-large` and specify a value of 1024 for the `dimensions` API parameter, which will shorten the embedding down from 3072 dimensions, trading off some accuracy in exchange for the smaller vector size.

312 349 

Details

164 Base64.getDecoder().decode(images.data().orElseThrow().get(0).b64Json().orElseThrow()));164 Base64.getDecoder().decode(images.data().orElseThrow().get(0).b64Json().orElseThrow()));

165```165```

166 166 

167```csharp

168using OpenAI.Images;

169 

170string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

171string model = "gpt-image-2";

172ImageClient client = new(model, key);

173 

174GeneratedImage image = await client.GenerateImageAsync(

175 "A children's book drawing of a veterinarian using a stethoscope to "

176 + "listen to the heartbeat of a baby otter."

177);

178 

179await File.WriteAllBytesAsync("otter.png", image.ImageBytes.ToArray());

180```

181 

167```ruby182```ruby

168require "base64"183require "base64"

169require "openai"184require "openai"

Details

11 11 

12 12 

13 13 

14In this guide, you will learn about building applications involving images with the OpenAI API.14<a id="a-tour-of-image-related-use-cases"></a>

15If you know what you want to build, find your use case below to get started. If you're not sure where to start, continue reading to get an overview.

16 

17### A tour of image-related use cases

18 15 

19Recent language models can process image inputs and analyze them—a capability known as **vision**. GPT Image models can use text and image inputs to create new images or edit existing ones.16Recent language models can process image inputs and analyze them—a capability known as **vision**. GPT Image models can use text and image inputs to create new images or edit existing ones.

20 17 

21The OpenAI API offers several endpoints to process images as input or generate them as output, enabling you to build powerful multimodal applications.18Choose an endpoint based on whether you want to analyze images or generate them:

22 19 

23| API | Supported use cases |20| API | Supported use cases |

24| ---------------------------------------------------- | --------------------------------------------------------------------- |21| ---------------------------------------------------- | -------------------------------------------------------------------------- |

25| [Responses API](https://developers.openai.com/api/reference/resources/responses) | Analyze images and use them as input and/or generate images as output |22| [Responses API](https://developers.openai.com/api/reference/resources/responses) | Analyze images, or generate and edit images with the image generation tool |

26| [Images API](https://developers.openai.com/api/reference/resources/images) | Generate images as output, optionally using images as input |23| [Images API](https://developers.openai.com/api/reference/resources/images) | Generate images as output, optionally using images as input |

27| [Chat Completions API](https://developers.openai.com/api/reference/resources/chat) | Analyze images and use them as input to generate text or audio |24| [Chat Completions API](https://developers.openai.com/api/reference/resources/chat) | Analyze images and generate text responses |

28 25 

29To learn more about the input and output modalities supported by our models, refer to our [models page](https://developers.openai.com/api/docs/models).26To learn more about the input and output modalities supported by our models, refer to our [models page](https://developers.openai.com/api/docs/models).

30 27 

31## Generate or edit images28## Generate or edit images

32 29 

33You can generate or edit images using the Image API or the Responses API.30With the Images API, choose `gpt-image-2` to generate images from text or edit existing images. With the Responses API, choose a mainline model that supports the image generation tool; the tool handles GPT Image model selection.

34 

35The state-of-the-art image generation model, `gpt-image-2`, can understand text and images and use broad world knowledge to generate images with strong instruction following and contextual awareness.

36 31 

37 32 

38 33 


158Files.write(Path.of("cat_and_otter.png"), Base64.getDecoder().decode(imageResult));153Files.write(Path.of("cat_and_otter.png"), Base64.getDecoder().decode(imageResult));

159```154```

160 155 

156```csharp

157using OpenAI.Responses;

158#pragma warning disable OPENAI001

159 

160string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

161ResponsesClient client = new(key);

162 

163CreateResponseOptions options = new()

164{

165 Model = "gpt-5.6",

166};

167options.InputItems.Add(

168 ResponseItem.CreateUserMessageItem(

169 "Generate an image of a gray tabby cat hugging an otter with an orange scarf."

170 )

171);

172options.Tools.Add(

173 ResponseTool.CreateImageGenerationTool(model: "gpt-image-2")

174);

175 

176ResponseResult response = await client.CreateResponseAsync(options);

177ImageGenerationCallResponseItem image = response

178 .OutputItems.OfType<ImageGenerationCallResponseItem>()

179 .FirstOrDefault()

180 ?? throw new InvalidOperationException("No generated image was returned.");

181await File.WriteAllBytesAsync(

182 "cat_and_otter.png",

183 image.ImageResultBytes.ToArray()

184);

185```

186 

161```ruby187```ruby

162require "base64"188require "base64"

163require "openai"189require "openai"


200 226 

201### Using world knowledge for image generation227### Using world knowledge for image generation

202 228 

203GPT Image models can use visual understanding of the world to generate lifelike images including real-life details without a reference.229GPT Image models can draw on world knowledge without a reference image. For example, a prompt for a cabinet of semi-precious stones can produce a scene containing recognizable gemstones such as amethyst, rose quartz, and jade.

204 

205For example, if you prompt GPT Image to generate an image of a glass cabinet with the most popular semi-precious stones, the model knows enough to select gemstones like amethyst, rose quartz, jade, etc, and depict them in a realistic way.

206 230 

207## Analyze images231## Analyze images

208 232 

209**Vision** is the ability for a model to "see" and understand images. If there is text in an image, the model can also understand the text.233Use a vision-capable model to describe images, read visible text, and answer questions about objects, shapes, colors, or textures. Account for the model's [limitations](#limitations) when using its answers.

210It can understand most visual elements, including objects, shapes, colors, and textures, even if there are some [limitations](#limitations).

211 234 

212### Giving a model images as input235### Giving a model images as input

213 236 


215 238 

216 239 

217 240 

218You can provide images as input to generation requests in multiple ways:241Provide an image for analysis in any of these ways:

219 242 

220- By providing a fully qualified URL to an image file243- By providing a fully qualified URL to an image file

221- By providing an image as a Base64-encoded data URL244- By providing an image as a Base64-encoded data URL


938 961 

939### Image input requirements962### Image input requirements

940 963 

941Input images must meet the following requirements to be used in the API.964Use supported image files that are clear enough for the model to analyze.

942 965 

943<table>966| Requirement | Supported inputs |

944 <tr>967| ------------ | ------------------------------------------------------------------------------------- |

945 <td>Supported file types</td>968| File types | PNG (`.png`), JPEG (`.jpeg` or `.jpg`), WEBP (`.webp`), and non-animated GIF (`.gif`) |

946 <td>969| Request size | Up to 512 MB total payload per request |

947 - PNG (`.png`) - JPEG (`.jpeg` and `.jpg`) - WEBP (`.webp`) - Non-animated970| Image count | Up to 1,500 images per request |

948 GIF (`.gif`)971 

949 </td>972Image tokens and the rest of your prompt must also fit the model's input and context limits. A token estimate does not guarantee that a request meets every input limit. Image use must comply with our [usage policies](https://openai.com/policies/usage-policies/).

950 </tr>

951 <tr>

952 <td>Size limits</td>

953 <td>

954 - Up to 512 MB total payload size per request - Up to 1500 individual

955 image inputs per request

956 </td>

957 </tr>

958 <tr>

959 <td>Other requirements</td>

960 <td>

961 - No watermarks or logos - No NSFW content - Clear enough for a human to

962 understand

963 </td>

964 </tr>

965</table>

966 973 

967### Choose an image detail level974### Choose an image detail level

968 975 

969The `detail` parameter tells the model what level of detail to use when processing and understanding the image (`low`, `high`, `original`, or `auto`). If you skip the parameter, the model will use `auto`. This behavior is the same in both the Responses API and the Chat Completions API. On `gpt-5.5` and GPT-5.6 models, `auto` and the default omitted behavior are equivalent to `original`.976The `detail` parameter controls image preprocessing. Supported values depend on the model: `low`, `high`, `original`, or `auto`. If you omit the parameter, it defaults to `auto` in both the Responses API and the Chat Completions API. The [model sizing table](#model-sizing-behavior) shows the corresponding behavior.

970 977 

971 978 

972 979 


984Use the following guidance to choose a detail level:991Use the following guidance to choose a detail level:

985 992 

986| Detail level | Best for |993| Detail level | Best for |

987| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |994| ------------ | --------------------------------------------------------------------------------------------------------------------------- |

988| `low` | Fast, low-cost understanding when fine visual detail is not important. The model receives a low-resolution 512px x 512px version of the image. |995| `low` | Coarse image understanding. Resizing and token use depend on the model; `low` does not always use fewer tokens than `high`. |

989| `high` | Standard high-fidelity image understanding when precise original-image coordinates are not required. |996| `high` | Standard high-fidelity image understanding when precise original-image coordinates are not required. |

990| `original` | Large, dense, spatially sensitive, or computer-use images. Available on `gpt-5.4` and future models. |997| `original` | Large, dense, spatially sensitive, or computer-use images, when supported by the model. |

991| `auto` | Automatic detail selection. On `gpt-5.5` and GPT-5.6 models, `auto` and the omitted/default behavior are equivalent to `original`. |998| `auto` | Use the model's default sizing behavior, shown in the model sizing table. |

992 999 

993For high-accuracy tasks that require fine visual detail or precise coordinates in the original image, such as optical character recognition (OCR), small-object detection, bounding boxes, localization, or computer use, set `"detail": "original"` when supported. The `low` and `high` detail levels may resize the image before analysis, which can obscure small details and cause model-generated coordinates to no longer match the original image. On `gpt-5.4` and `gpt-5.5`, `original` can also resize images that exceed the model's patch or dimension limits; for coordinate-sensitive tasks, resize those images before sending them and remap returned coordinates to the original image. Use `low` or `high` when lower cost or latency is more important than fine-detail recognition or spatial accuracy. See the [Computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for more detail.1000For tasks that require fine visual detail or precise coordinates, such as optical character recognition (OCR), small-object detection, or computer use, use `"detail": "original"` when supported. Original detail can still resize images that exceed the model's limits. For coordinate-sensitive tasks, resize images to fit those limits before sending them and map returned coordinates back to the original image. See the [Computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use) for coordinate handling.

994 

995Read more about how models resize images in the [Model sizing

996 behavior](#model-sizing-behavior) section, and about token costs in the

997 [Calculating costs](#calculating-costs) section below.

998 1001 

999### Model sizing behavior1002### Model sizing behavior

1000 1003 

1001Different models use different resizing rules before image tokenization:1004The following table covers the general-purpose vision models available in the [image input cost calculator](#image-input-cost-calculator). Other models and specialized variants can use different limits. All resizing preserves aspect ratio without enlarging smaller images.

1002 1005 

1003<table>1006<table>

1004 <tr>1007 <tr>


1007 <th>Patch and resizing behavior</th>1010 <th>Patch and resizing behavior</th>

1008 </tr>1011 </tr>

1009 <tr>1012 <tr>

1010 <td>GPT-5.6 family</td>1013 <td>

1014 `gpt-5.6-sol`, `gpt-5.6-terra`,

1015 `gpt-5.6-luna`

1016 </td>

1011 <td>1017 <td>

1012 `low`, `high`, `original`,1018 `low`, `high`, `original`,

1013 `auto`1019 `auto`

1014 </td>1020 </td>

1015 <td>1021 <td>

1016 `low` and `high` can resize images under their1022 `low` fits within 512 × 512 pixels. `high` fits

1017 finite limits. `original` preserves the input dimensions and1023 within 2048 × 2048 pixels and 2,500 patches. `original` fits

1018 does not resize the image to a pixel-dimension or patch-budget limit.1024 within 65,535 × 65,535 pixels, with no patch-budget limit.

1019 `auto` and omitted `detail` use the same sizing1025 `auto` uses the same sizing behavior as `original`.

1020 behavior as `original`. Request payload and other image-input

1021 limits still apply.

1022 </td>1026 </td>

1023 </tr>1027 </tr>

1024 <tr>1028 <tr>


1030 `auto`1034 `auto`

1031 </td>1035 </td>

1032 <td>1036 <td>

1033 `high` allows up to 2,500 patches or a 2048-pixel maximum1037 `low` fits within 512 × 512 pixels. `high` allows up

1034 dimension. `original` allows up to 10,000 patches or a1038 to 2,500 patches and a 2048-pixel maximum dimension. `original`

1035 6000-pixel maximum dimension. If either limit is exceeded, we resize the1039 allows up to 10,000 patches and a 6000-pixel maximum dimension. Both

1036 image while preserving aspect ratio to fit within the lesser of those two1040 limits apply. `auto` uses the same sizing behavior as

1037 constraints for the selected detail level. `auto` and omitted1041 `original`.

1038 `detail` use the same sizing behavior as

1039 `original`. [Full resizing details

1040 below.](#patch-based-image-tokenization)

1041 </td>1042 </td>

1042 </tr>1043 </tr>

1043 <tr>1044 <tr>

1044 <td>1045 <td>

1045 `gpt-5.4`1046 `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`

1046 </td>1047 </td>

1047 <td>1048 <td>

1048 `low`, `high`, `original`,1049 `low`, `high`, `original`,

1049 `auto`1050 `auto`

1050 </td>1051 </td>

1051 <td>1052 <td>

1052 `high` allows up to 2,500 patches or a 2048-pixel maximum1053 `low` uses a 2048-pixel maximum dimension and a 6,144-patch

1053 dimension. `original` allows up to 10,000 patches or a1054 budget, so it can use more tokens than `high`.

1054 6000-pixel maximum dimension. If either limit is exceeded, we resize the1055 `high` allows up to 2,500 patches and a 2048-pixel maximum

1055 image while preserving aspect ratio to fit within the lesser of those two1056 dimension. `original` allows up to 10,000 patches and a

1056 constraints for the selected detail level. `auto` and omitted1057 6000-pixel maximum dimension. Both limits apply. `auto` uses

1057 `detail` use the same sizing behavior as1058 the same sizing behavior as `high`.

1058 `high`. [Full resizing details

1059 below.](#patch-based-image-tokenization)

1060 </td>1059 </td>

1061 </tr>1060 </tr>

1062 <tr>1061 <tr>

1063 <td>1062 <td>

1064 `gpt-5.4-mini`, `gpt-5.4-nano`,1063 `gpt-5.2`, `gpt-4.1-mini`

1065 `gpt-5-mini`, `gpt-5-nano`, `gpt-5.2`,

1066 `gpt-5.3-codex`, `gpt-5-codex-mini`,

1067 `gpt-5.1-codex-mini`, `gpt-5.2-codex`,

1068 `gpt-5.2-chat-latest`, `o4-mini`, and the

1069 `gpt-4.1-mini` and `gpt-4.1-nano` 2025-04-14

1070 snapshot variants

1071 </td>1064 </td>

1072 <td>1065 <td>

1073 `low`, `high`, `auto`1066 `low`, `high`, `auto`

1074 </td>1067 </td>

1075 <td>1068 <td>

1076 `high` allows up to 1,536 patches or a 2048-pixel maximum1069 These detail levels use the same sizing limits: a 2048-pixel maximum

1077 dimension. If either limit is exceeded, we resize the image while1070 dimension and a 6,144-patch budget. `original` is not

1078 preserving aspect ratio to fit within the lesser of those two constraints.1071 supported.

1079 [Full resizing details below.](#patch-based-image-tokenization)

1080 </td>1072 </td>

1081 </tr>1073 </tr>

1082 <tr>1074 <tr>

1083 <td>1075 <td>

1084 `GPT-4o`, `GPT-4.1`, `GPT-4o-mini`,1076 `gpt-5.1`, `gpt-4.1`, `gpt-4o`,

1085 `computer-use-preview`, and o-series models except1077 `gpt-4o-mini`

1086 `o4-mini`

1087 </td>1078 </td>

1088 <td>1079 <td>

1089 `low`, `high`, `auto`1080 `low`, `high`, `auto`

1090 </td>1081 </td>

1091 <td>1082 <td>

1092 Use tile-based resizing behavior. See 1083 `low` uses a fixed token count. `high` and

1093 [the detailed behavior below](#gpt-4o-gpt-41-gpt-4o-mini-cua-and-o-series-except-o4-mini)1084 `auto` use the

1085 [tile-based sizing rules](#tile-based-image-tokenization).

1094 </td>1086 </td>

1095 </tr>1087 </tr>

1096</table>1088</table>

1097 1089 

1098## Calculating costs1090## Calculating costs

1099 1091 

1100Image inputs are metered and charged in token units similar to text inputs. How images are converted to text token inputs varies based on the model. You can find a vision pricing calculator in the FAQ section of the [pricing page](https://openai.com/api/pricing/).1092Vision models convert image inputs into billable input tokens. The calculator and patch/tile rules in this section cover vision-model inputs, not GPT Image generation or editing. See [GPT Image model inputs](#gpt-image-model-inputs) for that separate pricing.

1093 

1094Image tokens also count toward your [tokens per minute (TPM) limits](https://developers.openai.com/api/docs/guides/rate-limits). The calculator estimates one image at standard input rates; it does not include the rest of your prompt or model output.

1095 

1096### Image input cost calculator

1097 

1098Estimate input tokens and cost for one image.

1101 1099 

1102### Patch-based image tokenization1100### Patch-based image tokenization

1103 1101 

1104Some models tokenize images by covering them with 32px x 32px patches. Many model and detail-level combinations define a maximum patch budget. The token cost of an image is determined as follows:1102Some models tokenize images by covering them with 32px x 32px patches. Many model and detail-level combinations define a maximum patch budget. First, the API fits the image within the selected detail level's pixel-dimension limit, preserving aspect ratio and rounding to integer pixels without enlarging smaller images. The token cost is then determined as follows:

1105 1103 

1106A. Compute how many 32px x 32px patches are needed to cover the original image. A patch may extend beyond the image boundary.1104A. Compute how many 32px x 32px patches are needed to cover the image after applying the pixel-dimension limit. A patch may extend beyond the image boundary.

1107 1105 

1108```1106```

1109original_patch_count = ceil(width/32)×ceil(height/32)1107patch_count = ceil(width/32)×ceil(height/32)

1110```1108```

1111 1109 

1112For GPT-5.6 models with `detail` set to `original` or `auto`, the service uses the original patch count without resizing the image to a patch budget or pixel-dimension limit. This means large images can use more input tokens than they did with earlier models. To control token use and latency, resize the image before sending it or select `low` or `high` detail.1110GPT-5.6 Sol, Terra, and Luna have no patch-budget limit for `original` or `auto`. After applying their pixel-dimension limit, skip the patch-budget resizing step. Large images can therefore use more tokens than with earlier models; resize them before sending or select `low` or `high` to control token use.

1113 1111 

1114B. If the original image would exceed the model's patch budget, scale it down proportionally until it fits within that budget. Then adjust the scale so the final resized image stays within budget after converting to integer pixel dimensions and computing patch coverage.1112B. When a patch budget applies and the image exceeds it, scale the image down proportionally. Adjust the scale to stay within budget after converting to integer pixel dimensions and computing patch coverage. Keep full precision until calculating the final dimensions.

1115 1113 

1116```1114```

1117shrink_factor = sqrt((32^2 * patch_budget) / (width * height))1115shrink_factor = sqrt((32^2 * patch_budget) / (width * height))


1121)1119)

1122```1120```

1123 1121 

1124C. Convert the adjusted scale into integer pixel dimensions, then compute the number of patches needed to cover the resized image. This resized patch count is the image-token count before applying the model multiplier, and it is capped by the model's patch budget.1122C. If step B resized the image, round down the final scaled width and height to integer pixels. Compute the patches needed to cover the resulting image. This is the image-token count before applying the model multiplier. When a patch budget applies, this count stays within that budget.

1125 1123 

1126```1124```

1127resized_patch_count = ceil(resized_width/32)×ceil(resized_height/32)1125resized_patch_count = ceil(resized_width/32)×ceil(resized_height/32)

1128```1126```

1129 1127 

1130D. Apply a multiplier based on the model to get the total tokens:1128D. Multiply the patch count by the model's multiplier and round up to get the billable image input tokens. Apply the model's input price to those tokens once; the multiplier does not apply to other prompt tokens or to the price again.

1131 1129 

1132| Model | Multiplier |1130| Model | Multiplier |

1133| --------------- | ---------- |1131| -------------------------------------- | ---------- |

1134| `gpt-5.6-sol` | 1.2 |1132| `gpt-5.6-sol` | 1.2 |

1135| `gpt-5.6-terra` | 1.2 |1133| `gpt-5.6-terra` | 1.2 |

1136| `gpt-5.6-luna` | 1.2 |1134| `gpt-5.6-luna` | 1.2 |

1137| `gpt-5.5` | 1.2 |1135| `gpt-5.5` | 1.2 |

1136| `gpt-5.4` | 1.2 |

1138| `gpt-5.4-mini` | 1.2 |1137| `gpt-5.4-mini` | 1.2 |

1139| `gpt-5.4-nano` | 1.2 |1138| `gpt-5.4-nano` | 1.2 |

1140| `gpt-5-mini` | 1.2 |1139| `gpt-5.2` | 1.2 |

1141| `gpt-5-nano` | 1.5 |1140| `gpt-5-mini`\* | 1.2 |

1142| `gpt-4.1-mini*` | 1.62 |1141| `gpt-5-nano`\* | 1.5 |

1143| `gpt-4.1-nano*` | 2.46 |1142| `gpt-4.1-mini` | 1.62 |

1144| `o4-mini` | 1.72 |1143| `gpt-4.1-nano`\* (2025-04-14 snapshot) | 2.46 |

1145 1144| `o4-mini`\* | 1.72 |

1146_For `gpt-4.1-mini` and `gpt-4.1-nano`, this applies to the 2025-04-14 snapshot variants._

1147 

1148**Cost calculation examples for a model with a 1,536-patch budget**

1149 

1150- A 1024 × 1024 image has a post-resize patch count of **1024**

1151 - A. `original_patch_count = ceil(1024 / 32) * ceil(1024 / 32) = 32 * 32 = 1024`

1152 - B. `1024` is below the `1,536` patch budget, so no resize is needed.

1153 - C. `resized_patch_count = 1024`

1154 - Resized patch count before the model multiplier: `1024`

1155 - Multiply by the model's token multiplier to get the billed token units.

1156- A 1800 × 2400 image has a post-resize patch count of **1452**

1157 - A. `original_patch_count = ceil(1800 / 32) * ceil(2400 / 32) = 57 * 75 = 4275`

1158 - B. `4275` exceeds the `1,536` patch budget, so we first compute `shrink_factor = sqrt((32^2 * 1536) / (1800 * 2400)) = 0.603`.

1159 - We then adjust that scale so the final integer pixel dimensions stay within budget after patch counting: `adjusted_shrink_factor = 0.603 * min(floor(1800 * 0.603 / 32) / (1800 * 0.603 / 32), floor(2400 * 0.603 / 32) / (2400 * 0.603 / 32)) = 0.586`.

1160 - Resized image dimensions: `1056 × 1408`

1161 - C. `resized_patch_count = ceil(1056 / 32) * ceil(1408 / 32) = 33 * 44 = 1452`

1162 - Resized patch count before the model multiplier: `1452`

1163 - Multiply by the model's token multiplier to get the billed token units.

1164 1145 

1165### Tile-based image tokenization1146_For `gpt-4.1-mini`, this applies to the 2025-04-14 snapshot._

1166 1147 

1167#### GPT-4o, GPT-4.1, GPT-4o-mini, CUA, and o-series (except o4-mini)1148\* Deprecated and scheduled for shutdown. See the [deprecation schedule](https://developers.openai.com/api/docs/deprecations) for dates and replacements. These models aren't included in the calculator or the model sizing table above.

1168 1149 

1169The token cost of an image is determined by two factors: size and detail.1150**Cost calculation examples for `gpt-5.4` with `detail: high`**

1170 1151 

1171Any image with `"detail": "low"` costs a set, base number of tokens. This amount varies by model. To calculate the cost of an image with `"detail": "high"`, we do the following:1152This combination uses a 2048-pixel maximum dimension, a 2,500-patch budget, and a 1.2× multiplier.

1172 1153 

1173- Scale to fit in a 2048px x 2048px square, maintaining original aspect ratio1154- A 1024 × 1024 image needs `32 × 32 = 1024` patches. No resizing is needed. The billable image input is `ceil(1024 × 1.2) = 1229` tokens.

1174- Scale so that the image's shortest side is 768px long1155- A 2048 × 2048 image initially needs `64 × 64 = 4096` patches. The patch budget reduces it to 1600 × 1600 pixels, or `50 × 50 = 2500` patches. The estimate is `ceil(2500 × 1.2) = 3000` tokens.

1175- Count the number of 512px squares in the image. Each square costs a set amount of tokens, shown below.1156 

1176- Add the base tokens to the total1157Floating-point rounding in billing can make the final count differ from the estimate by one token.

1158 

1159### Tile-based image tokenization

1160 

1161<a id="gpt-4o-gpt-41-gpt-4o-mini-cua-and-o-series-except-o4-mini"></a>

1162 

1163The models in this table use a base token count plus tokens for image tiles:

1177 1164 

1178| Model | Base tokens | Tile tokens |1165| Model | Base tokens | Tile tokens |

1179| ------------------------------ | ----------- | ----------- |1166| -------------------------- | ----------- | ----------- |

1180| `gpt-5`, `gpt-5-chat-latest` | 70 | 140 |1167| `gpt-5.1` | 70 | 140 |

1181| `gpt-4o`, `gpt-4.1`, `gpt-4.5` | 85 | 170 |1168| `gpt-5`\* | 70 | 140 |

1169| `gpt-4o`, `gpt-4.1` | 85 | 170 |

1182| `gpt-4o-mini` | 2833 | 5667 |1170| `gpt-4o-mini` | 2833 | 5667 |

1183| `o1`, `o1-pro`, `o3` | 75 | 150 |1171| `o1`\*, `o1-pro`\*, `o3`\* | 75 | 150 |

1184| `computer-use-preview` | 65 | 129 |1172 

1173\* Deprecated and scheduled for shutdown. See the [deprecation schedule](https://developers.openai.com/api/docs/deprecations) for dates and replacements. These models aren't included in the calculator or the model sizing table above.

1174 

1175With `"detail": "low"`, an image costs only the model's base tokens, regardless of dimensions. With `"detail": "high"` or `"detail": "auto"`:

1185 1176 

1186### GPT Image 11177- Scale down to fit in a 2048px x 2048px square, maintaining aspect ratio. Smaller images are not enlarged.

1178- If the shortest side exceeds 768px, scale it down to 768px and round down the other dimension.

1179- Count the 512px squares needed to cover the image. Each square uses the model's tile tokens.

1180- Add the model's base tokens to the tile tokens.

1187 1181 

1188For GPT Image 1, we calculate the cost of an image input the same way as described above, except that we scale down the image so that the shortest side is 512px instead of 768px.1182### GPT Image model inputs

1189The price depends on the dimensions of the image and the [input fidelity](https://developers.openai.com/api/docs/guides/image-generation?image-generation-model=gpt-image-1#image-input-fidelity).1183 

1184GPT Image models use separate image-token pricing for generation and editing. The vision calculator does not estimate their input or output costs. For current rates, see [image generation pricing](https://developers.openai.com/api/docs/pricing#image-generation); for generation and editing workflows, see the [Image generation guide](https://developers.openai.com/api/docs/guides/image-generation).

1185 

1186#### GPT Image 1

1187 

1188The following input-token rules apply to `gpt-image-1`. Use tile-based image sizing, but scale the shortest side down to 512px instead of 768px. Token use depends on the image dimensions and the `input_fidelity` parameter in the [Images API](https://developers.openai.com/api/reference/resources/images/methods/edit).

1190 1189 

1191When input fidelity is set to low, the base cost is 65 image tokens, and each tile costs 129 image tokens.1190When input fidelity is set to low, the base cost is 65 image tokens, and each tile costs 129 image tokens.

1192When using high input fidelity, we add a set number of tokens based on the image's aspect ratio in addition to the image tokens described above.1191When using high input fidelity, we add a set number of tokens based on the image's aspect ratio in addition to the image tokens described above.


1198 1197 

1199## Limitations1198## Limitations

1200 1199 

1201While models with vision capabilities are powerful and can be used in many situations, it's important to understand the limitations of these models. Here are some known limitations:1200Vision models can make mistakes. Account for these limitations when designing your application:

1202 1201 

1203- **Medical images**: The model is not suitable for interpreting specialized medical images like CT scans and shouldn't be used for medical advice.1202- **Medical images**: The model is not suitable for interpreting specialized medical images like CT scans and shouldn't be used for medical advice.

1204- **Non-English**: The model may not perform optimally when handling images with text of non-Latin alphabets, such as Japanese or Korean.1203- **Non-English**: The model may not perform optimally when handling images with text of non-Latin alphabets, such as Japanese or Korean.


1208- **Spatial reasoning**: The model struggles with tasks requiring precise spatial localization, such as identifying chess positions.1207- **Spatial reasoning**: The model struggles with tasks requiring precise spatial localization, such as identifying chess positions.

1209- **Accuracy**: The model may generate incorrect descriptions or captions in certain scenarios.1208- **Accuracy**: The model may generate incorrect descriptions or captions in certain scenarios.

1210- **Image shape**: The model struggles with panoramic and fisheye images.1209- **Image shape**: The model struggles with panoramic and fisheye images.

1211- **Metadata and resizing**: The model doesn't process original file names or metadata. `low` and `high` detail, and models with finite image budgets, may resize images before analysis. GPT-5.6 models preserve the input dimensions with `original` and `auto` detail.1210- **Metadata and resizing**: The model doesn't process original file names or metadata. Images may be resized before analysis, including with `original` detail. See [Model sizing behavior](#model-sizing-behavior) for the limits that apply to each model.

1212- **Counting**: The model may give approximate counts for objects in images.1211- **Counting**: The model may give approximate counts for objects in images.

1213- **CAPTCHAs**: For safety reasons, our system blocks the submission of CAPTCHAs.1212- **CAPTCHAs**: For safety reasons, our system blocks the submission of CAPTCHAs.

1214 

1215 

1216We process images at the token level, so each image we process counts towards your tokens per minute (TPM) limit.

1217 

1218For the most precise and up-to-date estimates for image processing, please use our image pricing calculator available [here](https://openai.com/api/pricing/).

Details

269 .forEach(text -> System.out.println(text.text()));269 .forEach(text -> System.out.println(text.text()));

270```270```

271 271 

272```csharp

273using OpenAI.Chat;

274using OpenAI.Responses;

275#pragma warning disable OPENAI001

276 

277string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

278string model = "gpt-5.6";

279 

280ChatClient chat = new(model, key);

281 

282ChatCompletion completion = await chat.CompleteChatAsync(

283 [

284 new SystemChatMessage("You are a helpful assistant."),

285 new UserChatMessage("Hello!"),

286 ]

287);

288Console.WriteLine(completion.Content[0].Text);

289 

290ResponsesClient responses = new(key);

291 

292ResponseResult response = await responses.CreateResponseAsync(

293 model,

294 [

295 ResponseItem.CreateSystemMessageItem("You are a helpful assistant."),

296 ResponseItem.CreateUserMessageItem("Hello!"),

297 ]

298);

299Console.WriteLine(response.GetOutputText());

300```

301 

272```ruby302```ruby

273require "openai"303require "openai"

274 304 


396 .forEach(System.out::println);426 .forEach(System.out::println);

397```427```

398 428 

429```csharp

430using OpenAI.Chat;

431 

432string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

433string model = "gpt-5.6";

434ChatClient client = new(model, key);

435 

436ChatCompletion completion = await client.CompleteChatAsync(

437 [

438 new SystemChatMessage("You are a helpful assistant."),

439 new UserChatMessage("Hello!"),

440 ]

441);

442 

443Console.WriteLine(completion.Content[0].Text);

444```

445 

399```ruby446```ruby

400require "openai"447require "openai"

401 448 


506 .forEach(text -> System.out.println(text.text()));553 .forEach(text -> System.out.println(text.text()));

507```554```

508 555 

556```csharp

557using OpenAI.Responses;

558#pragma warning disable OPENAI001

559 

560string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

561ResponsesClient client = new(key);

562 

563CreateResponseOptions options = new()

564{

565 Model = "gpt-5.6",

566 Instructions = "You are a helpful assistant.",

567};

568options.InputItems.Add(ResponseItem.CreateUserMessageItem("Hello!"));

569 

570ResponseResult response = await client.CreateResponseAsync(options);

571 

572Console.WriteLine(response.GetOutputText());

573```

574 

509```ruby575```ruby

510require "openai"576require "openai"

511 577 


656 .forEach(System.out::println);722 .forEach(System.out::println);

657```723```

658 724 

725```csharp

726using OpenAI.Chat;

727 

728string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

729string model = "gpt-5.6";

730ChatClient client = new(model, key);

731 

732List<ChatMessage> messages =

733[

734 new SystemChatMessage("You are a helpful assistant."),

735 new UserChatMessage("What is the capital of France?"),

736];

737ChatCompletion first = await client.CompleteChatAsync(messages);

738 

739messages.Add(new AssistantChatMessage(first));

740messages.Add(new UserChatMessage("And its population?"));

741ChatCompletion second = await client.CompleteChatAsync(messages);

742 

743Console.WriteLine(second.Content[0].Text);

744```

745 

659```ruby746```ruby

660require "openai"747require "openai"

661 748 


945 .forEach(text -> System.out.println(text.text()));1032 .forEach(text -> System.out.println(text.text()));

946```1033```

947 1034 

1035```csharp

1036using OpenAI.Responses;

1037#pragma warning disable OPENAI001

1038 

1039string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

1040ResponsesClient client = new(key);

1041 

1042ResponseResult first = await client.CreateResponseAsync(

1043 "gpt-5.6",

1044 "What is the capital of France?"

1045);

1046ResponseResult second = await client.CreateResponseAsync(

1047 "gpt-5.6",

1048 "And its population?",

1049 previousResponseId: first.Id

1050);

1051 

1052Console.WriteLine(second.GetOutputText());

1053```

1054 

948```ruby1055```ruby

949require "openai"1056require "openai"

950 1057 


1217 .forEach(System.out::println);1324 .forEach(System.out::println);

1218```1325```

1219 1326 

1327```csharp

1328using OpenAI.Chat;

1329#pragma warning disable OPENAI001

1330 

1331string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

1332string model = "gpt-5.6";

1333ChatClient client = new(model, key);

1334 

1335BinaryData schema = BinaryData.FromString(

1336 """

1337 {

1338 "type": "object",

1339 "properties": {

1340 "name": { "type": "string", "minLength": 1 },

1341 "age": { "type": "number", "minimum": 0, "maximum": 130 }

1342 },

1343 "required": ["name", "age"],

1344 "additionalProperties": false

1345 }

1346 """

1347);

1348ChatCompletionOptions options = new()

1349{

1350 ReasoningEffortLevel = ChatReasoningEffortLevel.Medium,

1351 ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(

1352 "person",

1353 schema,

1354 jsonSchemaIsStrict: true

1355 ),

1356};

1357 

1358ChatCompletion completion = await client.CompleteChatAsync(

1359 [new UserChatMessage("Jane, 54 years old")],

1360 options

1361);

1362 

1363Console.WriteLine(completion.Content[0].Text);

1364```

1365 

1220```ruby1366```ruby

1221require "openai"1367require "openai"

1222 1368 


1434 .forEach(text -> System.out.println(text.text()));1580 .forEach(text -> System.out.println(text.text()));

1435```1581```

1436 1582 

1583```csharp

1584using OpenAI.Responses;

1585#pragma warning disable OPENAI001

1586 

1587string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

1588ResponsesClient client = new(key);

1589 

1590BinaryData schema = BinaryData.FromString(

1591 """

1592 {

1593 "type": "object",

1594 "properties": {

1595 "name": { "type": "string", "minLength": 1 },

1596 "age": { "type": "number", "minimum": 0, "maximum": 130 }

1597 },

1598 "required": ["name", "age"],

1599 "additionalProperties": false

1600 }

1601 """

1602);

1603CreateResponseOptions options = new()

1604{

1605 Model = "gpt-5.6",

1606 TextOptions = new ResponseTextOptions

1607 {

1608 TextFormat = ResponseTextFormat.CreateJsonSchemaFormat(

1609 "person",

1610 schema,

1611 jsonSchemaIsStrict: true

1612 ),

1613 },

1614};

1615options.InputItems.Add(

1616 ResponseItem.CreateUserMessageItem("Jane, 54 years old")

1617);

1618 

1619ResponseResult response = await client.CreateResponseAsync(options);

1620 

1621Console.WriteLine(response.GetOutputText());

1622```

1623 

1437```ruby1624```ruby

1438require "openai"1625require "openai"

1439 1626 

Details

287System.out.println(moderation.results().get(0).flagged());287System.out.println(moderation.results().get(0).flagged());

288```288```

289 289 

290```csharp

291using OpenAI.Moderations;

292 

293string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

294string model = "omni-moderation-latest";

295ModerationClient client = new(model, key);

296 

297ModerationResult result = await client.ClassifyTextAsync(

298 "Text to classify goes here."

299);

300 

301Console.WriteLine($"Flagged: {result.Flagged}");

302Console.WriteLine(

303 $"Violence: {result.Violence.Flagged}; score: {result.Violence.Score:F3}"

304);

305```

306 

290```ruby307```ruby

291require "openai"308require "openai"

292 309 


434System.out.println(moderation.results().get(0).flagged());451System.out.println(moderation.results().get(0).flagged());

435```452```

436 453 

454```csharp

455using OpenAI.Moderations;

456#pragma warning disable OPENAI001

457 

458string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

459string model = "omni-moderation-latest";

460ModerationClient client = new(model, key);

461 

462ModerationResult result = await client.ClassifyInputsAsync(

463 [

464 ModerationInputPart.CreateTextPart("Text to classify goes here."),

465 ModerationInputPart.CreateImagePart(

466 new Uri(

467 "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg"

468 )

469 ),

470 ]

471);

472 

473Console.WriteLine($"Flagged: {result.Flagged}");

474Console.WriteLine(

475 $"Violence: {result.Violence.Flagged}; score: {result.Violence.Score:F3}; inputs: {result.Violence.ApplicableInputKinds}"

476);

477```

478 

437```ruby479```ruby

438require "openai"480require "openai"

439 481 

guides/mutual-tls.md +246 −0 created

Details

1# Mutual TLS

2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 

5Mutual TLS (mTLS) adds TLS client certificate verification to OpenAI API

6requests. After you activate a trusted certificate for an organization or

7project, requests in that scope must present an accepted client certificate in

8addition to their normal bearer credential.

9 

10Use mTLS when a workload can securely hold a client private key and you want

11OpenAI to verify its certificate identity before authorizing an API request.

12mTLS does not replace API keys, service-account credentials, or workload

13identity access tokens.

14 

15X.509 workload identity federation uses the same active mTLS trust anchors.

16 The certificate exchange returns a short-lived bearer token, and later API

17 calls still send that bearer token plus an accepted API mTLS certificate. See

18 [Configure workload identity federation with X.509

19 certificates](https://developers.openai.com/api/docs/guides/workload-identity-federation/x509).

20 

21## Before you configure mTLS

22 

23Any API organization can manage mTLS through normal role-based access control

24(RBAC):

25 

26- `api.mtls.read` lets a principal list, view, and test certificate settings.

27- `api.mtls.write` lets a principal upload, update, activate, deactivate, and

28 delete certificates.

29 

30The organization owner role includes these permissions, but you can grant them

31through a custom role. For more information, see [Manage permissions in the

32OpenAI platform](https://developers.openai.com/api/docs/guides/rbac).

33 

34Prepare:

35 

36- A client certificate and its private key for each workload.

37- Any intermediate certificates needed to build a path from the client

38 certificate to your trust anchor.

39- A stable PEM-encoded trust anchor that you can activate at the organization

40 or project level.

41- A non-critical project and a tested recovery path before you enable mTLS for

42 production traffic.

43 

44Keep private keys outside source control. Do not log private keys, certificate

45contents, or bearer credentials.

46 

47## Upload and activate trust

48 

49Upload stores a certificate but does not enforce mTLS. Activation is the step

50that changes request behavior.

51 

521. Open [Organization settings > Security > Mutual

53 TLS](https://platform.openai.com/settings/organization/security/mtls).

542. Upload one PEM-encoded trust anchor for each certificate object. Give it a

55 name that identifies the authority and rotation generation.

563. Optionally, add a [CEL filter](#filter-client-certificates-with-cel) that

57 constrains which verified client certificates that anchor can accept.

584. Activate the certificate for a non-critical project first. Send

59 representative requests through an [mTLS API host](#use-an-mtls-host) from

60 every expected workload.

615. Activate the certificate for other projects or for the organization

62 after validation succeeds.

63 

64You can also manage certificates through the API:

65 

66| Task | Endpoint |

67| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

68| Upload a certificate | `POST /v1/organization/certificates` |

69| List organization certificates | `GET /v1/organization/certificates` |

70| Retrieve, update, or delete a certificate | `GET`, `POST`, or `DELETE /v1/organization/certificates/{certificate_id}` |

71| Activate or deactivate for an organization | `POST /v1/organization/certificates/activate` or `POST /v1/organization/certificates/deactivate` |

72| List, activate, or deactivate for a project | `GET /v1/organization/projects/{project_id}/certificates`, `POST /v1/organization/projects/{project_id}/certificates/activate`, or `POST /v1/organization/projects/{project_id}/certificates/deactivate` |

73 

74Use a credential with the required `api.mtls.read` or `api.mtls.write`

75permission. For request and response schemas, see the [organization

76certificates API reference](https://developers.openai.com/api/reference/resources/admin/subresources/organization).

77 

78## Certificate requirements

79 

80Use one PEM-encoded trust anchor per certificate object. The upload must

81contain a valid certificate that expires more than one day after upload. The

82client certificate must include an Authority Key Identifier (AKI) for request

83verification.

84 

85For a request to pass mTLS:

86 

87- The client certificate must be valid at request time and suitable for TLS

88 client authentication.

89- The client certificate must build a valid path to an active organization- or

90 project-level trust anchor.

91- If the path includes intermediate certificates, the client must present them

92 during the TLS handshake.

93- The configured trust anchor and the client chain must pass standard X.509

94 client-certificate path validation.

95 

96If an upload contains more than one PEM-encoded certificate, request-chain

97verification uses only the first configured certificate as the anchor; do not

98rely on PEM-bundle semantics.

99 

100OpenAI does not fetch missing intermediates from Authority Information Access

101(AIA) URLs and does not perform certificate revocation list (CRL) or Online

102Certificate Status Protocol (OCSP) checks. Present the complete required chain

103and manage incident response through certificate rotation, deactivation, and

104your own certificate lifecycle controls.

105 

106## Understand verification order

107 

108OpenAI checks active project-level certificates before active

109organization-level certificates. If neither scope has an active certificate,

110mTLS does not add a certificate check to the request.

111 

112When an active certificate exists, OpenAI verifies client identity in this

113order:

114 

1151. OpenAI first attempts the existing direct path, which verifies the client

116 certificate directly against an active anchor without request

117 intermediates.

1182. After an ordinary direct-path no-match, OpenAI tries request-chain

119 verification with the client certificate and the intermediate certificates

120 presented by the TLS connection.

1213. If a path verifies, OpenAI evaluates the active certificate's CEL filter, if

122 present, against the verified client certificate.

123 

124Request-chain verification is available by default.

125 

126The request-chain path is a fallback after an ordinary no-match, not a recovery

127path for every direct-path error. Missing or malformed certificate material, a

128missing AKI, or a deterministic error after the direct path selects an

129anchor can fail the request without trying the presented chain.

130 

131## Filter client certificates with CEL

132 

133Attach an optional Common Expression Language (CEL) filter to an uploaded

134certificate to constrain the verified client certificates that anchor accepts.

135The expression must evaluate to a boolean and runs against the verified client

136certificate on both the direct and request-chain paths.

137 

138CEL exposes these fields:

139 

140- `subject.common_name`, `subject.country_code`, `subject.organization`,

141 `subject.organizational_unit`, `subject.locality`, `subject.province`,

142 `subject.street_address`, and `subject.postal_code`.

143- `subject_alt_names`, a list whose entries expose `type`, `value`, and `oid`.

144 Supported SAN type identifiers are `DNS`, `EMAIL`, `IP_ADDRESS`, `URI`, and

145 `CUSTOM`.

146 

147For example, require a production organizational unit and a DNS SAN in a

148specific namespace:

149 

150```text

151subject.organizational_unit == "Production" &&

152subject_alt_names.exists(san, san.type == DNS && san.value.endsWith(".example.com"))

153```

154 

155A certificate that verifies but does not match the filter fails with

156`certificate_attribute_verification_failed`. OpenAI rejects a policy that does not pass validation when you save it.

157 

158## Use an mTLS host

159 

160Send API traffic to an mTLS host instead of `api.openai.com`:

161 

162| Host | Use |

163| ------------------------ | ------------------------------------- |

164| `mtls.api.openai.com` | Default API mTLS host. |

165| `mtls-us.api.openai.com` | United States regional API mTLS host. |

166| `mtls-eu.api.openai.com` | EU regional API mTLS host. |

167 

168mTLS is host-based. Use the same `/v1` route you would call on the

169corresponding API surface, and test each API and model that your workload

170uses. Route and model availability can differ across regional hosts.

171 

172For example, send a normal bearer credential and a client certificate to the

173default mTLS host:

174 

175```bash

176export OPENAI_MTLS_CERT_CHAIN="/path/to/client-chain.pem"

177export OPENAI_MTLS_KEY="/path/to/client-key.pem"

178 

179curl https://mtls.api.openai.com/v1/models \

180 --cert "$OPENAI_MTLS_CERT_CHAIN" \

181 --key "$OPENAI_MTLS_KEY" \

182 --header "Authorization: Bearer $OPENAI_API_KEY"

183```

184 

185The certificate-chain file should contain the client certificate first,

186followed by any required intermediates. Do not send certificate material in

187HTTP headers or request bodies.

188 

189X.509 workload identity federation uses a separate exact exchange endpoint:

190`POST https://mtls.auth.openai.com/oauth/token`. That exchange produces a

191short-lived bearer token; it does not provide certificate-only API authentication. For

192the complete request shape, see the [workload identity token exchange

193reference](https://developers.openai.com/api/reference/workload-identity-federation#exchange-an-x509-certificate).

194 

195## Rotate certificates

196 

197Rotate trust anchors with overlap so existing workloads keep working:

198 

1991. Upload the new trust anchor without deactivating the old one.

2002. Activate the new anchor in each intended project or at the organization

201 level.

2023. Update workloads to present client certificates that chain to the new

203 anchor, then test each mTLS host and API surface they use.

2044. Deactivate the old anchor after all workloads have moved.

2055. Delete the old certificate only after you deactivate it for the organization

206 and every project.

207 

208You can rotate intermediates without changing the configured trust anchor.

209Present the new complete chain on later requests.

210 

211## Troubleshoot requests

212 

213Use stable error codes to distinguish configuration errors from temporary

214service errors:

215 

216| Error code | What to check |

217| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |

218| `certificate_required` | An active certificate applies, but the request did not present required client certificate material. |

219| `invalid_certificate` | OpenAI cannot decode or parse the client certificate, or the certificate lacks the AKI required for verification. |

220| `certificate_verification_failed` | The client certificate or presented chain does not reach an active trust anchor. |

221| `certificate_attribute_verification_failed` | The certificate path verified, but the CEL filter rejected the verified client certificate. |

222| `authentication_temporarily_unavailable` | A verifier timeout, internal dependency error, or CEL evaluator error caused HTTP `503`. Retry with your normal transient-error policy. |

223 

224For management requests, `mtls_certificate_invalid` means the uploaded PEM

225did not pass validation, `expired_certificate` means it expires too soon or has

226expired, `mtls_cel_policy_invalid` means the filter does not pass validation, and

227`certificate_in_use` means you must deactivate the certificate before deleting

228it.

229 

230## Current limitations

231 

232- An organization can upload up to 50 certificate objects.

233- mTLS adds certificate verification to normal API authentication; it does not provide

234 certificate-only API authorization.

235- OpenAI does not fetch AIA intermediates and does not perform CRL or OCSP

236 checks.

237- Private Link is not compatible with mTLS. See [Private

238 Link](https://developers.openai.com/api/docs/guides/private-link) when you need a private Azure network

239 path instead.

240- The supported API mTLS hosts are `mtls.api.openai.com`,

241 `mtls-us.api.openai.com`, and `mtls-eu.api.openai.com`. Do not assume every

242 other regional API host has an mTLS counterpart.

243- X.509 workload identity federation does not return a refresh token and does

244 not use DPoP, a `cnf` claim, or a certificate-bound bearer token. See

245 [Configure workload identity federation with X.509

246 certificates](https://developers.openai.com/api/docs/guides/workload-identity-federation/x509).

Details

280 .forEach(text -> System.out.println(text.text()));280 .forEach(text -> System.out.println(text.text()));

281```281```

282 282 

283```csharp

284using OpenAI.Responses;

285#pragma warning disable OPENAI001

286 

287string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

288ResponsesClient client = new(key);

289 

290CreateResponseOptions options = new()

291{

292 Model = "gpt-5.6",

293 Instructions = "Talk like a pirate.",

294 ReasoningOptions = new ResponseReasoningOptions

295 {

296 ReasoningEffortLevel = ResponseReasoningEffortLevel.Low,

297 },

298};

299options.InputItems.Add(

300 ResponseItem.CreateUserMessageItem("Are semicolons optional in JavaScript?")

301);

302 

303ResponseResult response = await client.CreateResponseAsync(options);

304 

305Console.WriteLine(response.GetOutputText());

306```

307 

283```ruby308```ruby

284require "openai"309require "openai"

285 310 


430 .forEach(text -> System.out.println(text.text()));455 .forEach(text -> System.out.println(text.text()));

431```456```

432 457 

458```csharp

459using OpenAI.Responses;

460#pragma warning disable OPENAI001

461 

462string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

463ResponsesClient client = new(key);

464 

465CreateResponseOptions options = new()

466{

467 Model = "gpt-5.6",

468 ReasoningOptions = new ResponseReasoningOptions

469 {

470 ReasoningEffortLevel = ResponseReasoningEffortLevel.Low,

471 },

472};

473options.InputItems.Add(

474 ResponseItem.CreateDeveloperMessageItem("Talk like a pirate.")

475);

476options.InputItems.Add(

477 ResponseItem.CreateUserMessageItem("Are semicolons optional in JavaScript?")

478);

479 

480ResponseResult response = await client.CreateResponseAsync(options);

481 

482Console.WriteLine(response.GetOutputText());

483```

484 

433```ruby485```ruby

434require "openai"486require "openai"

435 487 

Details

121 .forEach(text -> System.out.println(text.text()));121 .forEach(text -> System.out.println(text.text()));

122```122```

123 123 

124```csharp

125using OpenAI.Responses;

126#pragma warning disable OPENAI001

127 

128string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

129ResponsesClient client = new(key);

130 

131string prompt =

132 """

133 Write a bash script that takes a matrix represented as a string with format

134 '[1,2],[3,4],[5,6]' and prints the transpose in the same format.

135 """;

136CreateResponseOptions options = new()

137{

138 Model = "gpt-5.6",

139 ReasoningOptions = new ResponseReasoningOptions

140 {

141 ReasoningEffortLevel = ResponseReasoningEffortLevel.Low,

142 },

143};

144options.InputItems.Add(ResponseItem.CreateUserMessageItem(prompt));

145 

146ResponseResult response = await client.CreateResponseAsync(options);

147 

148Console.WriteLine(response.GetOutputText());

149```

150 

124```ruby151```ruby

125require "openai"152require "openai"

126 153 


443 470 

444`all_turns` has an effect only when the request has access to earlier response items. Use `previous_response_id`, attach the response to a conversation, or manually replay the complete response history. On the first request, `current_turn` and `all_turns` behave the same because no earlier reasoning exists.471`all_turns` has an effect only when the request has access to earlier response items. Use `previous_response_id`, attach the response to a conversation, or manually replay the complete response history. On the first request, `current_turn` and `all_turns` behave the same because no earlier reasoning exists.

445 472 

473Persisted reasoning can be reused only within the same model family. For example, `gpt-5.6-sol`, `gpt-5.6-terra`, and `gpt-5.6-luna` can reuse each other's reasoning, but reasoning does not carry between the GPT-5.6 and GPT-5.5 families.

474 

475When you switch model families, the API omits incompatible reasoning from the model's context, even when `reasoning.context` is `all_turns`.

476 

446### Continue reasoning with stored responses477### Continue reasoning with stored responses

447 478 

448Use `previous_response_id` for the shortest stateful integration:479Use `previous_response_id` for the shortest stateful integration:


1528 .forEach(text -> System.out.println(text.text()));1559 .forEach(text -> System.out.println(text.text()));

1529```1560```

1530 1561 

1562```csharp

1563using OpenAI.Responses;

1564#pragma warning disable OPENAI001

1565 

1566string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

1567ResponsesClient client = new(key);

1568 

1569string prompt =

1570 """

1571 I want to build a Python app that looks up user questions in a database where

1572 they are mapped to answers. If there is a close match, it retrieves the answer.

1573 Otherwise, it asks the user for an answer and stores the question and answer.

1574 Plan the directory structure, then return each file in full.

1575 Only supply your reasoning at the beginning and end, not throughout the code.

1576 """;

1577ResponseResult response = await client.CreateResponseAsync("gpt-5.6", prompt);

1578 

1579Console.WriteLine(response.GetOutputText());

1580```

1581 

1531```ruby1582```ruby

1532require "openai"1583require "openai"

1533 1584 

Details

95System.out.println(result.asTranscription().text());95System.out.println(result.asTranscription().text());

96```96```

97 97 

98```csharp

99using OpenAI.Audio;

100 

101string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

102string model = "gpt-transcribe";

103AudioClient client = new(model, key);

104 

105await using FileStream audio = File.OpenRead("audio.wav");

106AudioTranscription transcription = await client.TranscribeAudioAsync(

107 audio,

108 "audio.wav"

109);

110 

111Console.WriteLine(transcription.Text);

112```

113 

98```ruby114```ruby

99require "openai"115require "openai"

100require "pathname"116require "pathname"


588System.out.println(result.asTranslation().text());604System.out.println(result.asTranslation().text());

589```605```

590 606 

607```csharp

608using OpenAI.Audio;

609 

610string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

611AudioClient client = new("whisper-1", key);

612 

613await using FileStream audio = File.OpenRead("german.wav");

614AudioTranslation translation = await client.TranslateAudioAsync(

615 audio,

616 "german.wav"

617);

618 

619Console.WriteLine(translation.Text);

620```

621 

591```ruby622```ruby

592require "openai"623require "openai"

593require "pathname"624require "pathname"

Details

185 .forEach(text -> System.out.println(text.text()));185 .forEach(text -> System.out.println(text.text()));

186```186```

187 187 

188```csharp

189using OpenAI.Responses;

190#pragma warning disable OPENAI001

191 

192string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

193ResponsesClient client = new(key);

194 

195BinaryData schema = BinaryData.FromString(

196 """

197 {

198 "type": "object",

199 "properties": {

200 "name": { "type": "string" },

201 "date": { "type": "string" },

202 "participants": {

203 "type": "array",

204 "items": { "type": "string" }

205 }

206 },

207 "required": ["name", "date", "participants"],

208 "additionalProperties": false

209 }

210 """

211);

212CreateResponseOptions options = new()

213{

214 Model = "gpt-5.6",

215 TextOptions = new ResponseTextOptions

216 {

217 TextFormat = ResponseTextFormat.CreateJsonSchemaFormat(

218 "event",

219 schema,

220 jsonSchemaIsStrict: true

221 ),

222 },

223};

224options.InputItems.Add(

225 ResponseItem.CreateSystemMessageItem("Extract the event information.")

226);

227options.InputItems.Add(

228 ResponseItem.CreateUserMessageItem(

229 "Alice and Bob are going to a science fair on Friday."

230 )

231);

232 

233ResponseResult response = await client.CreateResponseAsync(options);

234 

235Console.WriteLine(response.GetOutputText());

236```

237 

188```ruby238```ruby

189require "openai"239require "openai"

190 240 

guides/text.md +52 −0

Details

268 .forEach(text -> System.out.println(text.text()));268 .forEach(text -> System.out.println(text.text()));

269```269```

270 270 

271```csharp

272using OpenAI.Responses;

273#pragma warning disable OPENAI001

274 

275string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

276ResponsesClient client = new(key);

277 

278CreateResponseOptions options = new()

279{

280 Model = "gpt-5.6",

281 Instructions = "Talk like a pirate.",

282 ReasoningOptions = new ResponseReasoningOptions

283 {

284 ReasoningEffortLevel = ResponseReasoningEffortLevel.Low,

285 },

286};

287options.InputItems.Add(

288 ResponseItem.CreateUserMessageItem("Are semicolons optional in JavaScript?")

289);

290 

291ResponseResult response = await client.CreateResponseAsync(options);

292 

293Console.WriteLine(response.GetOutputText());

294```

295 

271```ruby296```ruby

272require "openai"297require "openai"

273 298 


418 .forEach(text -> System.out.println(text.text()));443 .forEach(text -> System.out.println(text.text()));

419```444```

420 445 

446```csharp

447using OpenAI.Responses;

448#pragma warning disable OPENAI001

449 

450string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

451ResponsesClient client = new(key);

452 

453CreateResponseOptions options = new()

454{

455 Model = "gpt-5.6",

456 ReasoningOptions = new ResponseReasoningOptions

457 {

458 ReasoningEffortLevel = ResponseReasoningEffortLevel.Low,

459 },

460};

461options.InputItems.Add(

462 ResponseItem.CreateDeveloperMessageItem("Talk like a pirate.")

463);

464options.InputItems.Add(

465 ResponseItem.CreateUserMessageItem("Are semicolons optional in JavaScript?")

466);

467 

468ResponseResult response = await client.CreateResponseAsync(options);

469 

470Console.WriteLine(response.GetOutputText());

471```

472 

421```ruby473```ruby

422require "openai"474require "openai"

423 475 

Details

123}123}

124```124```

125 125 

126```csharp

127using OpenAI.Audio;

128#pragma warning disable OPENAI001

129 

130string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

131string model = "gpt-4o-mini-tts";

132AudioClient client = new(model, key);

133 

134BinaryData audio = await client.GenerateSpeechAsync(

135 "Today is a wonderful day to build something people love!",

136 GeneratedSpeechVoice.Coral,

137 new SpeechGenerationOptions

138 {

139 Instructions = "Speak in a cheerful and positive tone.",

140 }

141);

142 

143await File.WriteAllBytesAsync("speech.mp3", audio.ToArray());

144```

145 

126```ruby146```ruby

127require "openai"147require "openai"

128 148 

guides/tools.md +24 −12

Details

229#pragma warning disable OPENAI001229#pragma warning disable OPENAI001

230 230 

231string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;231string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

232string vectorStoreId = "<vector_store_id>";

232ResponsesClient client = new(key);233ResponsesClient client = new(key);

233 234 

234CreateResponseOptions options = new() { Model = "gpt-5.6" };235CreateResponseOptions options = new() { Model = "gpt-5.6" };

235options.Tools.Add(236options.Tools.Add(

236 ResponseTool.CreateFileSearchTool(["<vector_store_id>"])237 ResponseTool.CreateFileSearchTool([vectorStoreId])

237);238);

238options.InputItems.Add(239options.InputItems.Add(

239 ResponseItem.CreateUserMessageItem("What is deep research by OpenAI?")240 ResponseItem.CreateUserMessageItem("What is deep research by OpenAI?")


693```694```

694 695 

695```csharp696```csharp

696using System.Text.Json;

697using System.Text.Json.Serialization.Metadata;

698using OpenAI.Responses;697using OpenAI.Responses;

699#pragma warning disable CA1869

700#pragma warning disable OPENAI001698#pragma warning disable OPENAI001

701 699 

702string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;700string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;


729 ResponseItem.CreateUserMessageItem("What is the weather like in Paris today?")727 ResponseItem.CreateUserMessageItem("What is the weather like in Paris today?")

730);728);

731 729 

732ResponseResult response = client.CreateResponse(options);730ResponseResult response = await client.CreateResponseAsync(options);

733Console.WriteLine(731foreach (ResponseItem outputItem in response.OutputItems)

734 JsonSerializer.Serialize(732{

735 response.OutputItems[0],733 if (outputItem is FunctionCallResponseItem functionCall)

736 new JsonSerializerOptions

737 {734 {

738 TypeInfoResolver = new DefaultJsonTypeInfoResolver(),735 Console.WriteLine(

736 $"{functionCall.FunctionName}({functionCall.FunctionArguments})"

737 );

739 }738 }

740 )739 else if (outputItem is MessageResponseItem message)

741);740 {

741 foreach (ResponseContentPart content in message.Content)

742 {

743 if (content.Kind == ResponseContentPartKind.OutputText)

744 {

745 Console.WriteLine(content.Text);

746 }

747 else if (content.Kind == ResponseContentPartKind.Refusal)

748 {

749 Console.WriteLine(content.Refusal);

750 }

751 }

752 }

753}

742```754```

743 755 

744```ruby756```ruby

Details

867 )867 )

868);868);

869 869 

870// STEP 1: Create a response that requests tool-call approval.870// Step 1: Create a response that requests tool-call approval.

871options.InputItems.Add(ResponseItem.CreateUserMessageItem("Roll 2d4+1"));871options.InputItems.Add(ResponseItem.CreateUserMessageItem("Roll 2d4+1"));

872ResponseResult response1 = await client.CreateResponseAsync(options);872ResponseResult response1 = await client.CreateResponseAsync(options);

873 873 

874McpToolCallApprovalRequestItem approvalRequest =874McpToolCallApprovalRequestItem approvalRequest =

875 response1.OutputItems.OfType<McpToolCallApprovalRequestItem>().Single();875 response1.OutputItems.OfType<McpToolCallApprovalRequestItem>().Single();

876 876 

877// STEP 2: Approve the tool call and get the final response.877// Step 2: Approve the tool call and get the final response.

878options.PreviousResponseId = response1.Id;878options.PreviousResponseId = response1.Id;

879options.InputItems.Clear();879options.InputItems.Clear();

880options.InputItems.Add(880options.InputItems.Add(

Details

8it for a short-lived OpenAI access token.8it for a short-lived OpenAI access token.

9 9 

10OpenAI API workloads can also exchange a verified certificate identity through10OpenAI API workloads can also exchange a verified certificate identity through

11the X.509 workload identity federation beta.11X.509 workload identity federation.

12 12 

13You can use workload identity federation with the OpenAI API or Codex:13You can use workload identity federation with the OpenAI API or Codex:

14 14 


69 69 

70 70 

71 71 

72 - **[X.509 certificates (beta)](https://developers.openai.com/api/docs/guides/workload-identity-federation/x509)**: Configure certificate-backed exchange with the X.509 beta.72 - **[X.509 certificates](https://developers.openai.com/api/docs/guides/workload-identity-federation/x509)**: Configure certificate-backed exchange for OpenAI API workloads.

73- **[Kubernetes](https://developers.openai.com/api/docs/guides/workload-identity-federation/kubernetes)**: Use projected service account tokens in self-managed clusters.73- **[Kubernetes](https://developers.openai.com/api/docs/guides/workload-identity-federation/kubernetes)**: Use projected service account tokens in self-managed clusters.

74- **[AWS](https://developers.openai.com/api/docs/guides/workload-identity-federation/aws)**: Use outbound identity federation or Amazon EKS projected tokens.74- **[AWS](https://developers.openai.com/api/docs/guides/workload-identity-federation/aws)**: Use outbound identity federation or Amazon EKS projected tokens.

75- **[Microsoft Azure](https://developers.openai.com/api/docs/guides/workload-identity-federation/microsoft-azure)**: Use managed identity tokens or AKS projected service account tokens.75- **[Microsoft Azure](https://developers.openai.com/api/docs/guides/workload-identity-federation/microsoft-azure)**: Use managed identity tokens or AKS projected service account tokens.


93 93 

94## Use workload identity with the OpenAI API94## Use workload identity with the OpenAI API

95 95 

96Use this path when your workload calls the OpenAI API directly. You must be an96Use this path when your workload calls the OpenAI API directly. You need

97organization owner to configure it.97permission to manage Workload Identity Providers and service account mappings

98for the organization.

98 99 

99Go to [Organization Settings > Security > Workload Identity Provider](https://platform.openai.com/settings/organization/security/workload-identity-provider).100Go to [Organization Settings > Security > Workload Identity Provider](https://platform.openai.com/settings/organization/security/workload-identity-provider).

100Create the provider first, then configure its service account mappings from the101Create the provider first, then configure its service account mappings from the

101provider details page.102provider details page.

102 103 

103### X.509 providers (beta)104### X.509 providers

104 

105X.509 workload identity federation is available in beta. If X.509 doesn't

106 appear as a provider type, contact your system administrator. Your

107 administrator can work with OpenAI to enable the beta for your organization.

108 105 

109An X.509 provider derives workload identity attributes from a client certificate that OpenAI verifies against your organization's existing Mutual TLS configuration. It doesn't store certificates or maintain a separate trust store.106An X.509 provider derives workload identity attributes from a client certificate that OpenAI verifies against your organization's existing Mutual TLS configuration. It doesn't store certificates or maintain a separate trust store.

110 107 

111Before creating the provider, configure and activate the trusted CA certificate that anchors your client certificate in [Organization Settings > Security > Mutual TLS](https://platform.openai.com/settings/organization/security/mtls). The [OpenAI Mutual TLS Beta Program](https://help.openai.com/en/articles/10876024-openai-mutual-tls-beta-program) explains certificate requirements, activation scope, supported API endpoints, certificate-chain behavior, and client configuration restrictions.108Before creating the provider, configure and activate the trusted certificate

109that anchors your client certificate in [Organization Settings > Security >

110Mutual TLS](https://platform.openai.com/settings/organization/security/mtls).

111The [Mutual TLS guide](https://developers.openai.com/api/docs/guides/mutual-tls) explains permissions,

112certificate requirements, activation scope, mTLS hosts, certificate-chain

113behavior, CEL filters, and rotation.

112 114 

113Next, create the X.509 provider, derive one non-empty `openai.subject` value, and map that identity to a project service account with only the permissions the workload needs. The workload presents its certificate to the X.509 token endpoint to obtain a short-lived bearer token, then sends the bearer token and an accepted client certificate to the API mTLS endpoint.115Next, create the X.509 provider, derive one non-empty `openai.subject` value, and map that identity to a project service account with only the permissions the workload needs. The workload presents its certificate to the X.509 token endpoint to obtain a short-lived bearer token, then sends the bearer token and an accepted client certificate to the API mTLS endpoint.

114 116 

Details

1# Configure workload identity federation with X.509 certificates (beta)1# Configure workload identity federation with X.509 certificates

2 2 

3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.3> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

4 4 

5X.509 workload identity federation lets a workload exchange an identity from a TLS client certificate for a short-lived OpenAI access token. The workload then calls the OpenAI API with both the access token and an accepted client certificate. This flow replaces the API key, not the client certificate.5X.509 workload identity federation lets a workload exchange an identity from a TLS client certificate for a short-lived OpenAI access token. The workload then calls the OpenAI API with both the access token and an accepted client certificate. This flow replaces the API key, not the client certificate.

6 6 

7X.509 workload identity federation is available in beta for the OpenAI API;7X.509 workload identity federation is available for the OpenAI API. Codex does

8 Codex does not support it. If X.509 doesn't appear as a provider type, contact8 not support it. For Codex, use an OIDC token or SPIFFE JWT-SVID and follow the

9 your system administrator. For Codex, use an OIDC token or SPIFFE JWT-SVID and9 [Codex workload identity guide](https://developers.openai.com/codex/enterprise/workload-identity).

10 follow the [Codex workload identity

11 guide](https://developers.openai.com/codex/enterprise/workload-identity).

12 10 

13For token exchange request and response details, see the [workload identity token exchange reference](https://developers.openai.com/api/reference/workload-identity-federation#exchange-an-x509-certificate). For Mutual TLS certificate requirements and supported API endpoints, see the [OpenAI Mutual TLS Beta Program](https://help.openai.com/en/articles/10876024-openai-mutual-tls-beta-program).11For token exchange request and response details, see the [workload identity token exchange reference](https://developers.openai.com/api/reference/workload-identity-federation#exchange-an-x509-certificate). For Mutual TLS permissions, certificate requirements, activation, mTLS hosts, and rotation, see the [Mutual TLS guide](https://developers.openai.com/api/docs/guides/mutual-tls).

14 12 

15## How it works13## How it works

16 14 


28 26 

29You need:27You need:

30 28 

31- Access to the X.509 workload identity federation beta for your organization.

32- Permission to manage Mutual TLS certificates and Workload Identity Providers for your organization.29- Permission to manage Mutual TLS certificates and Workload Identity Providers for your organization.

33- A project and service account for the workload.30- A project and service account for the workload.

34- A client certificate, its private key, and any intermediate certificates required to build a path to your trusted root.31- A client certificate, its private key, and any intermediate certificates required to build a path to your trusted root.


40 37 

41X.509 Workload Identity Providers reuse your organization's existing Mutual TLS certificate configuration. They don't upload certificates or maintain a separate certificate trust store.38X.509 Workload Identity Providers reuse your organization's existing Mutual TLS certificate configuration. They don't upload certificates or maintain a separate certificate trust store.

42 39 

43Follow the [OpenAI Mutual TLS Beta Program](https://help.openai.com/en/articles/10876024-openai-mutual-tls-beta-program) to review CA certificate requirements, supported endpoints, certificate activation behavior, and client configuration. Then open [Organization settings > Security > Mutual TLS](https://platform.openai.com/settings/organization/security/mtls), upload the trusted CA certificate in PEM format, and activate it for the organization or for each project that will use X.509 workload identity federation.40Follow the [Mutual TLS guide](https://developers.openai.com/api/docs/guides/mutual-tls) to review certificate

41requirements, mTLS hosts, certificate activation behavior, CEL filters, and

42client configuration. Then open [Organization settings > Security > Mutual

43TLS](https://platform.openai.com/settings/organization/security/mtls), upload

44the trusted certificate in PEM format, and activate it for the organization or

45for each project that will use X.509 workload identity federation.

44 46 

45If your client certificate chains through an intermediate certificate, configure the stable trust anchor and present the leaf followed by the current intermediate certificates during the TLS handshake. OpenAI uses intermediates provided by the request and doesn't retrieve missing intermediates from certificate URLs. The Mutual TLS beta article documents the current chain-support and endpoint restrictions.47If your client certificate chains through an intermediate certificate, configure the stable trust anchor and present the leaf followed by the current intermediate certificates during the TLS handshake. OpenAI uses intermediates provided by the request and doesn't retrieve missing intermediates from certificate URLs.

46 48 

47## Configure an X.509 provider49## Configure an X.509 provider

48 50 

49After X.509 workload identity federation is enabled for your organization:51To configure an X.509 provider:

50 52 

511. Open [Organization settings > Security > Workload Identity Provider](https://platform.openai.com/settings/organization/security/workload-identity-provider), then select **Create identity provider**.531. Open [Organization settings > Security > Workload Identity Provider](https://platform.openai.com/settings/organization/security/workload-identity-provider), then select **Create identity provider**.

522. Choose **X.509** for **Provider type**, then enter a name and optional description. X.509 providers don't use OIDC issuer, audience, discovery, or JWKS settings. You can't change the provider type after you create it.542. Choose **X.509** for **Provider type**, then enter a name and optional description. X.509 providers don't use OIDC issuer, audience, discovery, or JWKS settings. You can't change the provider type after you create it.


81 83 

821. From the X.509 provider details page, select **Create mapping**.841. From the X.509 provider details page, select **Create mapping**.

832. Select the target project and service account, and grant only the API permissions the workload needs.852. Select the target project and service account, and grant only the API permissions the workload needs.

843. In the **Key** and **Value** fields, require an exact `openai.subject` value. X.509 mappings support either no assertions, represented as an empty object (`{}`), or assertions whose keys start with `openai.`. During the beta, don't leave the assertions empty.863. In the **Key** and **Value** fields, require an exact `openai.subject` value. X.509 mappings support either no assertions, represented as an empty object (`{}`), or assertions whose keys start with `openai.`.

854. Select **Create**.874. Select **Create**.

86 88 

87For example:89For example:


169X.509 token exchange returns generic OAuth errors and doesn't expose certificate, root, provider, or mapping details.171X.509 token exchange returns generic OAuth errors and doesn't expose certificate, root, provider, or mapping details.

170 172 

171| Result | Typical causes |173| Result | Typical causes |

172| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |174| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

173| HTTP `403` | The request used a method or path other than exact `POST /oauth/token` on `mtls.auth.openai.com`. |175| HTTP `403` | The request used a method or path other than exact `POST /oauth/token` on `mtls.auth.openai.com`. |

174| `invalid_subject_token` | The TLS client certificate is missing or invalid, the presented chain can't reach an active root, the certificate is outside its validity period, or a Mutual TLS certificate-admission rule rejects it. |176| `invalid_subject_token` | The TLS client certificate is missing or invalid, the presented chain can't reach an active root, the certificate is outside its validity period, or a Mutual TLS certificate-admission rule rejects it. |

175| `invalid_grant` | The X.509 flow isn't enabled, the provider or mapping is invalid or disabled, a provider **Attribute conditions** expression rejects the identity, no applicable roots are active, or no mapping matches. |177| `invalid_grant` | The provider or mapping is invalid or disabled, a provider **Attribute conditions** expression rejects the identity, no applicable roots are active, or no mapping matches. |

176| Server error | OpenAI returned a temporary server error. Retry according to your normal transient-error policy. |178| Server error | OpenAI returned a temporary server error. Retry according to your normal transient-error policy. |

177 179 

178An X.509 exchange never falls back to an OIDC or ordinary OAuth flow.180An X.509 exchange never falls back to an OIDC or ordinary OAuth flow.

quickstart.md +24 −12

Details

1330#pragma warning disable OPENAI0011330#pragma warning disable OPENAI001

1331 1331 

1332string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;1332string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;

1333string vectorStoreId = "<vector_store_id>";

1333ResponsesClient client = new(key);1334ResponsesClient client = new(key);

1334 1335 

1335CreateResponseOptions options = new() { Model = "gpt-5.6" };1336CreateResponseOptions options = new() { Model = "gpt-5.6" };

1336options.Tools.Add(1337options.Tools.Add(

1337 ResponseTool.CreateFileSearchTool(["<vector_store_id>"])1338 ResponseTool.CreateFileSearchTool([vectorStoreId])

1338);1339);

1339options.InputItems.Add(1340options.InputItems.Add(

1340 ResponseItem.CreateUserMessageItem("What is deep research by OpenAI?")1341 ResponseItem.CreateUserMessageItem("What is deep research by OpenAI?")


1658```1659```

1659 1660 

1660```csharp1661```csharp

1661using System.Text.Json;

1662using System.Text.Json.Serialization.Metadata;

1663using OpenAI.Responses;1662using OpenAI.Responses;

1664#pragma warning disable CA1869

1665#pragma warning disable OPENAI0011663#pragma warning disable OPENAI001

1666 1664 

1667string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;1665string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;


1694 ResponseItem.CreateUserMessageItem("What is the weather like in Paris today?")1692 ResponseItem.CreateUserMessageItem("What is the weather like in Paris today?")

1695);1693);

1696 1694 

1697ResponseResult response = client.CreateResponse(options);1695ResponseResult response = await client.CreateResponseAsync(options);

1698Console.WriteLine(1696foreach (ResponseItem outputItem in response.OutputItems)

1699 JsonSerializer.Serialize(1697{

1700 response.OutputItems[0],1698 if (outputItem is FunctionCallResponseItem functionCall)

1701 new JsonSerializerOptions

1702 {1699 {

1703 TypeInfoResolver = new DefaultJsonTypeInfoResolver(),1700 Console.WriteLine(

1701 $"{functionCall.FunctionName}({functionCall.FunctionArguments})"

1702 );

1704 }1703 }

1705 )1704 else if (outputItem is MessageResponseItem message)

1706);1705 {

1706 foreach (ResponseContentPart content in message.Content)

1707 {

1708 if (content.Kind == ResponseContentPartKind.OutputText)

1709 {

1710 Console.WriteLine(content.Text);

1711 }

1712 else if (content.Kind == ResponseContentPartKind.Refusal)

1713 {

1714 Console.WriteLine(content.Refusal);

1715 }

1716 }

1717 }

1718}

1707```1719```

1708 1720 

1709```ruby1721```ruby