# How to Integrate with External APIs Using OpenAI Plugins: A Complete Technical Guide

> Learn to integrate external APIs with OpenAI plugins. This technical guide covers JSON manifests, OpenAPI specs, and Python scripts for seamless API calls. Master plugin development today.

- Repository: [OpenAI/plugins](https://github.com/openai/plugins)
- Tags: how-to-guide
- Published: 2026-06-26

---

**OpenAI Plugins enable language models to invoke external HTTP APIs through a declarative framework that combines JSON manifests, OpenAPI specifications, and Python wrapper scripts to map natural language intents to concrete function calls.**

The `openai/plugins` repository provides a composable, file-system-driven architecture for connecting large language models to third-party services. By leveraging declarative configuration files and lightweight implementation scripts, developers can expose any REST API to the model without modifying core runtime code.

## Architecture of OpenAI Plugins for External API Integration

The framework separates concerns into distinct components that together describe how to **integrate with external APIs using OpenAI plugins**. Each component serves a specific role in the discovery, authentication, and execution pipeline.

### Plugin Manifest and OpenAPI Specification

The **Plugin Manifest** ([`.app.json`](https://github.com/openai/plugins/blob/main/.app.json)) serves as the entry point for every plugin. Located at paths like [`plugins/figma/.app.json`](https://github.com/openai/plugins/blob/main/plugins/figma/.app.json), this JSON file declares the plugin package metadata, version, and the **OpenAPI specification** that describes the external service's HTTP endpoints.

The OpenAPI spec embedded in the manifest defines available paths, parameters, request/response schemas, and data types. This specification is either included directly in the manifest or referenced from a separate [`openapi.yaml`](https://github.com/openai/plugins/blob/main/openapi.yaml) file, providing the model with a machine-readable contract of the external API's capabilities.

### Authentication Configuration

The **Authentication Config** describes credential acquisition and injection methods. As documented in [`plugins/box/skills/box/references/auth-and-setup.md`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/references/auth-and-setup.md), the framework supports multiple authentication flows including OAuth2 client-credentials, API keys, and bearer tokens.

This configuration specifies how the runtime should obtain access tokens and where to inject them in outbound HTTP requests—whether as query parameters, headers, or cookie values.

### Skills, Functions, and Agent YAML

A **skill** is a reusable unit that maps natural-language intents to concrete function calls. Each skill contains:

- **Metadata** ([`evals.json`](https://github.com/openai/plugins/blob/main/evals.json) or [`metadata.json`](https://github.com/openai/plugins/blob/main/metadata.json)) that tells the model which function name, arguments, and return types to use
- **Agent YAML** ([`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml)) that constructs the OpenAI function-calling schema visible to the model

The file [`plugins/box/skills/box/agents/openai.yaml`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/agents/openai.yaml) demonstrates how argument definitions are pulled from skill metadata and wired to implementation scripts, creating the bridge between the model's function-calling interface and the external API.

### Implementation Scripts and Runtime Dispatcher

**Implementation Scripts** are Python or Node.js files that receive parsed arguments, perform actual HTTP requests, and return JSON-serializable responses. The file [`plugins/box/skills/box/scripts/box_rest.py`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/scripts/box_rest.py) provides a real-world example of a wrapper that communicates with Box's Content API.

The **Runtime Dispatcher**, implemented in [`.agents/skills/plugin-creator/scripts/create_basic_plugin.py`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/scripts/create_basic_plugin.py), loads the manifest, spins up a FastAPI server, and registers functions with the OpenAI API. This generic agent eliminates the need to build custom servers for each integration.

## Execution Flow: From User Prompt to API Response

When you integrate with external APIs using OpenAI plugins, the execution follows a precise six-step flow:

1. The user submits a prompt, and the chat model receives the **function-calling schema** originating from [`agents/openai.yaml`](https://github.com/openai/plugins/blob/main/agents/openai.yaml)
2. The model decides to invoke a specific function (e.g., `list_documents`)
3. The plugin runtime receives the call, unmarshals arguments, and executes the corresponding implementation script
4. The script builds an HTTP request using the OpenAPI spec, adds the appropriate auth header (API key or OAuth token), and forwards it to the external service
5. The response is transformed into the declared return schema and sent back to the model as the function result
6. The model integrates the result into its next reply, completing the external API integration loop

Because the system is declarative, adding a new third-party API requires only creating new configuration files and a thin wrapper script—no changes to the core runtime.

## Complete Example: Building a Weather Plugin

Below is a self-contained example demonstrating how to integrate with external APIs using OpenAI plugins to call the OpenWeatherMap API.

First, create the plugin manifest at [`plugins/weather/.app.json`](https://github.com/openai/plugins/blob/main/plugins/weather/.app.json):

```json
{
  "name": "weather",
  "version": "0.1.0",
  "openapi": {
    "openapi": "3.0.0",
    "info": {
      "title": "Weather API",
      "version": "1.0"
    },
    "paths": {
      "/weather": {
        "get": {
          "parameters": [
            { "name": "q", "in": "query", "required": true, "schema": { "type": "string" } },
            { "name": "appid", "in": "query", "required": true, "schema": { "type": "string" } }
          ],
          "responses": {
            "200": {
              "description": "Current weather",
              "content": {
                "application/json": {
                  "schema": { "$ref": "#/components/schemas/WeatherResponse" }
                }
              }
            }
          }
        }
      }
    },
    "components": {
      "schemas": {
        "WeatherResponse": {
          "type": "object",
          "properties": {
            "temp_c": { "type": "number" },
            "description": { "type": "string" }
          },
          "required": ["temp_c", "description"]
        }
      }
    }
  },
  "auth": {
    "type": "apiKey",
    "in": "query",
    "name": "appid"
  }
}

```

Next, define the function schema at [`plugins/weather/skills/get_current/agents/openai.yaml`](https://github.com/openai/plugins/blob/main/plugins/weather/skills/get_current/agents/openai.yaml):

```yaml
name: get_current_weather
description: Retrieve the current temperature for a city.
parameters:
  type: object
  properties:
    city:
      type: string
      description: Name of the city (e.g., "Paris")
required:
  - city

```

Finally, implement the request logic at [`plugins/weather/skills/get_current/scripts/weather.py`](https://github.com/openai/plugins/blob/main/plugins/weather/skills/get_current/scripts/weather.py):

```python
import os
import requests

def get_current_weather(city: str) -> dict:
    api_key = os.getenv("WEATHER_API_KEY")
    url = "https://api.openweathermap.org/data/2.5/weather"
    resp = requests.get(url, params={"q": city, "appid": api_key, "units": "metric"})
    data = resp.json()
    return {
        "temp_c": data["main"]["temp"],
        "description": data["weather"][0]["description"],
    }

```

When a user asks *"What's the weather in Tokyo?"*, the model automatically calls `get_current_weather` with `"Tokyo"` as the argument, the plugin hits OpenWeatherMap, and the model embeds the temperature in its reply.

## Key Implementation Files in the Repository

The `openai/plugins` repository contains several reference implementations that demonstrate production patterns for external API integration:

- **[`plugins/figma/.app.json`](https://github.com/openai/plugins/blob/main/plugins/figma/.app.json)** – Example manifest containing a complete OpenAPI specification for the Figma API
- **[`plugins/box/skills/box/scripts/box_rest.py`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/scripts/box_rest.py)** – Real-world Python wrapper that authenticates and communicates with Box's Content API
- **[`plugins/box/skills/box/agents/openai.yaml`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/agents/openai.yaml)** – Function-calling schema that exposes Box capabilities to the language model
- **[`plugins/box/skills/box/references/auth-and-setup.md`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/references/auth-and-setup.md)** – Documentation of OAuth2 client-credentials flow implementation
- **[`.agents/skills/plugin-creator/scripts/create_basic_plugin.py`](https://github.com/openai/plugins/blob/main/.agents/skills/plugin-creator/scripts/create_basic_plugin.py)** – Generic scaffolding script that transforms JSON/YAML descriptors into a running FastAPI server
- **[`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py)** – Alternative implementation calling the Zotero REST API

## Summary

- **OpenAI Plugins** use a declarative architecture to expose external HTTP APIs to language models through function calling
- The **Plugin Manifest** ([`.app.json`](https://github.com/openai/plugins/blob/main/.app.json)) and **OpenAPI specification** define the API contract, while **Agent YAML** files create the function schema visible to the model
- **Implementation scripts** in Python or Node.js handle the actual HTTP requests, authentication, and response transformation
- Adding new external services requires only creating new configuration files and wrapper scripts in the `plugins/` directory, with no changes to the core runtime
- The framework supports multiple authentication schemes including OAuth2 and API keys, configured declaratively in the manifest

## Frequently Asked Questions

### How does the OpenAI plugin runtime know which external API endpoint to call?

The runtime consults the **OpenAPI specification** embedded in the plugin manifest ([`.app.json`](https://github.com/openai/plugins/blob/main/.app.json)) to determine the exact HTTP method, path, parameters, and data schemas for each endpoint. When the model invokes a function, the implementation script (such as [`plugins/box/skills/box/scripts/box_rest.py`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/scripts/box_rest.py)) constructs the request using these specifications, ensuring the external API receives properly formatted calls.

### What authentication methods are supported when integrating external APIs?

The framework supports **OAuth2 client-credentials**, **API keys**, and **bearer tokens** according to the repository's authentication configuration files. As shown in [`plugins/box/skills/box/references/auth-and-setup.md`](https://github.com/openai/plugins/blob/main/plugins/box/skills/box/references/auth-and-setup.md), credentials can be injected as query parameters, headers, or cookies based on the `auth` block in the manifest, with sensitive values stored in environment variables rather than committed to source control.

### Can I use languages other than Python for the implementation scripts?

While the examples in the repository primarily use Python (such as [`plugins/zotero/skills/zotero/scripts/zotero.py`](https://github.com/openai/plugins/blob/main/plugins/zotero/skills/zotero/scripts/zotero.py)), the architecture is language-agnostic. The runtime dispatcher executes the script specified in the skill configuration, meaning Node.js, Go, or any executable can handle the HTTP request logic as long as it accepts JSON arguments and returns JSON-serializable output.

### Where should I store sensitive API keys and credentials?

Sensitive credentials should be stored in **environment variables** referenced by your implementation scripts, as demonstrated in the weather example where `WEATHER_API_KEY` is read via `os.getenv()`. Never commit API keys, OAuth secrets, or access tokens to the repository; instead, document the required environment variables in a `references/` markdown file so users know how to configure their local environment.