SpyBara
Go Premium

Documentation 2026-07-20 23:01 UTC to 2026-07-21 22:02 UTC

10 files changed +669 −79. View all changes and history on the product overview
2026
Fri 24 15:00 Thu 23 21:57 Wed 22 20:02 Tue 21 22:02 Mon 20 23:01 Fri 17 22:57 Thu 16 20:57 Wed 15 19:58 Tue 14 17:03 Wed 8 02:01 Mon 6 22:58
Details

119 ]}119 ]}

120/>120/>

121 121 

122## Add code review rules

123 

124For [Codex code review in GitHub](https://learn.chatgpt.com/docs/third-party/github#customize-what-codex-reviews),

125add a `## Code Review Rules` section to the `AGENTS.md` closest to the code the

126rules govern. Put repository-wide checks at the root and service-specific

127checks in a nested file.

128 

129```md

130## Code Review Rules

131 

132### Experiment cohorts

133 

134- Do not filter treatment comparisons on post-exposure behavior, including conversion or retention.

135 Safe path: build cohorts from assignment or exposure; report conversion as an outcome.

136```

137 

138Keep rules concise, explain the behavior to flag and any safe path or

139exception, and reserve formatting and lint checks for CI. See [Customize what

140Codex reviews](https://learn.chatgpt.com/docs/third-party/github#customize-what-codex-reviews) for

141setup and rule-writing guidance.

142 

122## Customize fallback filenames143## Customize fallback filenames

123 144 

124If your repository already uses a different filename (for example `TEAM_GUIDE.md`), add it to the fallback list so Codex treats it like an instructions file.145If your repository already uses a different filename (for example `TEAM_GUIDE.md`), add it to the fallback list so Codex treats it like an instructions file.

developers.md +7 −1

Details

169 {169 {

170 title: "Reference",170 title: "Reference",

171 description:171 description:

172 "Find commands, slash commands, and settings for developer surfaces.",172 "Find commands, settings, and plugin submission errors for developer surfaces.",

173 pages: [173 pages: [

174 {174 {

175 title: "CLI customization",175 title: "CLI customization",


192 href: "/codex/developer-settings?surface=app",192 href: "/codex/developer-settings?surface=app",

193 icon: "settings",193 icon: "settings",

194 },194 },

195 {

196 title: "Plugin submission errors",

197 description: "Find and fix plugin package and submission errors.",

198 href: "/codex/plugin-submission-errors",

199 icon: "plugin",

200 },

195 ],201 ],

196 },202 },

197 ]}203 ]}

Details

1# Deploy the Windows app1# Deploy the Windows app

2 2 

3Choose a deployment path based on who controls installation and updates and3Users can install the ChatGPT desktop app themselves, or your IT team can

4whether your network allows Microsoft's app-distribution services. The app is4deploy it with an enterprise management tool. The app is Store-signed, but

5Store-signed, but users don't need to open the Microsoft Store. Standard5users don't need to open the Microsoft Store to install or update it.

6installation and updates use Microsoft's app installation and Windows Update

7services.

8 

9| Scenario | Deployment path |

10| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |

