Head terms

Posting to social media from ChatGPT: Custom GPT Actions with PostGate

How to wire a Custom GPT Action to PostGate's OpenAPI contract, why ChatGPT's native MCP connectors need OAuth PostGate doesn't speak yet, and the safety gap you have to close yourself since POST /api/posts publishes immediately unless you say otherwise.

Quick answer

ChatGPT can post to social media two ways: a Custom GPT Action wired to an OpenAPI schema with API-key auth, or native MCP connectors — which OpenAI expects to sit behind a full OAuth 2.1 server. PostGate's MCP server uses a static bearer key, so the Custom GPT Action route is the supported path today: import postgate.studio/openapi.json and force draft: true.

ChatGPT doesn't call arbitrary third-party APIs by instinct — it needs to be told what an API looks like before it can use one. For a Custom GPT, that means an OpenAPI schema pasted into the Actions editor. For the newer Connectors and Developer Mode surfaces, it means a proper MCP server behind OAuth. Those are two different integration paths with two different security postures, and as of today only one of them is a direct fit for PostGate's hosted infrastructure. This post covers both, precisely, and is honest about which one you should actually wire up.

PostGate handles OAuth, publishing and delivery evidence for every network through one API. Start free or read the docs.

01

What are ChatGPT's two ways to call an external API?

Custom GPT Actions have been the standard way to give a GPT access to an external service since OpenAI shipped them: you provide an OpenAPI-compliant schema describing one or more endpoints, and ChatGPT uses the schema's names, descriptions, and parameters to decide which action to call and how to fill in the request. Authentication is configured per-GPT as either an API key (sent as a bearer token or custom header) or OAuth — no separate protocol, no separate server, just a schema and a credential.

Separately, OpenAI has been rolling out native Model Context Protocol support across its own products — the Agents SDK, the Responses API, and ChatGPT itself through Apps & Connectors settings and Developer Mode. That path connects ChatGPT directly to an MCP server the same way an MCP-native client like Claude does, without an OpenAPI schema in between. But OpenAI's own MCP server documentation is explicit that an authenticated MCP server connecting to ChatGPT is expected to implement a full OAuth 2.1 authorization-code-with-PKCE flow — an authorization endpoint, a token endpoint, and discovery metadata — not a static bearer token passed at setup time.

PostGate's hosted MCP server, like the MCP connection documented here, authenticates with a plain Authorization: Bearer $POSTGATE_API_KEY header — the same scoped API key used everywhere else in the product. That's the right shape for MCP clients that accept a static token in their config, which is exactly how Claude connects to it. It is not, today, an OAuth authorization server. So plugging PostGate's MCP server directly into ChatGPT's native connector UI isn't a supported path yet. The Custom GPT Action route, which was built around API-key auth from the start, is the one that works right now.

02

How do you wire a Custom GPT Action to PostGate?

PostGate publishes a single OpenAPI 3.1 document that describes every endpoint, including POST /api/posts, and it's the same document the generated SDKs are built from. In the GPT editor, open Configure → Actions → Create new action, then use Import from URL and point it at the schema below instead of hand-writing one.

Set authentication to API Key, auth type Bearer, and paste a scoped PostGate key — start with a pg_test_ key while you get the action's behavior right, since test keys run the full request lifecycle without touching a real connected account.

Import the live schemaPostGate API
https://postgate.studio/openapi.json
03

Why can a Custom GPT publish immediately?

Here is the distinction that matters most. PostGate's hosted MCP server has a create_post tool that creates a draft by default — it only publishes when the call explicitly includes confirm: true. That default is baked into the MCP tool contract itself. The raw POST /api/posts endpoint, the one a Custom GPT Action calls directly, does not carry that same default. It supports four modes on one endpoint: draft: true saves it for approval, a scheduled_at timestamp schedules it, scheduled_at: "queue" takes the next recurring slot — and a request with none of those fields starts delivering immediately.

