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
- Go to chatgpt.com → your profile → My GPTs → Create a GPT
- Switch to the Configure tab
- 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.
- Scroll to Actions → click Create new action
- Paste your OpenAPI JSON into the schema editor
- Set Authentication if your API requires it (API Key, OAuth, or none)
- 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
operationIdis 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