11| Users install and update their own apps | Use the [web installer](https://get.microsoft.com/installer/download/9PLM9XGG6VKS?cid=website_cta_psi). |

12| IT deploys the app and Microsoft update services are available | Deploy the Microsoft Store app through Intune or another management tool. |

13| IT controls update timing or blocks Microsoft update services | Redeploy each new version through your software-management process. |

14| Your network blocks Microsoft app-distribution services | Deploy the Store-signed MSIX package for each device architecture. |

15 6 

16## Let users install and update the app7## Let users install and update the app

17 8 


27winget install --id 9PLM9XGG6VKS -s msstore18winget install --id 9PLM9XGG6VKS -s msstore

28```19```

29 20 

30## Deploy with an enterprise management tool21## Deploy the app with an enterprise management tool

31 22 

32If your organization centrally manages software, deploy the Microsoft Store app23If your organization centrally manages software, use Microsoft Intune or

33through Microsoft Intune or another compatible mobile device management (MDM)24another compatible mobile device management (MDM) or software-deployment

34or software-deployment platform. Search for ChatGPT from OpenAI in the25platform. If your platform supports Microsoft Store app deployment, search for

35Microsoft Store app flow, or use this Store product ID:26ChatGPT from OpenAI in the Store app flow, or use this Store product ID:

36 27 

37```text28```text

389PLM9XGG6VKS299PLM9XGG6VKS

39```30```

40 31 

41This is the recommended enterprise deployment method when your environment32For setup details, see the following Microsoft documentation:

42allows Microsoft Store app deployment and update endpoints. Users don't need to

43open the Microsoft Store themselves.

44 33 

45For Intune setup details, see34- [Enterprise deployment guide](https://1drv.ms/b/c/123ec1ed6c72a14a/IQDVdo5pE5P3QKg5r0eieSvfAeE7cW0yy58ncBFW7OYajwU?e=dGH94F)

46[Add Microsoft Store apps to Microsoft Intune](https://learn.microsoft.com/en-us/intune/app-management/deployment/add-microsoft-store).35- [Intune deployment guide](https://1drv.ms/b/c/123ec1ed6c72a14a/IQDh_5o31T6XT7bUn5RPldEJAZX58gEuRr8YnJD7d2IMpec?e=nByKw6)

36- [MECM deployment guide](https://1drv.ms/b/c/123ec1ed6c72a14a/IQB829f_TSbkR7-H9qA4Q9ntAa9D2He3qMjXksWi2ozdeg8?e=GTKgAl)

37- [Add Microsoft Store apps to Microsoft Intune](https://learn.microsoft.com/en-us/intune/app-management/deployment/add-microsoft-store)

47 38 

48## Control update timing39## Install without Microsoft distribution services

49 40 

50If your organization blocks automatic app updates, Windows Update, or Microsoft41If your environment can't use Microsoft app-distribution services for the

51Store update endpoints, your IT team owns the update lifecycle. Codex can run in42initial installation, download the Store-signed MSIX package for each device

52this environment, but you must redeploy newer packages through your change43architecture:

53management process.

54 

55Redeploy the package with each Codex release when possible. If that cadence

56isn't practical, check for a newer package at least weekly and redeploy when a

57newer version is available. Users should restart the app after you deploy an

58update.

59 

60Deferring updates is a security and reliability tradeoff because the app

61includes browser and runtime components that receive regular updates.

62 

63## Deploy without Microsoft distribution services

64 

65If your environment can't use the standard Microsoft distribution services,

66download the Store-signed MSIX package for each device architecture:

67 44 

68| Device architecture | Package |45| Device architecture | Package |

69| ------------------- | ---------------------------------------------------------------------------------------- |46| ------------------- | ---------------------------------------------------------------------------------------- |

70| x64 | [ChatGPT-x64.msix](https://persistent.oaistatic.com/codex-app-prod/ChatGPT-x64.msix) |47| x64 | [ChatGPT-x64.msix](https://persistent.oaistatic.com/codex-app-prod/ChatGPT-x64.msix) |

71| Arm64 | [ChatGPT-arm64.msix](https://persistent.oaistatic.com/codex-app-prod/ChatGPT-arm64.msix) |48| Arm64 | [ChatGPT-arm64.msix](https://persistent.oaistatic.com/codex-app-prod/ChatGPT-arm64.msix) |

72 49 

73These stable links point to the latest published Store package. For offline50These stable links point to the latest published Store-signed package for each

74deployment workflows that require a license file, also download the51architecture. For offline deployment workflows that require a license file,

52also download the

75[offline license (`ChatGPT-License.xml`)](https://persistent.oaistatic.com/codex-app-prod/ChatGPT-License.xml).53[offline license (`ChatGPT-License.xml`)](https://persistent.oaistatic.com/codex-app-prod/ChatGPT-License.xml).

76Ingest the appropriate MSIX and, when required, the license file into your MDM54Ingest the appropriate MSIX and, when required, the license file into your MDM

77or software-deployment platform.55or software-deployment platform.

78 56 

57After the initial installation, devices that can reach

58`persistent.oaistatic.com` can install updates automatically, so you don't

59need to redeploy newer packages through your management tool.

60 

79This deployment path:61This deployment path:

80 62 

81- Supports deployment in restricted environments.63- Supports initial installation in restricted environments.

82- Supports x64 and Arm64 devices.64- Supports x64 and Arm64 devices.

83- Doesn't provide a standalone MSI or non-Store EXE.65- Doesn't provide a standalone MSI or non-Store EXE.

84- Doesn't let users update the app themselves.

85- Requires your organization to deploy newer packages when updating the app.

86 66 

87## Related resources67## Related resources

88 68 

Details

119 ]}119 ]}

120/>120/>

121 121 

122## Add code review rules

123 

124For [Codex code review in GitHub](https://learn.chatgpt.com/docs/third-party/github#customize-what-codex-reviews),

125add a `## Code Review Rules` section to the `AGENTS.md` closest to the code the

126rules govern. Put repository-wide checks at the root and service-specific

127checks in a nested file.

128 

129```md

130## Code Review Rules

131 

132### Experiment cohorts

133 

134- Do not filter treatment comparisons on post-exposure behavior, including conversion or retention.

135 Safe path: build cohorts from assignment or exposure; report conversion as an outcome.

136```

137 

138Keep rules concise, explain the behavior to flag and any safe path or

139exception, and reserve formatting and lint checks for CI. See [Customize what

140Codex reviews](https://learn.chatgpt.com/docs/third-party/github#customize-what-codex-reviews) for

141setup and rule-writing guidance.

142 

122## Customize fallback filenames143## Customize fallback filenames

123 144 

124If your repository already uses a different filename (for example `TEAM_GUIDE.md`), add it to the fallback list so Codex treats it like an instructions file.145If your repository already uses a different filename (for example `TEAM_GUIDE.md`), add it to the fallback list so Codex treats it like an instructions file.

Details

67 67 

68## Customize what Codex reviews68## Customize what Codex reviews

69 69 

70Codex searches your repository for `AGENTS.md` files and follows any **Review guidelines** you include.70Codex searches your repository for `AGENTS.md` files and follows the applicable

71code review rules. Add a `## Code Review Rules` section to the file closest to

72the code the rules govern. Use `###` headings to group related checks when

73helpful.

71 74 

72To set guidelines for a repository, add or update a top-level `AGENTS.md` with a section like this:75For example, an experiment-reporting service can keep post-exposure behavior

76from changing a comparison cohort:

73 77 

74```md78```md

75## Review guidelines79## Code Review Rules

76 80 

77- Don't log PII.81### Experiment cohorts

78- Verify that authentication middleware wraps every route.82 

83- Do not filter treatment comparisons on post-exposure behavior, including conversion or retention.

84 Safe path: build cohorts from assignment or exposure; report conversion as an outcome.

79```85```

80 86 

81Codex applies guidance from the closest `AGENTS.md` to each changed file. You can place more specific instructions deeper in the tree when particular packages need extra scrutiny.87Put repository-wide rules in the root `AGENTS.md` and service-specific rules

88in a nested file, such as `services/experiment_reporting/AGENTS.md`. Codex

89applies the root and more-specific guidance that covers each changed file, so

90unrelated changes don't have to carry service-specific context.

91 

92Start with two or three concise rules that encode checks reviewers often explain. Useful rules:

93 

94- **Focus on consequential, repository-specific behavior.** Describe the

95 compatibility constraint, data boundary, or unsafe side effect to flag and

96 why it matters.

97- **State the safe path or exception.** Give Codex enough context to distinguish

98 a real issue from expected behavior.

99- **Keep rules scoped and durable.** Prefer outcomes over function names that

100 can change, and place guidance near the code it governs.

101- **Leave mechanical checks in CI.** Keep formatting, lint, and other

102 deterministic checks out of review rules.

103 

104Open a representative pull request and request a review with `@codex review`.

105Refine the rules based on the findings and feedback you see, and narrow or

106remove guidance that produces noise.

107 

108Code review rules guide Codex; they don't replace tests, branch protections, or

109required approvals.

82 110 

83For a one-off focus, add it to your pull request comment:111For a one-off focus, add it to your pull request comment:

84 112 

85`@codex review for security regressions`113`@codex review for security regressions`

86 114 

87If you want Codex to flag typos in documentation, add guidance in `AGENTS.md`

88(for example, “Treat typos in docs as P1.”).

89 

90## Act on review findings115## Act on review findings

91 116 

92After Codex posts a review, you can ask it to fix issues in the same pull117After Codex posts a review, you can ask it to fix issues in the same pull

plugin-submission-errors.md +455 −0 created

Details

1# Plugin submission errors

2 

3Plugins submitted to the public directory are held to a higher standard than

4plugins installed in a workspace. Directory submissions must pass the shared

5package checks and the additional checks for listing fields, review materials,

6MCP tools, skills, assets, and images. This reference also covers shared

7package checks, such as app references, that can appear outside the submission

8portal.

9 

10Use the error code returned during submission to find the matching requirement.

11Errors block submission. Warnings don't block submission, but you should review

12them before continuing.

13 

14Non-empty values can't contain only whitespace. Supported text excludes control

15characters, Unicode line or paragraph separators, and unsupported invisible

16formatting characters. HTTPS URLs must include a host and contain no embedded

17credentials or unsupported characters.

18 

19## Final directory submission

20 

21A package can pass upload validation and still fail final directory submission.

22Final submission uses stricter listing limits and checks MCP configuration,

23skill scans, test cases, and policy attestations.

24 

25| Field | Final submission rule |

26| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

27| Package name | Required; at most 64 characters. Start with an ASCII letter or digit and use only ASCII letters, digits, `_`, and `-`. |

28| Version | Required; use a semantic version of at most 64 characters. |

29| Display name | Required; one line; at most 30 characters. |

30| Short description | Required; one line; at most 30 characters. |

31| Long description | Required; at most 4,000 characters. Line breaks are allowed. |

32| Developer name | Required; one line; at most 80 characters. |

33| Category | Required; choose a supported category listed in the [Listing and interface errors](#listing-and-interface-errors) section. |

34| Capabilities | At most 20. Each capability must be non-empty, one line, and at most 120 characters. |

35| Starter prompts | At most 3. Each prompt must be non-empty, unique after Unicode and whitespace normalization, one line, at most 128 characters, and contain no app `@mention`. |

36| URLs | Required for MCP-backed submissions; optional for skills-only submissions. Website, support, privacy policy, and terms URLs must use HTTPS and be at most 1,024 characters. |

37| Brand colors | Optional six-digit hex colors. The light color must have at least 2:1 contrast against white, and the dark color must have at least 2:1 contrast against `#212121`. |

38 

39Every plugin submission also requires:

40 

41- Passing safety and security scans for every bundled skill. Scans can take up

42 to 2 hours.

43- A verified developer or business identity and all required policy

44 attestations.

45 

46For an MCP-backed plugin, final submission also requires:

47 

48- Website, support, privacy policy, and terms URLs that meet the rules above.

49- A demo-recording URL that shows the main use cases and tools across supported

50 platforms.

51- Exactly five positive test cases, three negative test cases, and release

52 notes.

53- A production HTTPS MCP server URL, a completed domain-verification challenge,

54 and a successful, current tool scan.

55- Explicit `readOnlyHint`, `openWorldHint`, and `destructiveHint` values and a

56 justification for each value on every MCP tool.

57- Reviewer-ready demo credentials when the server uses OAuth.

58- Screenshots only when the MCP server provides custom UI. If you add

59 screenshots, provide one PNG or JPEG image for every starter prompt. Each

60 screenshot must be exactly 706 pixels wide and 400–860 pixels tall.

61 

62### Final metadata errors

63 

64In these error names, `subtitle` means short description and `description`

65means long description.

66 

67| Name | Requirement |

68| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |

69| `submission_display_name_required` | Display name is required, non-empty, and single-line. |

70| `submission_display_name_too_long` | Display name must be 30 characters or fewer. |

71| `submission_display_name_character_unsupported` | Display name must use supported text and fit on one line. |

72| `submission_subtitle_required` | Short description is required, non-empty, and single-line. |

73| `submission_subtitle_too_long` | Short description must be 30 characters or fewer. |

74| `submission_subtitle_character_unsupported` | Short description must use supported text and fit on one line. |

75| `submission_description_required` | Long description is required and must be non-empty. Line breaks are allowed. |

76| `submission_description_too_long` | Long description must be 4,000 characters or fewer. |

77| `submission_description_character_unsupported` | Long description must use supported text. Line breaks are allowed. |

78| `submission_developer_name_required` | Developer name is required, non-empty, and single-line. |

79| `submission_developer_name_too_long` | Developer name must be 80 characters or fewer. |

80| `submission_developer_name_character_unsupported` | Developer name must use supported text and fit on one line. |

81| `plugin_capability_invalid` | Each capability must be non-empty, use supported text, fit on one line, and be 120 characters or fewer. |

82| `plugin_default_prompt_mention` | Starter prompts must not contain app `@mentions`. |

83| `plugin_default_prompt_duplicate` | Starter prompts must be unique after Unicode and whitespace normalization. |

84 

85### MCP and review errors

86 

87These errors apply to MCP-backed submissions.

88 

89| Name | Requirement |

90| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

91| `annotations_required` | Every MCP tool must set `readOnlyHint`, `openWorldHint`, and `destructiveHint` accurately. |

92| `justification_required` | Every MCP tool annotation must include a justification for its read-only, open-world, or destructive behavior. |

93| `scan_required` | MCP tools must have a successful, current scan of the production MCP server. |

94| `domain_verification_required` | The exact verification token must be hosted at the generated `/.well-known/openai-apps-challenge` URL on the MCP host or an allowed parent host, and **Verify Domain** must pass. |

95| `frame_domain_explanation_required` | Every external frame domain reported by the MCP tool scan must have an explanation of why the UI needs it and what content it provides. |

96| `screenshots_not_allowed` | Screenshots are allowed only when the current MCP tool scan reports a UI output template. |

97 

98## Archive errors

99 

100### Skills-only ZIP upload errors and warnings

101 

102**Skills only** uploads accept a plugin manifest and bundled skills. A changed

103package name blocks an update; the other findings require confirmation.

104 

105| Name | Requirement |

106| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |

107| `plugin_name_mismatch` | The package name in an update must match the existing plugin name. |

108| `plugin_version_unchanged` | A new release must use a different manifest `version`; reusing the published version requires confirmation. |

109| `mcp_configuration_excluded` | Skills-only ZIP uploads must not include `mcpServers` or `.mcp.json`; MCP-backed plugins must use **With MCP**. |

110| `app_configuration_excluded` | Skills-only ZIP uploads must not include `apps` or `.app.json`; plugins with app content must use **With MCP**. |

111| `screenshot_configuration_excluded` | Skills-only ZIP uploads must not include `interface.screenshots`; screenshots require **With MCP** and custom UI. |

112| `claude_format_normalized` | `.claude-plugin/plugin.json` is converted to `.codex-plugin/plugin.json`, with missing interface defaults and normalized text fields added by the portal. |

113| `manifest_normalized` | The portal saves the normalized manifest as `.codex-plugin/plugin.json`; changed fields require confirmation. |

114| `developer_name_defaulted` | `author.name` and `interface.developerName` must match, or the selected verified identity is used for both after confirmation. |

115 

116### ZIP structure and limit errors

117 

118| Name | Requirement |

119| --------------------------------------------- | ------------------------------------------------------------------------------------------------ |

120| `archive_empty` | Archive must not be empty. |

121| `archive_too_large` | Compressed ZIP must be 100 MB or less. |

122| `archive_format_not_zip` | Archive must be a valid, uncorrupted ZIP file. |

123| `archive_member_path_empty` | Archive entry path must not be empty. |

124| `archive_member_path_has_outer_whitespace` | Archive entry path must not begin or end with whitespace. |

125| `archive_member_path_has_backslash` | Archive entry path must use `/`, not backslashes. |

126| `archive_member_path_absolute` | Archive entry path must be relative to the archive root. |

127| `archive_member_path_has_empty_segment` | Archive entry path must not contain empty segments. |

128| `archive_member_path_has_parent_segment` | Archive entry path must not contain `..` segments. |

129| `archive_member_path_too_deep` | Archive entry path must contain at most 20 segments, including the filename. |

130| `archive_member_path_too_long` | Archive entry path must be within the supported path-length limit. |

131| `archive_member_path_normalization_collision` | Archive entry paths must remain unique after case and Unicode normalization. |

132| `archive_member_type_unsupported` | Archive entries must be regular files or directories. |

133| `archive_member_too_large` | Archive entry must not exceed 100 MiB. |

134| `archive_member_path_duplicate` | Archive entry path must be unique. |

135| `archive_member_path_type_conflict` | A file path cannot also be a directory or contain another archive entry. |

136| `archive_too_many_entries` | Archive must not contain more than 5,000 entries. |

137| `archive_uncompressed_too_large` | Extracted archive must not exceed 512 MiB. |

138| `archive_member_unreadable` | Every archive entry must be readable, must not be encrypted, and must use supported compression. |

139 

140## Plugin root errors

141 

142| Name | Requirement |

143| ------------------------------ | --------------------------------------------------------------------------------------------------------------------- |

144| `plugin_root_missing` | The selected path must exist and be a directory containing a plugin. |

145| `archive_plugin_files_missing` | A skills-only ZIP must contain a supported plugin manifest and at least one valid skill at `skills/<skill>/SKILL.md`. |

146| `plugin_root_ambiguous` | ZIP must contain exactly one plugin root, either at the archive root or in one top-level directory. |

147| `plugin_root_has_siblings` | A ZIP with a top-level plugin directory must not contain sibling files. |

148 

149## Plugin manifest errors

150 

151| Name | Requirement |

152| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |

153| `plugin_manifest_missing` | ZIP must contain `.codex-plugin/plugin.json`, `.agent-plugin/plugin.json`, or `.claude-plugin/plugin.json` at the root or in its single top-level directory. |

154| `plugin_manifest_not_file` | Plugin manifest must be a regular JSON file. |

155| `plugin_manifest_unreadable` | Plugin manifest must be readable UTF-8 text. |

156| `plugin_manifest_json_malformed` | Plugin manifest must contain valid JSON; malformed syntax is reported with a line number. |

157| `plugin_manifest_root_not_object` | Plugin manifest must contain a JSON object at the top level. |

158| `codex_manifest_parent_not_directory` | `.codex-plugin` must be a directory. |

159| `codex_manifest_path_not_file` | `.codex-plugin/plugin.json` must be a regular JSON file. |

160| `plugin_id_wrong_type` | `id` must be a string when provided. |

161| `plugin_id_empty` | `id` must be non-empty when provided. |

162| `plugin_name_missing` | `name` is required. |

163| `plugin_name_wrong_type` | `name` must be a string. |

164| `plugin_name_empty` | `name` must be non-empty. |

165| `plugin_name_too_long` | `name` must be 64 characters or fewer. |

166| `plugin_name_format` | `name` must start with an ASCII letter or digit and contain only ASCII letters, digits, `_`, or `-`. |

167| `plugin_version_missing` | `version` is required. |

168| `plugin_version_wrong_type` | `version` must be a string. |

169| `plugin_version_empty` | `version` must be a non-empty semantic-version string, such as `1.0.0`. |

170| `plugin_version_not_semver` | `version` must use semantic versioning, such as `1.0.0`. |

171| `plugin_version_too_long` | `version` must be 64 characters or fewer. |

172| `plugin_description_missing` | `description` is required. |

173| `plugin_description_wrong_type` | `description` must be a string. |

174| `plugin_description_empty` | `description` must be non-empty. |

175| `plugin_description_too_long` | `description` must be 1,024 characters or fewer. |

176| `plugin_description_character_unsupported` | `description` must use supported text. Line breaks are allowed. |

177| `plugin_developer_missing` | `author.name` is required. `interface.developerName` is also required and is reported separately. |

178| `plugin_author_wrong_type` | `author` must be an object. |

179| `plugin_author_name_wrong_type` | `author.name` must be a string. |

180| `plugin_author_name_empty` | `author.name` must be non-empty. |

181| `plugin_author_name_too_long` | `author.name` must be 120 characters or fewer. |

182| `plugin_author_name_character_unsupported` | `author.name` must use supported text. |

183| `plugin_author_email_wrong_type` | `author.email` must be a string when provided. |

184| `plugin_author_email_empty` | `author.email` must be non-empty when provided. |

185| `plugin_author_email_too_long` | `author.email` must be 320 characters or fewer. |

186| `plugin_author_email_character_unsupported` | `author.email` must use supported text. |

187| `plugin_author_url_wrong_type` | `author.url` must be a string when provided. |

188| `plugin_author_url_empty` | `author.url` must be non-empty when provided. |

189| `plugin_author_url_not_https` | `author.url` must be an HTTPS URL. |

190| `plugin_author_url_has_credentials` | `author.url` must not contain credentials. |

191| `plugin_author_url_too_long` | `author.url` must be 2,048 characters or fewer. |

192| `plugin_author_url_character_unsupported` | `author.url` must use supported text. |

193 

194## Listing and interface errors

195 

196The plugin manifest's `interface` object defines the public listing shown to

197users. It lives in `.codex-plugin/plugin.json` and uses fields such as

198`displayName` and `shortDescription`:

199 

200```json

201{

202 "interface": {

203 "displayName": "Example Plugin",

204 "shortDescription": "Summarize documents",

205 "longDescription": "Summarize and organize documents.",

206 "developerName": "Example",

207 "category": "Productivity",

208 "capabilities": ["Summarize documents"]

209 }

210}

211```

212 

213The four listing URLs (website, privacy policy, terms, and support) are

214optional for skills-only plugins and required for MCP-backed plugins. Their

215length limit is 2,048 characters for package validation and 1,024 characters

216for final directory submission.

217 

218| Name | Requirement |

219| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

220| `plugin_interface_wrong_type` | The plugin manifest's `interface` field must be a JSON object. |

221| `plugin_display_name_wrong_type` | `interface.displayName` must be a string. |

222| `plugin_display_name_empty` | `interface.displayName` is required and must be non-empty. |

223| `plugin_display_name_too_long` | `interface.displayName` must be 80 characters or fewer for package validation and 30 characters or fewer for final directory submission. |

224| `plugin_display_name_character_unsupported` | `interface.displayName` must use supported text. |

225| `plugin_short_description_missing` | `interface.shortDescription` is required, must fit on one line, and must be 240 characters or fewer for package validation and 30 characters or fewer for final directory submission. |

226| `plugin_short_description_wrong_type` | `interface.shortDescription` must be a string. |

227| `plugin_short_description_empty` | `interface.shortDescription` must be non-empty. |

228| `plugin_short_description_too_long` | `interface.shortDescription` must be 240 characters or fewer for package validation and 30 characters or fewer for final directory submission. |

229| `plugin_short_description_character_unsupported` | `interface.shortDescription` must use supported text. |

230| `plugin_long_description_wrong_type` | `interface.longDescription` must be a string. |

231| `plugin_long_description_empty` | `interface.longDescription` is required and must be non-empty. |

232| `plugin_long_description_too_long` | `interface.longDescription` must be 4,000 characters or fewer. |

233| `plugin_long_description_character_unsupported` | `interface.longDescription` must use supported text. Line breaks are allowed. |

234| `plugin_developer_name_wrong_type` | `interface.developerName` must be a string. |

235| `plugin_developer_name_empty` | `interface.developerName` is required and must be non-empty. |

236| `plugin_developer_name_too_long` | `interface.developerName` must be 120 characters or fewer for package validation and 80 characters or fewer for final directory submission. |

237| `plugin_developer_name_character_unsupported` | `interface.developerName` must use supported text. |

238| `plugin_category_wrong_type` | `interface.category` must be a string. |

239| `plugin_category_empty` | `interface.category` must be non-empty when provided; omit it to use `Other`. |

240| `plugin_category_unknown` | `interface.category` must be `Productivity`, `Creativity`, `Developer Tools`, `Business & Operations`, `Data & Analytics`, `Communication`, `Education & Research`, `Security`, `Finance`, `Healthcare`, `Travel`, `Entertainment`, or `Other`. |

241| `plugin_category_character_unsupported` | `interface.category` must use supported text. |

242| `plugin_capabilities_wrong_type` | `interface.capabilities` must be a list of strings. |

243| `plugin_capabilities_too_many` | `interface.capabilities` must contain 20 entries or fewer. |

244| `plugin_capability_wrong_type` | Each `interface.capabilities` entry must be a string. |

245| `plugin_capability_empty` | Each `interface.capabilities` entry must be non-empty when provided. |

246| `plugin_capability_too_long` | Each `interface.capabilities` entry must be 120 characters or fewer. |

247| `plugin_capability_character_unsupported` | Each `interface.capabilities` entry must use supported text. |

248| `plugin_website_url_wrong_type` | `interface.websiteURL` must be a string when provided. |

249| `plugin_website_url_empty` | `interface.websiteURL` must be non-empty when provided. |

250| `plugin_website_url_format` | `interface.websiteURL` must be an HTTPS URL. |

251| `plugin_website_url_too_long` | `interface.websiteURL` must meet the listing URL length limits. |

252| `plugin_privacy_policy_url_wrong_type` | `interface.privacyPolicyURL` must be a string when provided. |

253| `plugin_privacy_policy_url_empty` | `interface.privacyPolicyURL` must be non-empty when provided. |

254| `plugin_privacy_policy_url_format` | `interface.privacyPolicyURL` must be an HTTPS URL. |

255| `plugin_privacy_policy_url_too_long` | `interface.privacyPolicyURL` must meet the listing URL length limits. |

256| `plugin_terms_of_service_url_wrong_type` | `interface.termsOfServiceURL` must be a string when provided. |

257| `plugin_terms_of_service_url_empty` | `interface.termsOfServiceURL` must be non-empty when provided. |

258| `plugin_terms_of_service_url_format` | `interface.termsOfServiceURL` must be an HTTPS URL. |

259| `plugin_terms_of_service_url_too_long` | `interface.termsOfServiceURL` must meet the listing URL length limits. |

260| `plugin_support_url_wrong_type` | `interface.supportURL` must be a string when provided. |

261| `plugin_support_url_empty` | `interface.supportURL` must be non-empty when provided. |

262| `plugin_support_url_format` | `interface.supportURL` must be an HTTPS URL. |

263| `plugin_support_url_too_long` | `interface.supportURL` must meet the listing URL length limits. |

264| `plugin_homepage_wrong_type` | `homepage` must be a string when provided. |

265| `plugin_homepage_empty` | `homepage` must be non-empty when provided. |

266| `plugin_homepage_format` | `homepage` must be an HTTPS URL. |

267| `plugin_homepage_too_long` | `homepage` must be 2,048 characters or fewer. |

268| `plugin_brand_color_wrong_type` | `interface.brandColor` must be a string when provided. |

269| `plugin_brand_color_empty` | `interface.brandColor` must be non-empty when provided. |

270| `plugin_brand_color_format` | `interface.brandColor` must be a six-digit hex color, such as `#1ABCFE`. |

271| `plugin_brand_color_dark_wrong_type` | `interface.brandColorDark` must be a string when provided. |

272| `plugin_brand_color_dark_empty` | `interface.brandColorDark` must be non-empty when provided. |

273| `plugin_brand_color_dark_format` | `interface.brandColorDark` must be a six-digit hex color, such as `#1ABCFE`. |

274| `plugin_brand_color_contrast` | `interface.brandColor` must have at least 2:1 contrast against white. |

275| `plugin_brand_color_dark_contrast` | `interface.brandColorDark` must have at least 2:1 contrast against `#212121`. |

276| `plugin_default_prompt_wrong_type` | `interface.defaultPrompt` must be a string or list of strings. |

277| `plugin_default_prompt_too_many` | `interface.defaultPrompt` must contain at most three prompts. |

278| `plugin_default_prompt_entry_wrong_type` | Each `interface.defaultPrompt` entry must be a string. |

279| `plugin_default_prompt_empty` | Each `interface.defaultPrompt` entry must be non-empty when provided. |

280| `plugin_default_prompt_too_long` | Each `interface.defaultPrompt` entry must be 512 characters or fewer for package validation and 128 characters or fewer for final directory submission. |

281| `plugin_default_prompt_character_unsupported` | Each `interface.defaultPrompt` entry must use supported text and fit on one line. |

282 

283## Plugin content errors

284 

285| Name | Requirement |

286| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |

287| `plugin_skills_path_wrong_type` | `skills` must be a string path for the root `skills/` directory. |

288| `plugin_skills_path_empty` | `skills` must be a non-empty path to the root `skills/` directory when provided. |

289| `plugin_skills_path_unsupported` | `skills` must resolve to the root `skills/` directory. |

290| `plugin_skills_directory_missing` | A declared root `skills/` directory must exist. |

291| `plugin_skills_path_not_directory` | Root `skills/` must be a directory when declared. |

292| `plugin_apps_path_wrong_type` | `apps` must be a string path for the root `.app.json`. |

293| `plugin_apps_path_empty` | `apps` must be a non-empty path to the root `.app.json` when provided. |

294| `plugin_apps_path_unsupported` | `apps` must resolve to the root `.app.json`. |

295| `plugin_apps_file_missing` | A declared root `.app.json` file must exist. |

296| `plugin_apps_path_not_file` | Root `.app.json` must be a regular file when declared. |

297| `plugin_runtime_surface_missing` | A skills-only ZIP must contain at least one valid skill at `skills/<skill>/SKILL.md`; app and MCP references don't satisfy this requirement. |

298 

299## Skill errors

300 

301| Name | Requirement |

302| ----------------------------------------- | --------------------------------------------------------------------------------------------- |

303| `skill_manifest_missing` | Skill must contain a `SKILL.md` file. |

304| `skill_bundle_too_large` | Each compressed skill bundle must be within the MiB limit reported in the error. |

305| `skill_directory_hidden` | Skill directory names must not begin with `.`. |

306| `skill_manifest_nested` | Each skill directory must be an immediate child of `skills/`. |

307| `skill_manifest_not_regular_file` | `SKILL.md` must be a regular file. |

308| `skill_manifest_unreadable` | `SKILL.md` must be readable. |

309| `skill_manifest_invalid_utf8` | `SKILL.md` must contain valid UTF-8. |

310| `skill_frontmatter_missing` | `SKILL.md` must start with YAML front matter between `---` lines. |

311| `skill_frontmatter_unclosed` | `SKILL.md` YAML front matter must end with `---`. |

312| `skill_frontmatter_yaml_malformed` | `SKILL.md` front matter must contain valid YAML. |

313| `skill_frontmatter_wrong_type` | `SKILL.md` front matter must contain a YAML mapping. |

314| `skill_name_missing` | `name` is required and must not be empty. |

315| `skill_name_wrong_type` | `name` must be a string. |

316| `skill_name_empty` | `name` must be non-empty. |

317| `skill_name_character_unsupported` | Skill front matter `name` must use supported text. |

318| `skill_description_missing` | `description` is required and must not be empty. |

319| `skill_description_wrong_type` | `description` must be a string. |

320| `skill_description_empty` | `description` must be non-empty. |

321| `skill_description_too_long` | `description` must be 1,024 characters or fewer. |

322| `skill_description_character_unsupported` | Skill front matter `description` must use supported text. |

323| `skill_body_empty` | Skill instructions must not be empty. |

324| `skill_identity_too_long` | The combined plugin and skill name (`plugin-name:skill-name`) must be 64 characters or fewer. |

325| `skill_identity_duplicate` | Each skill `name` must be unique within the plugin. |

326 

327## Skill agent metadata errors

328 

329A bundled skill can define its own `interface` in

330`skills/<skill>/agents/openai.yaml`. This controls how the skill appears to

331users and is separate from the plugin manifest's `interface`. Skill interface

332fields use snake_case:

333 

334```yaml

335interface:

336 display_name: "Summarize documents"

337 short_description: "Summarize a document"

338 icon_small: "./assets/icon.png"

339 default_prompt: "Summarize the selected document."

340```

341 

342| Name | Requirement |

343| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

344| `skill_agent_not_regular_file` | `agents/openai.yaml` must be a regular file. |

345| `skill_agent_unreadable` | `agents/openai.yaml` must be readable. |

346| `skill_agent_invalid_utf8` | `agents/openai.yaml` must contain valid UTF-8. |

347| `skill_agent_yaml_malformed` | `agents/openai.yaml` must contain valid YAML. |

348| `skill_agent_top_level_wrong_type` | `agents/openai.yaml` must contain a YAML mapping at the top level. |

349| `skill_agent_interface_missing` | `interface` is required in `agents/openai.yaml` when that file is included. |

350| `skill_agent_interface_wrong_type` | `interface` in `agents/openai.yaml` must be a YAML mapping. |

351| `skill_agent_display_name_missing` | `interface.display_name` is required and must not be empty. |

352| `skill_agent_display_name_wrong_type` | `interface.display_name` must be a string. |

353| `skill_agent_display_name_empty` | `interface.display_name` must not be empty. |

354| `skill_agent_short_description_missing` | `interface.short_description` is required and must not be empty. |

355| `skill_agent_short_description_wrong_type` | `interface.short_description` must be a string. |

356| `skill_agent_short_description_empty` | `interface.short_description` must not be empty. |

357| `skill_agent_icon_small_wrong_type` | `interface.icon_small` must be a non-empty relative file path when provided. |

358| `skill_agent_icon_small_empty` | `interface.icon_small` must be a non-empty relative file path when provided, such as `assets/icon.png`. |

359| `skill_agent_icon_large_wrong_type` | `interface.icon_large` must be a non-empty relative file path when provided. |

360| `skill_agent_icon_large_empty` | `interface.icon_large` must be a non-empty relative file path when provided, such as `assets/icon.png`. |

361| `skill_agent_brand_color_wrong_type` | `interface.brand_color` must be a string when provided. |

362| `skill_agent_brand_color_empty` | `interface.brand_color` must be a non-empty six-digit hex color when provided, such as `#1ABCFE`. |

363| `skill_agent_brand_color_format` | `interface.brand_color` must be a six-digit hex color, such as `#1ABCFE`. |

364| `skill_agent_default_prompt_wrong_type` | `interface.default_prompt` must be a string when provided. |

365| `skill_agent_default_prompt_empty` | `interface.default_prompt` must be non-empty when provided. |

366| `skill_agent_policy_wrong_type` | `policy` must be a YAML mapping when provided. |

367| `skill_agent_allow_implicit_invocation_wrong_type` | `policy` may contain only `products` and `allow_implicit_invocation`. `products` must contain `CHAT`, `CODEX`, or both, and `allow_implicit_invocation` must be `true` or `false`. |

368| `skill_agent_dependencies_wrong_type` | `dependencies` must be a YAML mapping; only `tools` is supported. |

369| `skill_agent_dependency_unsupported` | Only `dependencies.tools` is supported in `agents/openai.yaml`. |

370 

371## Asset path errors

372 

373| Name | Requirement |

374| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |

375| `declared_asset_path_wrong_type` | The named asset field must be a file path string. |

376| `declared_asset_path_empty` | The named asset field must not be empty. |

377| `declared_asset_path_has_outer_whitespace` | The named asset field must not begin or end with whitespace. |

378| `declared_asset_path_has_control_character` | The named asset field must not contain characters U+0000–U+001F or U+007F. |

379| `branding_asset_path_missing_root_prefix` | The named asset field must start with `./`. |

380| `declared_asset_path_unsafe` | The named asset field must be a relative path inside the plugin and must not contain an absolute path, drive prefix, or `..` traversal segment. |

381| `declared_asset_path_outside_package` | The named asset field must reference a file inside the plugin. |

382| `declared_asset_file_missing` | The named asset field references a file that does not exist. |

383| `declared_asset_not_regular_file` | The named asset field must reference a file, not a directory or special file. |

384 

385## Image errors

386 

387Directory branding images must use a supported file type and meet the size and

388dimension limits below. These rules apply to packaged branding assets;

389starter-prompt screenshots use the separate portal limits listed above.

390 

391| Name | Requirement |

392| ----------------------------------------- | -------------------------------------------------------------------------- |

393| `plugin_logo_path_missing` | `interface.logo` is required and must reference a square image. |

394| `plugin_composer_icon_path_missing` | `interface.composerIcon` is required and must reference a square image. |

395| `image_file_unreadable` | Image file must be readable. |

396| `image_file_too_large` | Image must not exceed 5 MiB. |

397| `image_file_format_unsupported` | Image filename must end in `.png`, `.jpg`, `.jpeg`, `.webp`, or `.svg`. |

398| `raster_image_decode_failed` | Raster image must be a PNG, JPEG, or WebP file that can be decoded safely. |

399| `raster_image_extension_content_mismatch` | Image filename extension must match the detected image format. |

400| `raster_image_not_square` | Image must be square. |

401| `raster_image_dimensions_too_small` | Image dimensions must be at least 48×48 pixels. |

402| `raster_image_dimensions_too_large` | Image dimensions must not exceed 4,096×4,096 pixels. |

403| `svg_xml_malformed` | SVG must contain valid UTF-8 XML. |

404| `svg_root_element_invalid` | SVG root element must be `<svg>`. |

405| `svg_dimensions_missing` | SVG must define a numeric `viewBox` or numeric `width` and `height`. |

406| `svg_dimensions_not_numeric` | SVG dimensions must be numeric and omit units and percentages. |

407| `svg_dimensions_not_positive` | SVG width and height must be positive finite numbers. |

408| `svg_dimensions_not_square` | SVG dimensions must be square. |

409| `svg_dimensions_too_small` | SVG dimensions must be at least 48×48 pixels. |

410 

411## App reference errors

412 

413The shared package checks validate `.app.json` when a plugin references apps.

414The submission portal doesn't publish references to existing ChatGPT apps: a

415**Skills only** upload removes `.app.json`, and an MCP-backed submission must

416use **With MCP** and submit the MCP server directly.

417 

418For local or workspace packages, the top-level `apps` object maps each app

419alias to an app entry.

420 

421| Name | Requirement |

422| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

423| `app_manifest_unreadable` | `.app.json` must be readable UTF-8 text. |

424| `app_manifest_json_malformed` | `.app.json` contains malformed JSON near the reported line. |

425| `app_manifest_wrong_type` | `.app.json` must contain a JSON object at the top level. |

426| `app_entries_missing` | `apps` is required. |

427| `app_entries_wrong_type` | `apps` must be an object. |

428| `app_entry_wrong_type` | Each app entry must be an object. |

429| `app_id_missing` | Each app entry's `id` is required. |

430| `app_id_wrong_type` | Each app entry's `id` must be a string. |

431| `app_id_format` | Each app entry's `id` must begin with `asdk_app_`, `connector_`, or `templated_apps_`, followed by a letter or digit and then only letters, digits, `_`, or `-`. |

432| `app_entry_optional_wrong_type` | Each app entry's `optional` value must be `true` or `false` when provided. |

433| `app_entry_required_wrong_type` | Each app entry's `required` value must be `true` or `false` when provided. |

434| `app_not_eligible` | For a local or workspace package, each referenced app must be a released public Codex app, available connector, or released app template. Directory submissions must use **With MCP** and submit the MCP server directly. |

435 

436## Package warnings

437 

438These warnings identify package content that validation ignores or normalizes.

439They don't block submission. Review them to confirm the submitted plugin

440contains the expected files and settings.

441 

442| Name | Requirement |

443| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |

444| `duplicate_app_reference` | Each app ID in `.app.json` must be referenced once; duplicate references are treated as one app. |

445| `undeclared_app_manifest_ignored` | A root `.app.json` is imported only when the plugin-manifest `apps` field is set to `./.app.json`. |

446| `undeclared_mcp_manifest_ignored` | A root `.mcp.json` is imported only when the plugin-manifest `mcpServers` field is set to `./.mcp.json`. |

447| `skill_file_ignored` | Files directly under `skills/` aren't imported as skills; each skill must be in a directory containing `SKILL.md`. |

448| `skill_symlink_ignored` | Symbolic links directly under `skills/` aren't imported as skills; each skill must be a real directory containing `SKILL.md`. |

449| `skill_frontmatter_adjusted` | Skill `name` and `description` are normalized during import by trimming outer whitespace and collapsing internal whitespace. |

450| `skill_metadata_ignored` | Skill interface settings must use the `interface` mapping in `agents/openai.yaml`; `metadata` in `SKILL.md` doesn't configure the interface. |

451 

452## Next steps

453 

454After resolving all validation errors, return to

455[Submit plugins](https://learn.chatgpt.com/docs/submit-plugins) to complete the submission.

Details

75the plugin submission form does not recognize it, check that you are submitting75the plugin submission form does not recognize it, check that you are submitting

76from the same organization and project where the identity was verified. The76from the same organization and project where the identity was verified. The

77submitter also needs **Apps Management** write access for that organization.77submitter also needs **Apps Management** write access for that organization.

78Ask an organization owner or admin to update the submitter's role, then reload78Ask an organization owner or admin to update the role assigned to the

79the plugin submission portal.79submitter, then reload the plugin submission portal.

80 80 

81### Prepare required materials81### Prepare required materials

82 82 


1413. Define a content security policy that allows the exact domains your app1413. Define a content security policy that allows the exact domains your app

142 fetches from.142 fetches from.

1434. Complete domain verification if the portal shows a **Domain not verified**1434. Complete domain verification if the portal shows a **Domain not verified**

144 challenge. Use an HTTPS origin on the MCP hostname or a parent hostname, and144 challenge. Use an HTTPS origin on the MCP host or a parent host, and

145 host the exact token at `/.well-known/openai-apps-challenge`.145 host the exact token at `/.well-known/openai-apps-challenge`.

1465. Select **Scan Tools**.1465. Select **Scan Tools**.

1476. Review the discovered tools, domains, validation output, and tool metadata.1476. Review the discovered tools, domains, validation output, and tool metadata.


168return JSON, a list of tokens, or multiple tokens from the same URL.168return JSON, a list of tokens, or multiple tokens from the same URL.

169 169 

170The **Challenge Base URL** is an optional HTTPS origin that tells the portal170The **Challenge Base URL** is an optional HTTPS origin that tells the portal

171where to check the token. It must be the MCP hostname or a parent hostname.171where to check the token. It must be the MCP host or a parent host.

172Paths are ignored. For example, if the MCP server URL is172Paths are ignored. For example, if the MCP server URL is

173`https://api.example.com/mcp`, the default challenge URL is173`https://api.example.com/mcp`, the default challenge URL is

174`https://api.example.com/.well-known/openai-apps-challenge`, and174`https://api.example.com/.well-known/openai-apps-challenge`, and

175`https://example.com` can be used as a parent-origin challenge base if you can175`https://example.com` can be used as a parent-origin challenge base if you can

176host the token there.176host the token there.

177 177 

178If two plugins that contain apps share the same MCP hostname but differ only by178If two plugins that contain apps share the same MCP host but differ only by

179path, they also share the same default challenge URL. You cannot verify them179path, they also share the same default challenge URL. You cannot verify them

180separately by putting different tenant paths in the Challenge Base URL, because180separately by putting different tenant paths in the Challenge Base URL, because

181the path is ignored. Use a parent origin that can host the new token, give the181the path is ignored. Use a parent origin that can host the new token, give the

182app's MCP server a distinct hostname, or work with OpenAI support if neither182app's MCP server a distinct host, or work with OpenAI support if neither

183hosting option is possible.183hosting option is possible.

184 184 

185If another plugin that contains an app already uses the same MCP hostname, do185If another plugin that contains an app already uses the same MCP host, do

186not replace its existing challenge token unless that plugin no longer needs it.186not replace its existing challenge token unless that plugin no longer needs it.

187Use an allowed parent-origin Challenge Base URL or a distinct MCP hostname for187Use an allowed parent-origin Challenge Base URL or a distinct MCP host for

188the new submission.188the new submission.

189 189 

190Every tool should have clear names, descriptions, schemas, and output190Every tool should have clear names, descriptions, schemas, and output


211Upload the final skill bundle for skills-only or app-plus-skills submissions.211Upload the final skill bundle for skills-only or app-plus-skills submissions.

212Use the same file tree and instructions you tested locally.212Use the same file tree and instructions you tested locally.

213 213 

214For a **Skills only** submission, upload a ZIP no larger than 100 MB with one

215plugin root. Put the plugin manifest, such as `.codex-plugin/plugin.json`, and

216`skills/<skill>/SKILL.md` in that root. ZIP uploads only support skills. The

217portal excludes `.mcp.json`, `mcpServers`, `.app.json`, `apps`, and

218`interface.screenshots` and reports a warning when they are present.

219 

214![Skills tab with a skill bundle ready to upload](https://developers.openai.com/images/codex/plugins/submit/skills-upload.webp)220![Skills tab with a skill bundle ready to upload](https://developers.openai.com/images/codex/plugins/submit/skills-upload.webp)

215 221 

216Each skill should include:222Each skill should include:


276 282 

277Review the full draft before submitting.283Review the full draft before submitting.

278 284 

285Directory plugins are held to a higher standard and require additional

286validation. See [Plugin submission errors](https://learn.chatgpt.com/docs/plugin-submission-errors) for

287the full list of checks and how to fix each error.

288 

279In the release notes, summarize:289In the release notes, summarize:

280 290 

281- What the plugin does.291- What the plugin does.

Details

67 67 

68## Customize what Codex reviews68## Customize what Codex reviews

69 69 

70Codex searches your repository for `AGENTS.md` files and follows any **Review guidelines** you include.70Codex searches your repository for `AGENTS.md` files and follows the applicable

71code review rules. Add a `## Code Review Rules` section to the file closest to

72the code the rules govern. Use `###` headings to group related checks when

73helpful.

71 74 

72To set guidelines for a repository, add or update a top-level `AGENTS.md` with a section like this:75For example, an experiment-reporting service can keep post-exposure behavior

76from changing a comparison cohort:

73 77 

74```md78```md

75## Review guidelines79## Code Review Rules

76 80 

77- Don't log PII.81### Experiment cohorts

78- Verify that authentication middleware wraps every route.82 

83- Do not filter treatment comparisons on post-exposure behavior, including conversion or retention.

84 Safe path: build cohorts from assignment or exposure; report conversion as an outcome.

79```85```

80 86 

81Codex applies guidance from the closest `AGENTS.md` to each changed file. You can place more specific instructions deeper in the tree when particular packages need extra scrutiny.87Put repository-wide rules in the root `AGENTS.md` and service-specific rules

88in a nested file, such as `services/experiment_reporting/AGENTS.md`. Codex

89applies the root and more-specific guidance that covers each changed file, so

90unrelated changes don't have to carry service-specific context.

91 

92Start with two or three concise rules that encode checks reviewers often explain. Useful rules:

93 

94- **Focus on consequential, repository-specific behavior.** Describe the

95 compatibility constraint, data boundary, or unsafe side effect to flag and

96 why it matters.

97- **State the safe path or exception.** Give Codex enough context to distinguish

98 a real issue from expected behavior.

99- **Keep rules scoped and durable.** Prefer outcomes over function names that

100 can change, and place guidance near the code it governs.

101- **Leave mechanical checks in CI.** Keep formatting, lint, and other

102 deterministic checks out of review rules.

103 

104Open a representative pull request and request a review with `@codex review`.

105Refine the rules based on the findings and feedback you see, and narrow or

106remove guidance that produces noise.

107 

108Code review rules guide Codex; they don't replace tests, branch protections, or

109required approvals.

82 110 

83For a one-off focus, add it to your pull request comment:111For a one-off focus, add it to your pull request comment:

84 112 

85`@codex review for security regressions`113`@codex review for security regressions`

86 114 

87If you want Codex to flag typos in documentation, add guidance in `AGENTS.md`

88(for example, “Treat typos in docs as P1.”).

89 

90## Act on review findings115## Act on review findings

91 116 

92After Codex posts a review, you can ask it to fix issues in the same pull117After Codex posts a review, you can ask it to fix issues in the same pull

Details

36 36 

37## Define review guidance37## Define review guidance

38 38 

39To customize what Codex reviews, add or update a top-level `AGENTS.md` with a section like this:39To customize what Codex reviews, add a `## Code Review Rules` section to the

40`AGENTS.md` closest to the code the rules govern. For example:

40 41 

41```md42```md

42## Review guidelines43## Code Review Rules

43 44 

44- Flag typos and grammar issues as P0 issues.45### Experiment cohorts

45- Flag potential missing documentation as P1 issues.46 

46- Flag missing tests as P1 issues.47- Do not filter treatment comparisons on post-exposure behavior, including conversion or retention.

47 ...48 Safe path: build cohorts from assignment or exposure; report conversion as an outcome.

48```49```

49 50 

50Codex applies guidance from the closest `AGENTS.md` to each changed file. You can place more specific instructions deeper in the tree when particular packages need extra scrutiny.51Put repository-wide rules in the root `AGENTS.md` and service-specific rules

52in a nested file. Keep rules concise, describe the behavior to flag and any

53safe path or exception, and leave formatting and lint checks in CI. See

54[Customize what Codex reviews](https://developers.openai.com/codex/third-party/github#customize-what-codex-reviews)

55for setup and rule-writing guidance.

whats-new.md +42 −0

Details

4work, with examples and links to learn more. For every versioned update, bug fix,4work, with examples and links to learn more. For every versioned update, bug fix,

5and minor improvement, see the [Codex changelog](https://learn.chatgpt.com/docs/changelog).5and minor improvement, see the [Codex changelog](https://learn.chatgpt.com/docs/changelog).

6 6 

7## July 13–17, 2026

8 

9### Keep Work conversations and Projects together on desktop

10 

11The ChatGPT desktop app now keeps Chat and Work conversations together in the

12ChatGPT view. Cloud Work conversations sync across web, mobile, and desktop;

13local Work conversations stay on your computer. ChatGPT Projects are available

14in the desktop app. Codex keeps its dedicated view and separate history for

15developer workflows.

16 

17[Compare Work mode and Codex on

18desktop](https://learn.chatgpt.com/docs/use-chatgpt#compare-work-mode-and-codex-on-desktop) to choose the

19view that fits your task.

20 

21<PromptComponent

22 prompt={`Open the Launch project, review its files and recent conversations, and continue the launch plan from the latest Work conversation.`}

23/>

24 

25### Control parallel Codex work with Codex Micro

26 

27On July 15, OpenAI and Work Louder launched

28[Codex Micro](https://learn.chatgpt.com/docs/features/codex-micro), a limited-run physical control

29surface for Codex in the ChatGPT desktop app. Its Agent Keys show the status of

30up to six chats and switch between them. Customizable Command Keys, an analog

31stick, and a dial can trigger common actions or skills, start push-to-talk, and

32adjust reasoning effort without leaving the keyboard.

33 

34### Use GPT-5.6 through Amazon Bedrock

35 

36GPT-5.6 Sol, Terra, and Luna reached general availability through Amazon

37Bedrock. Local ChatGPT Work and Codex surfaces can use the built-in

38[`amazon-bedrock` provider](https://learn.chatgpt.com/docs/amazon-bedrock) with a Bedrock API key or the

39AWS SDK credential chain. This includes Work and Codex in the ChatGPT desktop

40app, Codex CLI, the IDE extension, and the Codex SDK.

41 

42### Inspect Codex task visualizations on iOS

43 

44ChatGPT for iOS 1.2026.188 added inline visualizations to Codex tasks and

45improved creating and managing tasks from conversations, including reliable

46links to newly created tasks. Read the

47[July 13 iOS release notes](https://learn.chatgpt.com/docs/changelog#codex-2026-07-13-mobile).

48 

7## July 6–10, 202649## July 6–10, 2026

8 50 

9<a id="take-on-ambitious-work-with-chatgpt-work"></a>51<a id="take-on-ambitious-work-with-chatgpt-work"></a>