That means an OpenAPI schema imported as-is, with a GPT free to fill in whatever fields it thinks the request needs, can produce an immediate live publish on the first try. Nothing in Actions today interrupts that with a platform-level confirmation step before a side-effecting call runs — OpenAI's own Actions documentation describes how ChatGPT selects and fills an action, but describes no approval or review step before it fires. Whatever hesitation a GPT shows in practice is model judgment, not a guarantee the platform enforces the way PostGate's MCP tool contract does.

Immediate publish — no draft field setPostGate API
POST /api/posts?profile_group_id=grp_acme123
Authorization: Bearer $POSTGATE_API_KEY

{
  "post": { "body": "Launch day. 🚀" },
  "profiles": ["pro_instagram123", "pro_linkedin456"]
}
04

How do you make the action draft-first yourself?

Since the platform doesn't supply the guardrail here, put it in the schema and the GPT's instructions instead. Restrict the imported createPost operation's request body so post.draft is a required field the model must set, and tell the GPT explicitly, in its instructions, to always pass "draft": true and never to publish without the user confirming in chat first. A human — or a second, explicit action call — then promotes the draft with POST /api/posts/{id}/publish.

This is a prompt-and-schema-level safeguard, not an API-boundary one, so treat it accordingly: it can be worked around by a sufficiently adversarial input, the same way any instruction a model reasons over can be. It's meaningfully better than nothing, and it's the most a Custom GPT Action can offer today — but it is not the same guarantee as a tool contract that fails closed on its own.

Always draft, never publish directlyPostGate API
{
  "post": { "body": "Launch day. 🚀", "draft": true },
  "profiles": ["pro_instagram123", "pro_linkedin456"]
}
05

How should you scope the key you hand a GPT?

The credential still matters most. Issue the GPT a key with publish scope, not full — publish covers content and scheduling without destructive or administrative operations like connection management or key issuance. Bind it permanently to one profile group, so even if a prompt injection or a bad instruction gets the GPT to fill in the wrong account, the key physically cannot reach any customer's data outside that group. PostGate rejects any attempt to override that binding.

If you want the stronger, API-boundary-enforced version of this — draft-first as a contract instead of an instruction — that's exactly what the hosted MCP server gives an MCP-native client. Claude connects to it directly today; see how Claude does it for the comparison.

Frequently asked questions

Can ChatGPT connect to an MCP server directly?

Yes — OpenAI supports MCP natively through the Agents SDK, the Responses API, and ChatGPT's Apps & Connectors settings and Developer Mode. But OpenAI's documentation is explicit that an authenticated MCP server connecting to ChatGPT is expected to implement a full OAuth 2.1 authorization-code-with-PKCE flow, not a static bearer token. PostGate's hosted MCP server authenticates with a plain bearer API key, so plugging it into ChatGPT's native connector UI is not a supported path yet.

Will a Custom GPT publish to social media without asking me first?

It can. The raw POST /api/posts endpoint starts delivering immediately when a request includes no draft or scheduled_at field, and nothing in Actions today interrupts a side-effecting call with a platform-level confirmation step. Whatever hesitation a GPT shows in practice is model judgment, not a guarantee the platform enforces — so an OpenAPI schema imported as-is can produce an immediate live publish on the first try.

How do I make a Custom GPT draft-first?

Put the guardrail in the schema and instructions, since the endpoint won't: restrict the createPost operation's request body so post.draft is a required field, and tell the GPT explicitly to always pass draft: true and never publish without the user confirming in chat. A human then promotes the draft with POST /api/posts/{id}/publish. This is a prompt-and-schema-level safeguard — meaningfully better than nothing, but not the same as a tool contract that fails closed.

What API key should I give a Custom GPT?

A key with publish scope, not full — publish covers content and scheduling without destructive or administrative operations like connection management or key issuance. Bind it permanently to one profile group so a prompt injection or bad instruction cannot reach data outside that group, and start with a pg_test_ key, which runs the full request lifecycle without touching a real connected account.

Build it with PostGate

One API for every social platform your customers care about — sandboxed, documented, and ready today.

Start building Read the docs