The rule that decides most of it

If the action returns per-user data or changes anything, the user must be authenticated individually. A shared API key gives every user of the GPT the access of whoever holds that key — which is rarely what anyone intended.

Authentication is where GPT Actions projects either become defensible or become an audit finding. This covers the options, when each is appropriate, and the mistakes worth avoiding.

The options

MethodAppropriate forNot appropriate for
NoneGenuinely public dataAnything internal
API keyRead-only, non-personal, low sensitivityPer-user data; any write
OAuthPer-user data, writes, anything sensitiveNothing — it is the default answer

The shared-key failure mode is worth stating plainly. If your action authenticates with one key that has broad access, then any user of the GPT can retrieve anything that key can reach — regardless of what their own role permits. The assistant becomes a way around your access control rather than a client of it.

What OAuth gives you that a key does not

  • Per-user identity, so your existing permissions apply unchanged.
  • Scoped consent — the user sees and approves what is being granted.
  • Individual revocation without affecting anyone else.
  • Expiry, limiting the value of a leaked token.
  • Attribution in logs — a real person, not a service account.

Attribution alone justifies the extra effort. An audit log full of entries from "integration-user" answers no question anyone will actually ask.

Designing the API behind the action

Authentication proves who is asking. The API design determines what they can reach:

  1. Expose narrow operations — "get order status by ID", not "query orders".
  2. Validate every parameter. Assume inputs may be malformed or adversarial.
  3. Apply the user's permissions server-side, never relying on the caller to filter.
  4. Return only needed fields. Whole-record responses leak data nobody asked for.
  5. Rate limit per user to contain loops and bulk extraction.
  6. Log the request, the identity and what was returned.
  7. Fail closed — if authorisation is unclear, refuse.

Write the operation list before writing any code. If you can enumerate every operation on one page and justify each, the security conversation is short. If the list says "flexible query endpoint", it will not be.

Mistakes that recur

MistakeConsequence
Shared key for per-user dataAccess control bypass
Permissions enforced only in the schema descriptionNo enforcement at all
Returning full recordsData disclosure beyond the use case
No rate limitingRunaway loops; bulk extraction unnoticed
Secrets in the action schemaExposure
No loggingCannot answer "what was accessed"
Write operations without confirmationIrreversible actions from an ambiguous request

Writes need more than authentication

  • Explicit confirmation before anything consequential happens.
  • Idempotency so a retry does not create a second record.
  • An audit entry recording who approved what.
  • Reversibility where possible, or a clear reason it is not.
  • Value limits where amounts are involved, with escalation above them.

Content returned by an action must never authorise a further action. If a retrieved record contains text instructing the assistant to do something, following it would mean an attacker who can write into your data can drive your API. Authority comes from the authenticated user's request, never from returned data.

A rollout that survives review

  1. OAuth from the start — retrofitting it is worse than building it.
  2. Read-only first, with full logging.
  3. A small user group until usage patterns are understood.
  4. Review the logs, particularly denied requests.
  5. Add writes individually, each with confirmation and audit.

Building GPT Actions against internal systems? Tell us what needs connecting. See our ChatGPT plugin service, the custom GPT guide, and MCP security practices.

Frequently asked questions

For a read-only action over genuinely public information, an API key is defensible. The moment the action touches per-user data or changes anything, a shared key means every user has the key holder's access — which is a privilege escalation path, not an authentication scheme.
Put a service in front of it that does. This is common with older systems, and it is the right place to add validation, filtering, rate limiting and logging as well. It is more work than pointing at the existing API, and it is the difference between a connector you can defend and one you cannot.
Design for it explicitly — refresh tokens where the flow supports them, and a clear re-authentication prompt where it does not. Expiry handled badly produces intermittent failures that users interpret as the tool being unreliable.