Structural Difference Between `.codex-plugin/plugin.json` and `.app.json` in OpenAI Plugins

The .codex-plugin/plugin.json file contains the full declarative UI metadata and interface specification that Codex consumes, while .app.json contains only the backend runtime app ID mapping, creating a strict separation between presentation layer and runtime configuration.

In the openai/plugins repository, each plugin directory contains two distinct JSON configuration files that serve different architectural purposes. Understanding the structural difference between .codex-plugin/plugin.json and .app.json is essential for developers building or debugging plugins within the OpenAI ecosystem.

Understanding .codex-plugin/plugin.json

Located at plugins/<name>/.codex-plugin/plugin.json, this file provides the comprehensive declarative description that Codex (ChatGPT) consumes to generate marketplace entries and render plugin information in the UI.

The structure includes top-level identity fields such as name, version, description, author, repository, and license. Crucially, the file contains an apps field that references the companion .app.json file (e.g., "apps": "./.app.json"), establishing the link between UI metadata and runtime configuration.

The heart of this file is the interface object, which defines all presentation-layer properties:

  • displayName: Human-readable plugin name shown to users
  • shortDescription and longDescription: Marketing copy and detailed explanations
  • composerIcon and logo: Paths to visual assets
  • websiteURL, privacyPolicyUrl, termsOfServiceUrl: Legal and informational links

For example, in plugins/fyxer/.codex-plugin/plugin.json, the interface object configures how the Fyxer plugin appears in the marketplace, while the apps field points to "./.app.json" to resolve the backend identifier.

Understanding .app.json

Located at the plugin root (e.g., plugins/fyxer/.app.json), this file serves a single, specific purpose: mapping the plugin's logical name to its backend-assigned runtime identifier.

The structure is minimal and flat:

{
  "apps": {
    "fyxer": {
      "id": "asdk_app_696e3c8854748191a6006dd80660ad35"
    }
  }
}

The key fyxer matches the plugin name defined in plugin.json, while the id value represents an opaque backend identifier used by the plugin host to route requests. This file is not consumed by the UI layer and is typically auto-generated during app registration rather than manually maintained. As seen in plugins/demandbase/.app.json, this pattern remains consistent across the repository.

Key Structural Differences

The architectural separation between these files can be summarized as follows:

  • Purpose: plugin.json describes what the plugin is and how it should be presented; .app.json describes how the plugin is identified for backend routing.
  • Content Scope: plugin.json contains rich metadata including the interface object with display names, descriptions, and icon paths; .app.json contains only the runtime id mapping under the apps key.
  • File Location: plugin.json resides in the .codex-plugin/ subdirectory; .app.json sits at the plugin root directory.
  • Consumer: Codex reads plugin.json to generate documentation and marketplace listings; the plugin host reads .app.json to resolve endpoint routing.

Practical Configuration Examples

Complete plugin.json Structure

{
  "name": "example-plugin",
  "version": "1.0.0",
  "description": "Demonstrates the split between UI metadata and runtime IDs.",
  "author": {
    "name": "Example Inc",
    "url": "https://example.com"
  },
  "apps": "./.app.json",
  "interface": {
    "displayName": "Example Plugin",
    "shortDescription": "Shows the structural split.",
    "longDescription": "This plugin demonstrates how UI presentation separates from backend identification.",
    "websiteURL": "https://example.com",
    "privacyPolicyUrl": "https://example.com/privacy",
    "termsOfServiceUrl": "https://example.com/tos",
    "composerIcon": "./assets/icon.png",
    "logo": "./assets/logo.png"
  }
}

Corresponding .app.json Structure

{
  "apps": {
    "example-plugin": {
      "id": "asdk_app_1234567890abcdef1234567890abcdef"
    }
  }
}

Accessing the Runtime ID Programmatically

When developing tooling around these configurations, you can read the mapping as follows:

const fs = require('fs');
const path = require('path');

const pluginName = 'example-plugin';
const appConfigPath = path.join(__dirname, '.app.json');
const appConfig = JSON.parse(fs.readFileSync(appConfigPath, 'utf8'));

const runtimeId = appConfig.apps[pluginName].id;
console.log(`Backend runtime ID: ${runtimeId}`);

Summary

  • .codex-plugin/plugin.json contains the full declarative metadata including the interface object that defines UI presentation, icons, descriptions, and marketplace information for Codex consumption.
  • .app.json provides only the runtime app ID mapping used by the backend plugin host for request routing, containing a single id field under the plugin name key.
  • The apps field in plugin.json creates a reference link to the .app.json file, maintaining clean separation between presentation and runtime concerns.
  • In the openai/plugins repository, examples like plugins/fyxer/.codex-plugin/plugin.json and plugins/fyxer/.app.json demonstrate this consistent architectural pattern across all plugin implementations.

Frequently Asked Questions

Can a plugin function without an .app.json file?

No. While plugin.json defines the plugin's identity and UI presentation, the .app.json file is required to provide the backend runtime id that the plugin host uses to route requests. Without this mapping, the system cannot resolve the plugin's endpoints, even though Codex could theoretically render the UI metadata from plugin.json alone.

Is the app ID in .app.json manually configurable?

Typically, no. According to the repository structure, the id value (e.g., asdk_app_696e3c8854748191a6006dd80660ad35) is assigned automatically by the backend when the app is registered. This file is generated as part of the deployment process and should not be manually edited, as doing so would break the routing between the plugin host and the actual backend service.

Why doesn't plugin.json simply embed the app ID instead of referencing .app.json?

This separation follows the architectural principle of decoupling presentation from runtime configuration. By keeping the environment-specific id in a separate file, plugin.json remains portable and can be shared or versioned independently of backend identifiers. This allows the same plugin.json to be used across different deployment stages where the runtime id would differ, while only the .app.json file needs to change per environment.

Where are these files located in the openai/plugins repository?

Each plugin follows the convention of placing plugin.json inside the .codex-plugin/ subdirectory (e.g., plugins/demandbase/.codex-plugin/plugin.json) and .app.json at the plugin root (e.g., plugins/demandbase/.app.json). This consistent file structure allows the Codex system to discover metadata and the plugin host to resolve runtime IDs using predictable paths.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →