</ >

Creating a Custom GPT with the OpenAI API and GPT Builder

How to define, configure, and deploy a Custom GPT using an OpenAI Actions schema — from writing the JSON spec to wiring it up in the GPT Builder UI.


Custom GPTs let you ship a purpose-built assistant directly inside ChatGPT — with a defined persona, custom instructions, and live API connections through Actions. Here’s how to build one from scratch.

What You’re Building

A Custom GPT with an Action — a connection to an external API described as an OpenAPI 3.1 JSON schema. When a user asks something that maps to your API, ChatGPT calls it automatically and works the response into its reply.

Step 1 — Write the OpenAPI Schema

Actions are powered by OpenAPI 3.1. You paste this JSON directly into the GPT Builder. Here’s a minimal example for a weather API:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Weather API",
    "description": "Get current weather and forecasts for any city.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.yourweatherservice.com"
    }
  ],
  "paths": {
    "/current": {
      "get": {
        "operationId": "getCurrentWeather",
        "summary": "Get current weather for a city",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The city name, e.g. 'London' or 'New York'"
          },
          {
            "name": "units",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["metric", "imperial"],
              "default": "metric"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current weather data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "city": { "type": "string" },
                    "temperature": { "type": "number" },
                    "condition": { "type": "string" },
                    "humidity": { "type": "number" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/forecast": {
      "get": {
        "operationId": "getForecast",
        "summary": "Get a 5-day forecast for a city",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Forecast data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "city": { "type": "string" },
                    "days": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": { "type": "string" },
                          "high": { "type": "number" },
                          "low": { "type": "number" },
                          "condition": { "type": "string" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

A few rules that matter:

  • Every endpoint needs a unique operationId — ChatGPT uses this to decide which endpoint to call
  • Descriptions on parameters and operations are read by the model, not just humans — write them clearly
  • Your server must be publicly reachable over HTTPS

Step 2 — Build the API Endpoint

Your API needs to accept the requests ChatGPT will send and return clean JSON. Here’s a simple Hono example:

import { Hono } from "hono";

const app = new Hono();

app.get("/current", async (c) => {
  const city = c.req.query("city");
  const units = c.req.query("units") ?? "metric";

  if (!city) {
    return c.json({ error: "city is required" }, 400);
  }

  // Fetch from your data source
  const data = await fetchWeatherData(city, units);

  return c.json({
    city: data.name,
    temperature: data.main.temp,
    condition: data.weather[0].description,
    humidity: data.main.humidity,
  });
});

app.get("/forecast", async (c) => {
  const city = c.req.query("city");
  const data = await fetchForecastData(city);

  return c.json({
    city,
    days: data.list.slice(0, 5).map((d) => ({
      date: d.dt_txt,
      high: d.main.temp_max,
      low: d.main.temp_min,
      condition: d.weather[0].description,
    })),
  });
});

export default app;

Deploy this somewhere with a public HTTPS URL before moving to the GPT Builder.

Step 3 — Configure in GPT Builder

  1. Go to chatgpt.com → your profile → My GPTsCreate a GPT
  2. Switch to the Configure tab
  3. Fill in Name, Description, and Instructions — the instructions act as your system prompt

Example instructions for the weather GPT:

You are a friendly weather assistant. When users ask about weather, always call the API to get real-time data — never guess or use your training data for current conditions. Present temperatures in the unit the user prefers. If a city is ambiguous, ask for clarification before calling the API.
  1. Scroll to Actions → click Create new action
  2. Paste your OpenAPI JSON into the schema editor
  3. Set Authentication if your API requires it (API Key, OAuth, or none)
  4. Click Test next to each endpoint to verify the connection

Step 4 — Authentication Options

For protected APIs, GPT Builder supports three auth methods:

// API Key (sent as a header or query param)
{
  "type": "apiKey",
  "in": "header",
  "name": "X-API-Key"
}
// Bearer token
{
  "type": "http",
  "scheme": "bearer"
}

For OAuth, you’ll configure the authorize URL, token URL, and scopes directly in the GPT Builder UI — no schema changes needed.

Step 5 — Publish

  • Only me — private, for your own use
  • Anyone with a link — shareable but unlisted
  • Everyone — listed in the GPT Store (requires a verified builder profile)

Key Takeaways

  • operationId is the function name — make it descriptive; the model uses it for routing decisions
  • Descriptions are prompts — the clearer your parameter descriptions, the more reliably ChatGPT will fill them correctly
  • Test with the built-in tester before publishing — it shows you exactly what request was sent and what came back
  • HTTPS is non-negotiable — ChatGPT won’t connect to plain HTTP endpoints
  • Keep responses lean — ChatGPT has a context window; don’t return 500-field objects when 5 fields will do