SpyBara
Go Premium

Documentation 2026-10-04 22:58 UTC to 2026-10-05 22:59 UTC

7 files changed +72 −91. View all changes and history on the product overview
2026
Wed 7 13:00 Tue 6 22:58 Mon 5 22:59 Sun 4 22:58 Fri 2 23:58 Thu 1 22:59
Details

14For request parameters and response schemas, see the14For request parameters and response schemas, see the

15[Content provenance API reference](https://developers.openai.com/api/reference/resources/content_provenance_checks/methods/create).15[Content provenance API reference](https://developers.openai.com/api/reference/resources/content_provenance_checks/methods/create).

16 16 

17To check text, [apply for access](https://openai.com/form/content-provenance-api/).

18Text verification is currently available only to approved organizations including

19AI research and academic institutions. OpenAI reviews each application on a

20case-by-case basis.

21 

17A `not_detected` result means the tool didn't find supported signals in the22A `not_detected` result means the tool didn't find supported signals in the

18 uploaded file. Content may still have been generated by OpenAI if its metadata23 uploaded file. Content may still have been generated by OpenAI if its metadata

19 was stripped or shows evidence of tampering, its watermark was degraded, it24 was stripped or shows evidence of tampering, its watermark was degraded, it


255`generated_at` provide the generating model and generation time when available;260`generated_at` provide the generating model and generation time when available;

256either field can be `null`.261either field can be `null`.

257 262 

258## Supported formats and availability263## Supported uploads

259 264 

260The API supports the following file formats:265By default, the API supports the following file formats:

261 266 

262- **Images:** PNG, JPEG, and WebP.267- **Images:** PNG, JPEG, and WebP.

263- **Audio:** MP3, Opus, AAC, FLAC, WAV, and PCM.268- **Audio:** MP3, Opus, AAC, FLAC, WAV, and PCM.


270manually set the `multipart/form-data` request header. The `curl` `-F` option275manually set the `multipart/form-data` request header. The `curl` `-F` option

271sets the request content type and multipart boundary. Send one file per request.276sets the request content type and multipart boundary. Send one file per request.

272 277 

273Content provenance checks aren't eligible for278## Rate limits

274[Zero Data Retention](https://developers.openai.com/api/docs/guides/your-data#zero-data-retention).

275 279 

276Strict rate limits help protect the API against misuse. Organizations can280Strict rate limits help protect the API against misuse. Organizations can

277[apply for higher limits](https://openai.com/form/content-provenance-api/), and281[apply for higher limits](https://openai.com/form/content-provenance-api/), and

guides/custom-mcp-server.md +24 −16 renamed

Details

Previously: guides/developer-mode.md

1# ChatGPT Developer mode1# Add custom MCP server

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 


12 12 

13 13 

14 14 

15## What is ChatGPT developer mode15<a id="what-is-chatgpt-developer-mode"></a>

16 16 

17ChatGPT developer mode provides full Model Context Protocol (MCP) client support for all tools, both read and write. It's powerful but dangerous, and is intended for developers who understand how to safely configure and test apps. When using developer mode, watch for [prompt injections and other risks](https://developers.openai.com/api/docs/mcp), model mistakes on write actions that could destroy data, and malicious MCPs that attempt to steal information.17## Connect a custom MCP server

18 

19Connect your Model Context Protocol (MCP) server to ChatGPT as a plugin. ChatGPT supports both read and write tools from your server.

20 

21Only connect to MCP servers you trust. An untrusted server may access or steal information shared through app use, or trick ChatGPT into using tools in unintended ways, including changing or deleting data. Review [prompt injections and other risks](https://developers.openai.com/api/docs/mcp) before connecting a server.

18 22 

19## How to use23## How to use

20 24 

21- **Eligibility:** Available to Pro, Plus, Business, Enterprise, and Education accounts on the web.25- **Access:** Use ChatGPT on the web. Workspace permissions and security restrictions, including Lockdown, apply to adding and using custom MCP servers.

22- **Enable developer mode:** In [ChatGPT](https://chatgpt.com), open **Settings → Security and login** and turn on **Developer mode**.26- **Add an MCP server as a plugin:**

23- **Create apps from MCP servers:**27 1. Go to [ChatGPT Plugins](https://chatgpt.com/plugins).

24 - Go to [ChatGPT Plugins](https://chatgpt.com/plugins).28 2. Select the plus button, then **Add custom MCP server**.

25 - Select the plus button and create a developer-mode app for your remote MCP server. It will appear in the composer's **Developer mode** tool later during conversations. The plus button will only create developer-mode apps after you turn on Developer mode.29 3. Enter a name and, optionally, a description. Under **Connection**, enter your **Server URL**, or select **Tunnel** for a [Secure MCP Tunnel](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels).

30 4. Configure authentication for your server.

31 5. Review the risk warning and select **I understand and want to continue**.

32 6. Select **Create as a plugin**.

26 - Supported MCP protocols: SSE and streaming HTTP.33 - Supported MCP protocols: SSE and streaming HTTP.

27 - Authentication supported: OAuth, No Authentication, and Mixed Authentication34 - Authentication options include **OAuth**, **No authentication**, and **OAuth or no authentication**.

28 - For OAuth, if static credentials are provided, then they will be used. Otherwise, ChatGPT can use Client ID Metadata Documents when the authorization server advertises support and the app creator chooses CIMD. CIMD supports public-client token exchange (`none`) and signed client assertion token exchange (`private_key_jwt`). ChatGPT can also use DCR when configured.35 - For OAuth, if static credentials are provided, then they will be used. Otherwise, ChatGPT can use Client ID Metadata Documents when the authorization server advertises support and the app creator chooses CIMD. CIMD supports public-client token exchange (`none`) and signed client assertion token exchange (`private_key_jwt`). ChatGPT can also use DCR when configured.

29 - Mixed authentication supports OAuth and No Authentication. This means the initialize and list tools APIs use no auth, and tools use OAuth or no auth based on the security schemes set on their tool metadata.36 - Mixed authentication supports OAuth and no authentication. This means the initialize and list tools APIs use no auth, and tools use OAuth or no auth based on the security schemes set on their tool metadata.

30 - Created apps will show under "Drafts" in the app settings.37 - Find the resulting plugin in your personal plugins or the workspace where you created it. Install it before using it in a conversation.

38 

31- **Manage tools:** In app settings there is a details page per app. Use that to toggle tools on or off and refresh apps to pull new tools, descriptions, and server instructions from the MCP server.39- **Manage tools:** In app settings there is a details page per app. Use that to toggle tools on or off and refresh apps to pull new tools, descriptions, and server instructions from the MCP server.

32- **Use apps in conversations:** Choose **Developer mode** from the Plus menu and select the apps for the conversation. You may need to explore different prompting techniques to call the correct tools. For example:40- **Use apps in conversations:** In the prompt box, type `@` and select your installed plugin. Custom MCP plugins can be used alongside other apps, subject to workspace permissions and security restrictions. You may need to explore different prompting techniques to call the correct tools. For example:

33 - Be explicit: "Use the \"Acme CRM\" app's \"update_record\" tool to …". When needed, include the server label and tool name.41 - Be explicit: `Use the "Acme CRM" app's "update_record" tool to …`. When needed, include the server label and tool name.

34 - Disallow alternatives to avoid ambiguity: "Do not use built-in browsing or other tools; only use the Acme CRM app."42 - Disallow alternatives to avoid ambiguity: "Do not use built-in browsing or other tools; only use the Acme CRM app."

35 - Disambiguate similar tools: "Prefer `Calendar.create_event` for meetings; do not use `Reminders.create_task` for scheduling."43 - Disambiguate similar tools: "Prefer `Calendar.create_event` for meetings; do not use `Reminders.create_task` for scheduling."

36 - Specify input shape and sequencing: "First call `Repo.read_file` with `{ path: "…" }`. Then call `Repo.write_file` with the modified content. Do not call other tools."44 - Specify input shape and sequencing: "First call `Repo.read_file` with `{ path: "…" }`. Then call `Repo.write_file` with the modified content. Do not call other tools."

37 - If multiple apps overlap, state preferences up front (e.g., "Use `CompanyDB` for authoritative data; use other sources only if `CompanyDB` returns no results").45 - If multiple apps overlap, state preferences up front (for example, "Use `CompanyDB` for authoritative data; use other sources only if `CompanyDB` returns no results").

38 - Developer mode does not require `search`/`fetch` tools. Any tools your app exposes (including write actions) are available, subject to confirmation settings.46 - Custom MCP servers do not require `search`/`fetch` tools. Any tools your app exposes (including write actions) are available, subject to confirmation settings.

39 - See more guidance in [Using tools](https://developers.openai.com/api/docs/guides/tools) and [Prompting](https://developers.openai.com/api/docs/guides/prompting).47 - See more guidance in [Using tools](https://developers.openai.com/api/docs/guides/tools) and [Prompting](https://developers.openai.com/api/docs/guides/prompting).

40 - Improve tool selection with better tool descriptions: In your MCP server, write action-oriented tool names and descriptions that include "Use this when…" guidance, note disallowed/edge cases, and add parameter descriptions (and enums) to help the model choose the right tool among similar ones and avoid built-in tools when inappropriate.48 - Improve tool selection with better tool descriptions: In your MCP server, write action-oriented tool names and descriptions that include "Use this when…" guidance, note disallowed/edge cases, and add parameter descriptions (and enums) to help the model choose the right tool among similar ones and avoid built-in tools when inappropriate.

41 - Add server instructions for cross-tool guidance: Use the MCP [`instructions` field](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization) for server-wide guidance such as required tool sequences, shared rate limits, or relationships between tools. Keep the first 512 characters self-contained.49 - Add server instructions for cross-tool guidance: Use the MCP [`instructions` field](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization) for server-wide guidance such as required tool sequences, shared rate limits, or relationships between tools. Keep the first 512 characters self-contained.


55```63```

56 64 

57- **Reviewing and confirming tool calls:**65- **Reviewing and confirming tool calls:**

58 - Inspect JSON tool payloads verify correctness and debug problems. For each tool call, you can use the carat to expand and collapse the tool call details. Full JSON contents of the tool input and output are available.66 - Inspect JSON tool payloads to verify correctness and debug problems. For each tool call, expand the tool call details. Full JSON contents of the tool input and output are available.

59 - Write actions by default require confirmation. Carefully review the tool input which will be sent to a write action to ensure the behavior is as desired. Incorrect write actions can inadvertently destroy, alter, or share data!67 - Write actions by default require confirmation. Carefully review the tool input which will be sent to a write action to ensure the behavior is as desired. Incorrect write actions can inadvertently destroy, alter, or share data!

60 - Read-only detection: We respect the `readOnlyHint` tool annotation (see [MCP tool annotations](https://modelcontextprotocol.io/legacy/concepts/tools#available-tool-annotations)). Tools without this hint are treated as write actions.68 - Read-only detection: We respect the `readOnlyHint` tool annotation (see [MCP tool annotations](https://modelcontextprotocol.io/legacy/concepts/tools#available-tool-annotations)). Tools without this hint are treated as write actions.

61 - You can choose to remember the approve or deny choice for a given tool for a conversation, which means it will apply that choice for the rest of that conversation. Because of this, you should only allow a tool to remember the approve choice if you know and trust the underlying application to make further write actions without your approval. New conversations will prompt for confirmation again. Refreshing the same conversation will also prompt for confirmation again on subsequent turns.69 - You can choose to remember the approve or deny choice for a given tool for a conversation, which means it will apply that choice for the rest of that conversation. Because of this, you should only allow a tool to remember the approve choice if you know and trust the underlying application to make further write actions without your approval. New conversations will prompt for confirmation again. Refreshing the same conversation will also prompt for confirmation again on subsequent turns.

Details

577 577 

578Your sampling policy determines which requests create retained objects. A missing object or event alone doesn't mean storage has failed.578Your sampling policy determines which requests create retained objects. A missing object or event alone doesn't mean storage has failed.

579 579 

580#### Identify validation probes

581 

582For AWS, Azure, and GCP, validation writes test objects to your configured storage destination. Their names include `csg_validation_` followed by a random hexadecimal suffix. Each object's body contains the text `customer-storage-gateway-validation:csg_validation_<suffix>`, using the same suffix as its name. They contain no customer prompts or responses. OpenAI reads each test object using the configured access, then attempts an unauthenticated `GET` of the same object to check for public access. Private storage is expected to reject the unauthenticated request. These requests may appear in your provider's access logs, depending on your logging configuration.

583 

584For AWS, validation also checks the role's `sts:ExternalId` restriction. After successfully assuming your configured role with your project's external ID, OpenAI tests `AssumeRole` calls without an external ID and with the deliberately invalid test ID `proj_smoketest-verifier`. Both test calls are expected to return `AccessDenied`. They use the same OpenAI identity and configured role; the test ID doesn't identify another customer's project. AWS CloudTrail doesn't record these denied cross-account role-assumption attempts in your account. Keep your trust policy restricted to your real project ID; don't allow the test ID or remove the external-ID condition to make these probes succeed.

585 

580### Recover from a failure586### Recover from a failure

581 587 

582#### 1. Check the error588#### 1. Check the error

Details

126| `service_tier_changed` | The service tier used to process the request changed. | Keep the service tier consistent for requests expected to share a prefix. Check the returned `service_tier`, which can differ from the requested value. See [`service_tier`](https://developers.openai.com/api/reference/resources/responses/methods/create#%28resource%29%20responses%20%3E%20%28method%29%20create%20%3E%20%28params%29%200.non_streaming%20%3E%20%28param%29%20service_tier%20%3E%20%28schema%29) for supported values and behavior. |126| `service_tier_changed` | The service tier used to process the request changed. | Keep the service tier consistent for requests expected to share a prefix. Check the returned `service_tier`, which can differ from the requested value. See [`service_tier`](https://developers.openai.com/api/reference/resources/responses/methods/create#%28resource%29%20responses%20%3E%20%28method%29%20create%20%3E%20%28params%29%200.non_streaming%20%3E%20%28param%29%20service_tier%20%3E%20%28schema%29) for supported values and behavior. |

127| `tools_changed` | Tools were added, removed, or reordered, or their descriptions, schemas, or configuration changed. | Keep tool definitions and ordering stable. Use `tool_choice: "none"` to disable tools or `allowed_tools` to restrict which tools can run without changing the supplied tool list. See [Manage tools with append-only updates](https://developers.openai.com/api/docs/guides/prompt-caching#manage-tools-with-append-only-updates). |127| `tools_changed` | Tools were added, removed, or reordered, or their descriptions, schemas, or configuration changed. | Keep tool definitions and ordering stable. Use `tool_choice: "none"` to disable tools or `allowed_tools` to restrict which tools can run without changing the supplied tool list. See [Manage tools with append-only updates](https://developers.openai.com/api/docs/guides/prompt-caching#manage-tools-with-append-only-updates). |

128| `text_format_changed` | The output format or its schema changed. | Keep `text.format` and the schema consistent when the required output structure is unchanged. See [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). |128| `text_format_changed` | The output format or its schema changed. | Keep `text.format` and the schema consistent when the required output structure is unchanged. See [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs). |

129| `reasoning_effort_changed` | The reasoning effort changed. | Keep `reasoning.effort` consistent across requests intended to share a prefix. See [cache-affecting settings](https://developers.openai.com/api/docs/guides/prompt-caching#which-settings-affect-the-cached-prefix). |129| `reasoning_effort_changed` | The reasoning effort changed. | On supported GPT-6 and later models, append a `configuration_update` input item to [change reasoning effort during a conversation](https://developers.openai.com/api/docs/guides/reasoning?api-mode=responses#change-reasoning-mid-conversation) while preserving the earlier cached prefix.<br />On older models, keep `reasoning.effort` consistent across requests intended to share a prefix. |

130| `verbosity_changed` | The response verbosity changed. | Keep `text.verbosity` consistent across requests intended to share a prefix. See [cache-affecting settings](https://developers.openai.com/api/docs/guides/prompt-caching#which-settings-affect-the-cached-prefix). |130| `verbosity_changed` | The response verbosity changed. | Keep `text.verbosity` consistent across requests intended to share a prefix. See [cache-affecting settings](https://developers.openai.com/api/docs/guides/prompt-caching#which-settings-affect-the-cached-prefix). |

131| `context_compacted` | Compaction replaced earlier conversation content. | Preserve stable instructions and let later turns build on the compacted context. Compare total input cost: fewer input tokens can still save money despite lower cache reuse. See [Compaction](https://developers.openai.com/api/docs/guides/compaction). |131| `context_compacted` | Compaction replaced earlier conversation content. | Preserve stable instructions and let later turns build on the compacted context. Compare total input cost: fewer input tokens can still save money despite lower cache reuse. See [Compaction](https://developers.openai.com/api/docs/guides/compaction). |

132| `input_changed` | Earlier input changed, for example because instructions contain a timestamp or request ID, or previous messages were edited, reordered, or removed. | Move changing content after the reusable prefix and its cache breakpoint. Preserve earlier messages and tool results, and append new turns. See [Preserve conversation history](https://developers.openai.com/api/docs/guides/prompt-caching#preserve-conversation-history). |132| `input_changed` | Earlier input changed, for example because instructions contain a timestamp or request ID, or previous messages were edited, reordered, or removed. | Move changing content after the reusable prefix and its cache breakpoint. Preserve earlier messages and tool results, and append new turns. See [Preserve conversation history](https://developers.openai.com/api/docs/guides/prompt-caching#preserve-conversation-history). |

Details

8 8 

9Use this integration to:9Use this integration to:

10 10 

11- Route warning and deactivation notices to your trust and safety, security, or support team.11- Route warning and deactivation notices to your trust and safety or support team.

12- Add case details to an investigation or support ticket.12- Add case info to an investigation or support ticket.

13- Associate a notice with a user through your application's safety-identifier mapping.13- Associate a notice with a user through your application's safety-identifier mapping.

14 14 

15The steps in this guide use a warning to illustrate the workflow. You can use the same integration for deactivation notices.15The steps in this guide use a warning to illustrate the workflow. You can use the same integration for deactivation notices.

16 16 

17## How it works17## How it works

18 18 

19Safety webhooks notify your application that a notice was issued. The Safety Case Read API provides the details for that notice.19[Safety webhooks](https://developers.openai.com/api/reference/resources/webhooks#safety.warning_issued) notify your application that a notice was issued.

20 20 

21| Event | Notice |21| Event | Notice |

22| ---------------------------- | ------------------------------------------------------------ |22| ---------------------------- | ------------------------------------------------------------ |


25 25 

26Each event contains a case ID. Use it with `GET /v1/safety/cases/{id}` to retrieve the safety identifier, notice type, case creation timestamp, and available policy reason.26Each event contains a case ID. Use it with `GET /v1/safety/cases/{id}` to retrieve the safety identifier, notice type, case creation timestamp, and available policy reason.

27 27 

28These organization-level notifications are separate from project-level [misalignment alerts](https://developers.openai.com/api/docs/guides/safety-checks/misalignment-monitoring#receive-project-safety-alerts). Retrieving a case does not change or reverse the enforcement. The API returns case metadata, not the underlying conversation or a full investigation report.28These organization-level notifications are separate from project-level [misalignment alerts](https://developers.openai.com/api/docs/guides/safety-checks/misalignment-monitoring#receive-project-safety-alerts). Retrieving a case does not change or reverse the enforcement.

29 29 

30## Integrate with your application30## Integrate with your application

31 31 

32Start by configuring an organization-level webhook and a key for case lookups. Then connect the notifications to your review workflow.32Start by configuring an organization-level webhook and a key for case lookups. Then connect the notifications to your review workflow.

33 33 

34### 1. Configure your webhook and API key34### Configure your webhook and receive events

35 35 

36Use a stable [safety identifier](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers) for each user, and keep your application's mapping from that identifier to the user. Avoid including personal information in the identifier.36Use a stable [safety identifier](https://developers.openai.com/api/docs/guides/safety-best-practices#implement-safety-identifiers) for each user, and keep your application's mapping from that identifier to the user. Avoid including personal information in the identifier.

37 37 

38Open your [organization webhook settings](https://platform.openai.com/settings/organization/webhooks) and select **Create**. Enter your receiver's HTTPS URL and subscribe to `safety.warning_issued` and `safety.deactivation_issued`. Save the signing secret securely so your receiver can verify incoming events. These events use an organization endpoint, not a project endpoint.38Create an organization-level webhook endpoint in your [organization webhook settings](https://platform.openai.com/settings/organization/webhooks) and subscribe to `safety.warning_issued` and `safety.deactivation_issued`.

39 39 

40Your account needs `api.webhooks.read` and `organization.read` to view organization webhooks, plus `api.webhooks.write` and `organization.write` to create them. See [Permissions](https://developers.openai.com/api/docs/guides/rbac) for role configuration.40A warning notification has the following structure:

41 

42For case lookups, configure an API key for the same organization with **Restricted** permissions and set **Safety** to **Read** (`api.safety.read`). The API key authenticates case lookups; it is separate from the webhook signing secret.

43 

44### 2. Receive and verify the event

45 

46A warning notification has this structure. The IDs are illustrative:

47 41 

48```json42```json

49{43{


52 "created_at": 1787659200,46 "created_at": 1787659200,

53 "type": "safety.warning_issued",47 "type": "safety.warning_issued",

54 "data": {48 "data": {

55 "id": "C-example"49 "id": "C-abc123"

56 }50 }

57}51}

58```52```

59 53 

60Verify the signature before processing the event. Save the verified event for processing, return a successful `2xx` response promptly, and retrieve the case in a background worker. See the Webhooks guide for [signature verification](https://developers.openai.com/api/docs/guides/webhooks#verifying-webhook-signatures) and [acknowledgments, retries, and duplicate deliveries](https://developers.openai.com/api/docs/guides/webhooks#handling-webhook-requests-on-a-server).54For general webhook best practices, see the [Webhooks guide](https://developers.openai.com/api/docs/guides/webhooks).

61 55 

62The event's `id` identifies the webhook event. Its `data.id` identifies the safety case to retrieve.56The event's `id` identifies the webhook event. Its `data.id` identifies the safety case to retrieve.

63 57 

64### 3. Retrieve the case58### Retrieve the case

59 

60For case lookups, configure an API key for the same organization with **Restricted** permissions and set **Safety** to **Read** (`api.safety.read`).

65 61 

66Set `OPENAI_API_KEY` to your API key. Replace `C-example` with `data.id` from the verified event:62Look up case metadata through the Safety Case Read API:

67 63 

68```bash64```bash

69curl "https://api.openai.com/v1/safety/cases/C-example" \65curl "https://api.openai.com/v1/safety/cases/C-abc123" \

70 -H "Authorization: Bearer ${OPENAI_API_KEY}"66 -H "Authorization: Bearer ${OPENAI_API_KEY}"

71```67```

72 68 


74 70 

75```json71```json

76{72{

77 "id": "C-example",73 "id": "C-abc123",

78 "object": "safety.case",74 "object": "safety.case",

79 "created_at": 1787659100,75 "created_at": 1787659100,

80 "entity_identifier": "safety-id-example",76 "entity_identifier": "entity_identifier",

81 "reason": "cyber_abuse",77 "reason": "cyber_abuse",

82 "notice": {78 "notice": {

83 "type": "warning"79 "type": "warning"


85}81}

86```82```

87 83 

88Use `entity_identifier` to find the affected user in your application. The `reason` can be `null`; continue processing the notice when no reason is available.84The `entity_identifier` represents the affected safety identifier. The case creation timestamp can be used to help with investigation. See the [Safety Case API reference](https://developers.openai.com/api/reference/resources/safety/subresources/cases/methods/retrieve) for field definitions.

89 

90The case creation timestamp is not necessarily the event timestamp or the time of an individual request. See the [Safety Case API reference](https://developers.openai.com/api/reference/resources/safety/subresources/cases/methods/retrieve) for field definitions.

91 

92### 4. Create a review ticket

93 

94Add the case ID, notice type, safety identifier, case creation timestamp, and available policy reason to your internal ticket. Link the matching user record so your team can investigate using its own application records. If you cannot find a matching user, preserve the case details and flag the missing mapping for review.

95 85 

96Make ticket creation safe to retry. Follow the [webhook deduplication guidance](https://developers.openai.com/api/docs/guides/webhooks#handling-webhook-requests-on-a-server) so repeated deliveries do not create duplicate tickets. Retries of your own background processing should also reuse the existing ticket.86### Connect to your workflows

97 87 

98If an agent helps with triage, limit its access to the records it needs and keep customer-facing actions subject to your existing approval controls.88Use webhooks to receive notifications, then use case metadata to programmatically route those notices into your existing investigation or support systems.

99 

100## Confirm it's working

101 

102For a real event and an accessible case in your organization, check that:

103 

1041. Your receiver verifies the signature and returns a successful acknowledgment.

1052. The lookup returns HTTP `200`, with a case `id` matching the event's `data.id`.

1063. Your application identifies the expected user or flags a missing mapping.

1074. One review ticket contains the available case details. Reprocessing the event does not create another ticket.

108 

109Before receiving a real notice, test your processing logic with local example events and mocked case responses. Include both notice types, a `null` reason, a missing user mapping, and duplicate deliveries. Test signature verification separately, including rejection of invalid signatures; keep it enabled on your production receiver.

110 

111The example IDs on this page are not retrievable cases. Mocked tests validate your processing logic, not live delivery or API permissions. Do not trigger an actual enforcement to test your integration. An absence of enforcement notifications alone does not indicate a broken integration.

112 89 

113## Troubleshoot90## Troubleshoot

114 91 


119| Lookup returns `403` | Check that the key has the `api.safety.read` permission. |96| Lookup returns `403` | Check that the key has the `api.safety.read` permission. |

120| Lookup returns `404` | Use `data.id`, not the event ID. Check that the case belongs to the key's organization. |97| Lookup returns `404` | Use `data.id`, not the event ID. Check that the case belongs to the key's organization. |

121| Lookup returns `429` or a transient `5xx` | Retry with backoff and a bounded retry policy. Preserve the event for later processing or investigation. |98| Lookup returns `429` or a transient `5xx` | Retry with backoff and a bounded retry policy. Preserve the event for later processing or investigation. |

122| A ticket is created more than once | Check that ticket creation remains safe across repeated deliveries and worker retries. |

123 

124Do not silently discard a verified notification when its case lookup fails. Keep it available for retry or investigation.

Details

4 4 

5Secure MCP Tunnel lets you connect private MCP servers to supported OpenAI products without opening inbound firewall ports or exposing those servers to the public internet. Run `tunnel-client` inside the network that can already reach your MCP server; it opens an outbound HTTPS path to OpenAI, pulls queued MCP work, forwards requests locally, and returns responses through the same tunnel.5Secure MCP Tunnel lets you connect private MCP servers to supported OpenAI products without opening inbound firewall ports or exposing those servers to the public internet. Run `tunnel-client` inside the network that can already reach your MCP server; it opens an outbound HTTPS path to OpenAI, pulls queued MCP work, forwards requests locally, and returns responses through the same tunnel.

6 6 

7Secure MCP Tunnel supports private MCP connections, including developer-mode7Secure MCP Tunnel supports private MCP connections, including custom MCP

8 testing. It does not support public plugin submission or distribution. Public8 server testing. It does not support public plugin submission or distribution.

9 plugins require a stable, publicly reachable HTTPS MCP endpoint. If the MCP9 Public plugins require a stable, publicly reachable HTTPS MCP endpoint. If the

10 server must stay private, expose a public HTTPS proxy that forwards requests10 MCP server must stay private, expose a public HTTPS proxy that forwards

11 to it. See [public plugin submission](https://developers.openai.com/plugins/deploy/submission) for endpoint11 requests to it. See [public plugin submission](https://developers.openai.com/plugins/deploy/submission) for

12 and authentication requirements.12 endpoint and authentication requirements.

13 13 

14## What is an MCP tunnel?14## What is an MCP tunnel?

15 15 


57 57 

58## Permissions and access58## Permissions and access

59 59 

60[Platform tunnel permissions](https://developers.openai.com/api/docs/guides/rbac) and ChatGPT developer-mode access are separate:60[Platform tunnel permissions](https://developers.openai.com/api/docs/guides/rbac) and ChatGPT custom MCP server access are separate:

61 61 

62- Creating or editing a tunnel requires Tunnels **Read** + **Manage**.62- Creating or editing a tunnel requires Tunnels **Read** + **Manage**.

63- Running `tunnel-client` or selecting the tunnel while creating an app requires Tunnels **Read** + **Use**.63- Running `tunnel-client` or selecting the tunnel while creating an app requires Tunnels **Read** + **Use**.

64- Tunnel permissions apply to a Platform organization. A Platform organization owner or RBAC administrator grants the tunnel role.64- Tunnel permissions apply to a Platform organization. A Platform organization owner or RBAC administrator grants the tunnel role.

65- ChatGPT developer mode is a separate workspace permission. For Enterprise/Edu, a workspace admin grants developer-mode access; the user then enables it in **Settings → Security and login**. See the [developer-mode Help Center article](https://help.openai.com/en/articles/12584461-developer-mode-apps-and-full-mcp-connectors-in-chatgpt-beta) for plan-specific policy.65- Adding and using custom MCP servers in ChatGPT remains subject to workspace permissions and security restrictions. See [Add custom MCP server](https://developers.openai.com/api/docs/guides/custom-mcp-server).

66 66 

67Ask the target ChatGPT workspace admin for developer-mode access, and ask the target Platform organization owner/RBAC admin for tunnel permissions.67Ask the target ChatGPT workspace admin for permission to add and use custom MCP servers, and ask the target Platform organization owner/RBAC admin for tunnel permissions.

68 68 

69## Associate tunnels with the right organizations and workspaces69## Associate tunnels with the right organizations and workspaces

70 70 


135 135 

136## Connect from ChatGPT136## Connect from ChatGPT

137 137 

138Go to [ChatGPT Plugins](https://chatgpt.com/plugins), select the plus button to create a developer-mode app, and choose **Tunnel** under **Connection**. Select an available tunnel when ChatGPT lists it, or paste a valid `tunnel_id` if you already have one.138Go to [ChatGPT Plugins](https://chatgpt.com/plugins), select the plus button, then **Add custom MCP server**, and choose **Tunnel** under **Connection**. Select an available tunnel when ChatGPT lists it, or paste a valid `tunnel_id` if you already have one. Configure authentication, review the risk warning, and select **I understand and want to continue**, then **Create as a plugin**.

139 139 

140If the tunnel does not appear in ChatGPT, verify that the tunnel is associated with the target ChatGPT workspace, not only with a Platform organization, and that the app creator has Tunnels **Read** + **Use**.140If the tunnel does not appear in ChatGPT, verify that the tunnel is associated with the target ChatGPT workspace, not only with a Platform organization, and that the app creator has Tunnels **Read** + **Use**.

141 141 


218## Where to configure it218## Where to configure it

219 219 

220- Manage OpenAI-hosted MCP tunnel endpoints in [Platform tunnel settings](https://platform.openai.com/settings/organization/tunnels).220- Manage OpenAI-hosted MCP tunnel endpoints in [Platform tunnel settings](https://platform.openai.com/settings/organization/tunnels).

221- Use a tunnel when creating a developer-mode app at [ChatGPT Plugins](https://chatgpt.com/plugins).221- Use a tunnel when [adding a custom MCP server](https://developers.openai.com/api/docs/guides/custom-mcp-server) at [ChatGPT Plugins](https://chatgpt.com/plugins).

222- For Codex or API flows, use the tunnel-backed MCP target exposed by the supported product surface.222- For Codex or API flows, use the tunnel-backed MCP target exposed by the supported product surface.

223 223 

224## Next steps224## Next steps


240 settings.240 settings.

241 </figcaption>241 </figcaption>

242 </figure>242 </figure>

243 <figure>

244 [<img src="https://developers.openai.com/images/platform/guides/secure-mcp-tunnels/chatgpt-connectors-tunnel.png"

245 alt="Sanitized ChatGPT app creation screenshot with Tunnel selected."

246 loading="lazy"

247 class="w-full rounded-md border border-gray-200 dark:border-gray-800"

248 />](https://chatgpt.com/plugins)

249 <figcaption class="mt-3 text-sm text-gray-600 dark:text-gray-400">

250 Select Tunnel when connecting a ChatGPT developer-mode app to a private

251 MCP server.

252 </figcaption>

253 </figure>

mcp.md +3 −3

Details

479 479 

480### Connect in ChatGPT480### Connect in ChatGPT

481 481 

4821. In [ChatGPT](https://chatgpt.com), open **Settings → Security and login** and turn on **Developer mode**.4821. Go to [ChatGPT Plugins](https://chatgpt.com/plugins), select the plus button, then **Add custom MCP server**.

4831. Go to [ChatGPT Plugins](https://chatgpt.com/plugins), select the plus button, and connect your server URL in developer mode.4831. Enter your server URL and authentication details. Review the risk warning and select **I understand and want to continue**, then **Create as a plugin**.

4841. Test your plugin by running prompts in chat and deep research.4841. Install your plugin and test it by running prompts in chat and deep research.

485 485 

486For detailed setup steps, see [Connect and test your plugin](https://developers.openai.com/plugins/deploy/connect-chatgpt).486For detailed setup steps, see [Connect and test your plugin](https://developers.openai.com/plugins/deploy/connect-chatgpt).

487 487