Custom GPT Actions + Google Places/Business Profile: persist per-user API key (no re-prompt), attach to request (query or header), and handle FieldMask (v1)

I’m building a Custom GPT that, given a restaurant name, fetches Google Business/Places data: basic info, reviews, and photos (5).
If I manually enter my Google API key at test time, the API calls work.
What I need is a reliable way to persist the API key per user (entered once) and have Actions automatically attach it to outgoing requests—either as a query parameter (?key=..., Legacy Places) or as a header (X-Goog-Api-Key, Places v1). I also need a clean way to pass the required FieldMask for v1 (X-Goog-FieldMask).

I’ve read that embedding my own key inside the GPT is not supported and each user should provide their own key—this is fine. I just don’t want the user to be prompted every time.

Goal

  • Per-user API key persistence: user enters their Google API key once; Actions reuse it for future calls.

  • Attach the key automatically to each request:

    • Legacy: ?key=... query param, or

    • v1: X-Goog-Api-Key request header.

  • Pass FieldMask for v1 cleanly (ideally without the user having to type headers by hand every time).

  • No proxy with payload limits (GAS has a 1MB cap, so I want to call Google directly from Actions).

Environment

  • Custom GPT → Actions (OpenAPI 3.1)

  • In my Actions editor UI, the Authentication → API Key → Custom screen appears to allow only one “Custom header name”. I don’t see a built-in way to add the key as a query param.

  • When I tried putting securitySchemes/security in the OpenAPI to add the key automatically, the editor often ignored it or threw parser errors (details below).

What works

  • If I test with manual key entry, I can call:

    • Legacy: GET https://maps.googleapis.com/maps/api/place/findplacefromtext/json

    • Then GET .../details/json
      and I get place_id, details, and photo_reference. Photos can be constructed via /place/photo?maxwidth=...&photo_reference=...&key=....

What doesn’t / friction points

  1. Persisting the per-user key so it’s not prompted each call (I want “enter once, reuse”).

  2. Attaching the key as query param for Legacy when the Actions UI only exposes “Custom header name”.

  3. Using Places v1 which expects:

    • X-Goog-Api-Key header, and

    • required X-Goog-FieldMask header (per request).
      I need to model this cleanly in OpenAPI and the Actions UI.

  4. When I tried relying on OpenAPI securitySchemes (apiKey in query/header) with UI auth = none, the editor sometimes said things like:

    • In components section, schemas subsection is not an object

    • parameter X-Goog-FieldMask has location header; ignoring

    • or it simply didn’t attach the auth as expected.
      So I’m unsure about the recommended pattern for Actions: should we always use the UI auth instead of OpenAPI security? If yes, how to pass query keys or additional headers like X-Goog-FieldMask?

Minimal reproducible specs

A) Legacy (works if the key is attached as query)

{
  "openapi": "3.1.0",
  "info": { "title": "Google Places (Legacy) Minimal", "version": "1.0.0" },
  "servers": [{ "url": "https://maps.googleapis.com/maps/api/place" }],
  "paths": {
    "/findplacefromtext/json": {
      "get": {
        "operationId": "findPlace",
        "parameters": [
          { "name": "input", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "inputtype", "in": "query", "required": true, "schema": { "type": "string", "enum": ["textquery"] } },
          { "name": "fields", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/details/json": {
      "get": {
        "operationId": "placeDetails",
        "parameters": [
          { "name": "place_id", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "fields", "in": "query", "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    }
  }
}

Test values

  • GET /findplacefromtext/json with input=Ginza Kyubey, inputtype=textquery, fields=place_id

  • GET /details/json with place_id=<from previous>, fields=name,formatted_address,website,international_phone_number,opening_hours,rating,user_ratings_total,photos

Issue: The Actions UI seems to only let me set a custom header for API key (not query), so I can’t attach ?key=... automatically unless I use a proxy (which I want to avoid).


B) Places API v1 (preferable, uses headers)

{
  "openapi": "3.1.0",
  "info": { "title": "Google Places API (v1) Minimal", "version": "1.0.0" },
  "servers": [{ "url": "https://places.googleapis.com" }],
  "paths": {
    "/v1/places:searchText": {
      "post": {
        "operationId": "searchText",
        "parameters": [
          {
            "in": "header",
            "name": "X-Goog-FieldMask",
            "required": true,
            "schema": { "type": "string" },
            "description": "e.g. places.id,places.displayName,places.formattedAddress"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["textQuery"],
                "properties": {
                  "textQuery": { "type": "string" },
                  "languageCode": { "type": "string", "default": "ja" },
                  "regionCode": { "type": "string", "default": "JP" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/v1/places/{placeId}": {
      "get": {
        "operationId": "getPlace",
        "parameters": [
          { "in": "path", "name": "placeId", "required": true, "schema": { "type": "string" } },
          {
            "in": "header",
            "name": "X-Goog-FieldMask",
            "required": true,
            "schema": { "type": "string" },
            "description": "e.g. id,displayName,formattedAddress,internationalPhoneNumber,websiteUri,regularOpeningHours,rating,userRatingCount,photos"
          }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    }
  }
}

Desired Actions Auth for v1

  • Authentication Type: API Key → Custom

  • Custom header name: X-Goog-Api-Key

  • Users enter their Google API key once, and it’s reused.

Question: How do I also ensure X-Goog-FieldMask is sent every time?

  • Can Actions respect header parameters defined in OpenAPI like above?

  • Or should I model FieldMask as a required string query parameter (not ideal, since Google expects it as a header), or a fixed value via server-side default?

Errors I’ve seen

  • Could not parse valid OpenAPI spec when mixing UI auth with OpenAPI securitySchemes.

  • In components section, schemas subsection is not an object

  • parameter ... has location header; ignoring

  • When relying only on OpenAPI securitySchemes (UI auth = none), auth wasn’t attached and I got PERMISSION_DENIED from Google.

Questions for the forum

  1. What’s the recommended way in Actions to persist a per-user API key so they’re not re-prompted every call? (I assume “Authentication → API Key (Custom)” is the right place, and it is stored per user?)

  2. How to attach the key as a query param (?key=) for Legacy if the UI only exposes “custom header name”? Is there a supported pattern to make Actions append a query param from the API key?

  3. For Places v1, is the pattern “UI auth = API Key (Custom header X-Goog-Api-Key) + model FieldMask as a required header parameter in OpenAPI” the recommended approach? Will Actions reliably include such header parameters?

  4. Is it supported or discouraged to rely on OpenAPI securitySchemes (apiKey in query/header) with UI auth = none in Actions? If supported, any known pitfalls with 3.1 specs and header parameters being ignored?

  5. Any best-practice examples of Actions calling Google APIs that require both an API key header and an additional required header (like FieldMask)?

Thanks!

Pointers to working samples or minimal templates would be greatly appreciated. My ideal outcome is:

  • No proxy,

  • Users enter their Google API key once,

  • Actions automatically attach it (query for Legacy or header for v1),

  • Clean handling of X-Goog-FieldMask for v1,

  • Then I can compose results (info + 5 photos + short review summary) in the GPT response.