How to Test OpenAI Plugins Locally Before Publishing: A Complete Developer Guide
You can test OpenAI plugins locally by running the HTTP service on your machine, exposing it via an HTTPS tunnel like ngrok, adding the tunnel domain to your partner console allowlist, and loading the plugin into the ChatGPT Plugin Playground using your tunnel URL.
OpenAI plugins are ordinary HTTP services that expose a plugin manifest at .codex-plugin/plugin.json and implement endpoints described in that manifest. Testing them locally before publishing requires simulating the production environment where HTTPS and domain verification are mandatory. According to the openai/plugins repository, the standard workflow involves tunneling your local development server through a publicly reachable HTTPS URL so that the ChatGPT interface can fetch your manifest and validate your API endpoints.
Start Your Local Plugin Server
Most plugins in the openai/plugins repository are Node.js services that start with npm start, as defined in their package.json. To begin local testing:
-
Clone the repository and navigate to your target plugin directory.
-
Install dependencies using the lockfile to ensure exact versions:
git clone https://github.com/openai/plugins.git
cd plugins/plugins/zoom # example: Zoom plugin
npm ci
- Start the server, which typically listens on port 3000 by default:
npm start
Your plugin is now running locally, but it is not yet accessible to the ChatGPT servers, which require public HTTPS endpoints.
Expose Localhost via HTTPS Tunnel
OpenAI and partner platforms like Zoom require HTTPS endpoints even for local development. The de‑facto standard is to run ngrok (or a comparable tunneling tool) to generate a public URL.
Run ngrok to expose your local port:
ngrok http 3000
This generates a forwarding URL such as https://abcd1234.ngrok.io. Copy this address—you will need it for your manifest and domain allowlist.
Configure the Plugin Manifest
Update your .codex-plugin/plugin.json to point the api.base_url to your ngrok address. The manifest must accurately reflect the tunnel domain for ChatGPT to fetch your OpenAPI specification.
Example configuration from the Zoom plugin:
{
"schema_version": "v1",
"name_for_human": "Zoom",
"name_for_model": "zoom",
"description_for_human": "Interact with Zoom meetings",
"description_for_model": "Plugin to retrieve meeting info, share apps, etc.",
"auth": {
"type": "oauth",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"authorization_url": "https://zoom.us/oauth/authorize",
"token_url": "https://zoom.us/oauth/token",
"scopes": ["zoomapp:inmeeting"]
},
"api": {
"type": "openapi",
"url": "https://abcd1234.ngrok.io/openapi.yaml",
"is_user_authenticated": true
},
"logo_url": "https://raw.githubusercontent.com/openai/plugins/main/plugins/zoom/logo.png",
"contact_email": "[email protected]",
"legal_info_url": "https://zoom.us/terms"
}
Ensure the api.url field uses your current ngrok domain, as this changes every time you restart ngrok on the free tier.
Whitelist Your Tunnel Domain
The marketplace UI validates the domain listed in your manifest against an allowlist. According to plugins/zoom/skills/zoom-apps-sdk/troubleshooting/debugging.md, missing this step is the most common cause of a blank panel in the ChatGPT interface.
Navigate to your partner console (e.g., Zoom Marketplace or OpenAI Plugin Store) and add the ngrok sub‑domain (e.g., abcd1234.ngrok.io) to the Domain Allowlist page. Without this verification step, ChatGPT cannot fetch your manifest or API definitions.
Set Up OAuth Credentials
If your plugin uses authentication, you must provide client credentials via environment variables. The Zoom SDK guide in plugins/zoom/skills/zoom-apps-sdk/references/full-guide.md specifies the required variables:
Create a .env file in your plugin root:
cat > .env <<EOF
ZOOM_APP_CLIENT_ID=YOUR_CLIENT_ID
ZOOM_APP_CLIENT_SECRET=YOUR_CLIENT_SECRET
ZOOM_APP_REDIRECT_URI=https://abcd1234.ngrok.io/auth
SESSION_SECRET=$(openssl rand -hex 16)
EOF
Restart your local server after creating this file so it loads the new environment variables. Ensure your OAuth scopes match those declared in your manifest to avoid silent authentication failures.
Load and Test in ChatGPT Plugin Playground
With your tunnel active and domain whitelisted, you can test the full integration:
- Open the ChatGPT Plugin Playground at
https://chat.openai.com/?model=gpt-4-plugins. - Navigate to Plugins → Develop your own plugin → Add a local plugin.
- Paste your HTTPS tunnel URL (e.g.,
https://abcd1234.ngrok.io) and the path to your manifest (.codex-plugin/plugin.json). - Click Add.
The playground will fetch your manifest, validate the schema, and let you issue API calls directly from the UI. You can now invoke your plugin by name (e.g., /zoom …) or via natural language prompts.
Validate Endpoints with curl
While the Playground validates the full integration, you should verify individual endpoints independently using curl or Postman. This isolates network issues from plugin logic errors.
Test a specific endpoint:
curl -H "Authorization: Bearer $TOKEN" \
https://abcd1234.ngrok.io/api/meetings/123456789
Replace $TOKEN with a valid access token obtained through your OAuth flow. If this returns the expected JSON, your local server is correctly handling requests and authentication.
Troubleshoot Common Local Testing Issues
Several specific pitfalls documented in plugins/zoom/skills/zoom-apps-sdk/references/common-issues.md can break local testing:
- Domain allowlist errors: If you see a blank UI panel, verify your ngrok domain is added to the partner console allowlist and that you have restarted the tunnel since the last domain change.
- Global variable conflicts: The Zoom SDK’s CDN script defines
window.zoomSdk. Redeclaring this triggers aSyntaxError. Use a different variable name or import via NPM:import zoomSdk from '@zoom/appssdk'. - Capability gating: All SDK calls must be listed in the
config({ capabilities: [...] })initialization. Unlisted capabilities raise runtime errors. - OAuth scope mismatch: Missing scopes in the Marketplace console cause silent failures. Add required scopes via the Scopes tab in your partner dashboard.
Summary
Testing OpenAI plugins locally before publishing requires coordinating a local HTTP server, an HTTPS tunnel, and the ChatGPT Plugin Playground. Key takeaways include:
- Run your plugin locally with
npm startafter installing dependencies withnpm ci. - Expose localhost via ngrok to satisfy HTTPS requirements, updating
.codex-plugin/plugin.jsonwith the tunnel URL. - Add the ngrok domain to your partner console Domain Allowlist to prevent blank UI panels.
- Store OAuth credentials in a
.envfile using the variable names specified in your plugin’s SDK documentation. - Load the plugin into the ChatGPT Plugin Playground to validate the manifest schema and test natural language interactions.
- Use
curlto verify individual endpoints and isolate authentication or routing issues.
Frequently Asked Questions
Why does my plugin show a blank panel in ChatGPT?
A blank panel typically indicates a domain allowlist issue. The marketplace validates the domain in your .codex-plugin/plugin.json against an allowlist in your partner console. If you are using ngrok, you must add the current sub‑domain (e.g., abcd1234.ngrok.io) to the allowlist every time you restart ngrok, as the free tier generates new URLs for each session.
Can I test OpenAI plugins without using ngrok?
While you can test the HTTP logic locally, ChatGPT requires a publicly reachable HTTPS URL to fetch your manifest and OpenAPI specification. Without ngrok or a similar tunneling service (such as Cloudflare Tunnel or localtunnel), ChatGPT cannot reach your localhost. Therefore, ngrok is the de‑facto standard for local end‑to‑end testing.
What environment variables do I need for OAuth‑enabled plugins?
OAuth plugins require variables defined in the SDK documentation, such as ZOOM_APP_CLIENT_ID, ZOOM_APP_CLIENT_SECRET, and ZOOM_APP_REDIRECT_URI for the Zoom plugin. Store these in a .env file in your plugin root. Refer to plugins/zoom/skills/zoom-apps-sdk/references/full-guide.md for the specific environment variables required for your integration.
How do I fix "zoomSdk is not defined" errors during local testing?
This error occurs when you redeclare the global window.zoomSdk variable, which is defined by the Zoom SDK’s CDN script. Avoid redeclaration by using a different variable name for your instance, or switch to the NPM import: import zoomSdk from '@zoom/appssdk'. Additionally, ensure all SDK methods you call are listed in the capabilities array of your configuration object, as unlisted capabilities raise errors.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →