Free Intelligex Tool

Bring =GPT() into Google Sheets.

Install one Google Apps Script and turn spreadsheet cells into AI
workflows. Start with 100 free Intelligex-powered calls
or connect your own OpenAI API key for ongoing use.

No browser extension
No API key needed to start
Works inside your own Sheet

S
AI Content Workflow
Intelligex GPT

C2
fx
=GPT(A2,”Write a concise professional summary”)

A
B
C
1
Raw notes
Instruction
Status
2
Customer onboarding takes 4 days…
Summarize for leadership
Ready
3
Contracts arrive in mixed formats…
=GPT(A3,B3)
Running
4
Sales notes from discovery call…
A structured workflow can reduce manual review…
Complete

78 calls remaining
View live usage with =GPT_USAGE()

100
Free Intelligex calls
2 modes
Free credits or your API key
1 formula
Use =GPT() anywhere in Sheets
0 installs
No browser extension required

One formula, many workflows

Use AI where your data already lives.

Generate, clean, classify, extract, translate, and structure content
without moving records out of Google Sheets.

Write and rewrite

Produce summaries, descriptions, email drafts, reports, titles,
snippets, and polished versions of existing text.

Extract structured data

Pull names, dates, categories, keywords, JSON, HTML, and consistent
fields from messy notes or documents.

Classify and normalize

Categorize leads, standardize wording, detect themes, tag records,
translate text, and enforce repeatable formatting.

Process ranges

Pass one cell, several cells, or a complete range into the same
formula and ask GPT to analyze the combined context.

Control the output

Add precise instructions, limit output tokens, select a model in
your own-key mode, and force a new response when needed.

Avoid duplicate charges

Stable request IDs and caching help prevent repeated spreadsheet
recalculations from consuming the same call twice.

Choose your setup

Start free. Bring your own key when you are ready.

Both options use the same Apps Script. Switch between them later from
the Intelligex GPT menu.

For ongoing use

Your own OpenAI API key

Direct OpenAI billing

Send requests directly from Google Apps Script to OpenAI using your
own API account and available models.

  • No Intelligex registration required
  • Prompts do not pass through Intelligex
  • Choose an available OpenAI model
  • Control your own project limits and billing
  • Default model set to gpt-5-nano
!

A ChatGPT subscription and OpenAI API billing are separate. The free
Intelligex allowance does not require an OpenAI key, but own-key mode
is billed through your OpenAI API account.

Secure by design

Your Sheet never receives the Intelligex API key.

Free-credit requests use a verified access token. The private
Intelligex OpenAI key remains on the server.

Google Sheet
=GPT() formula
Apps Script
Access token
Intelligex API
Usage validation
AI Provider
Generated result

The public Apps Script contains no Intelligex OpenAI key. It stores
either your own API key or a server-issued Intelligex access token in
your personal Apps Script User Properties.

Installation

From zero to your first AI formula in a few steps.

No plugin marketplace, browser extension, or external spreadsheet
add-on is required.

Open Apps Script

In Google Sheets, select Extensions → Apps Script.

Copy the script

Unlock the code below and copy the complete Code.gs
content.

Paste and save

Delete the default code, paste the Intelligex script, and save the
project.

Reload the Sheet

Return to Google Sheets and reload the tab. The
Intelligex GPT menu will appear.

Choose your mode

Select 100 free calls or enter your own OpenAI API key.

Authorize and run

Approve Google’s permissions, then enter your first
=GPT() formula.

Complete source code

Unlock, copy, paste, and run.

Enter your email to reveal the complete Google Apps Script. The
source includes both the free-credit and own-key modes.

Version 1.1.0

VIP Content

This is a Free, but VIP, piece of content. Put your email address to continue

Loading...

Code.gs
Google Apps Script · JavaScript
/**
 * Intelligex GPT for Google Sheets
 * Version: 1.1.0
 *
 * Two modes:
 * 1. The user supplies their own OpenAI API key.
 * 2. The user verifies an email address and uses 100 Intelligex-funded calls.
 *
 * IMPORTANT:
 * The Intelligex server endpoints must be installed before the free-credit mode works.
 */

const INTELLIGEX_GPT_CONFIG = Object.freeze({
  CLIENT_VERSION: "1.1.0",

  // Change this only if your WordPress REST namespace is different.
  INTELLIGEX_API_BASE:
    "https://intelligex.ai/wp-json/intelligex-gpt/v1",

  OPENAI_RESPONSES_URL:
    "https://api.openai.com/v1/responses",

  DEFAULT_DIRECT_MODEL: "gpt-5-nano",
  DEFAULT_MAX_OUTPUT_TOKENS: 1000,
  MAX_DIRECT_OUTPUT_TOKENS: 8000,

  // Google Apps Script cache duration: six hours.
  CACHE_SECONDS: 21600,

  MODES: Object.freeze({
    OWN_KEY: "own_api_key",
    INTELLIGEX: "intelligex"
  }),

  PROPERTIES: Object.freeze({
    MODE: "INTELLIGEX_GPT_MODE",
    OPENAI_API_KEY: "INTELLIGEX_GPT_OPENAI_API_KEY",
    ACCESS_TOKEN: "INTELLIGEX_GPT_ACCESS_TOKEN",
    FIRST_NAME: "INTELLIGEX_GPT_FIRST_NAME",
    LAST_NAME: "INTELLIGEX_GPT_LAST_NAME",
    EMAIL: "INTELLIGEX_GPT_EMAIL"
  })
});


/* -------------------------------------------------------------------------- */
/* Spreadsheet menu and setup                                                 */
/* -------------------------------------------------------------------------- */

/**
 * Adds the Intelligex GPT menu whenever the spreadsheet opens.
 */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("Intelligex GPT")
    .addItem("Set up or change mode", "setupIntelligexGPT")
    .addSeparator()
    .addItem("Use my OpenAI API key", "setOwnOpenAIKey")
    .addItem("Register for 100 free calls", "registerForIntelligexFreeCalls")
    .addItem("Verify email code", "verifyIntelligexEmail")
    .addSeparator()
    .addItem("Switch to my API key", "switchToOwnKeyMode")
    .addItem("Switch to Intelligex credits", "switchToIntelligexMode")
    .addSeparator()
    .addItem("Check account and usage", "showIntelligexGPTStatus")
    .addItem("Clear all GPT settings", "clearIntelligexGPTSettings")
    .addToUi();
}


/**
 * Included for future use if this script is converted into a Sheets add-on.
 */
function onInstall(e) {
  onOpen(e);
}


/**
 * Lets the user choose a mode.
 *
 * YES: use the user's own OpenAI key.
 * NO: register or verify for Intelligex free calls.
 */
function setupIntelligexGPT() {
  const ui = SpreadsheetApp.getUi();

  const answer = ui.alert(
    "Set up Intelligex GPT",
    "Choose how GPT should work in this spreadsheet.\n\n" +
      "YES — Use your own OpenAI API key.\n" +
      "NO — Use up to 100 free calls provided by Intelligex.\n" +
      "CANCEL — Make no changes.",
    ui.ButtonSet.YES_NO_CANCEL
  );

  if (answer === ui.Button.YES) {
    setOwnOpenAIKey();
    return;
  }

  if (answer === ui.Button.NO) {
    const properties = getUserProperties_();
    const token = properties.getProperty(
      INTELLIGEX_GPT_CONFIG.PROPERTIES.ACCESS_TOKEN
    );

    if (token) {
      properties.setProperty(
        INTELLIGEX_GPT_CONFIG.PROPERTIES.MODE,
        INTELLIGEX_GPT_CONFIG.MODES.INTELLIGEX
      );

      ui.alert(
        "Intelligex credits enabled",
        "Your existing verified Intelligex access token is now active.",
        ui.ButtonSet.OK
      );
      return;
    }

    registerForIntelligexFreeCalls();
  }
}


/**
 * Collects and stores the user's personal OpenAI API key.
 *
 * UserProperties are used so credentials belong to the current Google user,
 * not to every editor of the spreadsheet.
 */
function setOwnOpenAIKey() {
  const ui = SpreadsheetApp.getUi();

  const response = ui.prompt(
    "Use your OpenAI API key",
    "Paste your OpenAI API key. It will be stored in your personal Apps Script User Properties.",
    ui.ButtonSet.OK_CANCEL
  );

  if (response.getSelectedButton() !== ui.Button.OK) {
    return;
  }

  const apiKey = response.getResponseText().trim();

  if (!isPlausibleOpenAIKey_(apiKey)) {
    ui.alert(
      "Invalid API key",
      "Enter a valid OpenAI API key beginning with “sk-”.",
      ui.ButtonSet.OK
    );
    return;
  }

  const properties = getUserProperties_();

  properties.setProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.OPENAI_API_KEY,
    apiKey
  );

  properties.setProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.MODE,
    INTELLIGEX_GPT_CONFIG.MODES.OWN_KEY
  );

  ui.alert(
    "OpenAI key saved",
    "Your own-key mode is active.\n\n" +
      'You can now use formulas such as =GPT("Write a short summary").',
    ui.ButtonSet.OK
  );
}


/**
 * Collects registration details and asks the Intelligex server to email a
 * verification code.
 */
function registerForIntelligexFreeCalls() {
  const ui = SpreadsheetApp.getUi();

  const consent = ui.alert(
    "Intelligex free calls",
    "Intelligex provides up to 100 free GPT calls after email verification.\n\n" +
      "In this mode, your prompts are sent to the Intelligex server and then " +
      "to the AI provider. Do not submit passwords, confidential data, " +
      "regulated personal information, or other sensitive content.\n\n" +
      "Continue with registration?",
    ui.ButtonSet.YES_NO
  );

  if (consent !== ui.Button.YES) {
    return;
  }

  const firstName = promptRequiredText_(
    "First name",
    "Enter your first name."
  );
  if (firstName === null) return;

  const lastName = promptRequiredText_(
    "Last name",
    "Enter your last name."
  );
  if (lastName === null) return;

  const email = promptRequiredText_(
    "Email address",
    "Enter the email address that should receive your verification code."
  );
  if (email === null) return;

  const normalizedEmail = email.trim().toLowerCase();

  if (!isValidEmail_(normalizedEmail)) {
    ui.alert(
      "Invalid email",
      "Enter a valid email address.",
      ui.ButtonSet.OK
    );
    return;
  }

  try {
    const result = callIntelligexPublicEndpoint_(
      "/register",
      {
        first_name: firstName.trim(),
        last_name: lastName.trim(),
        email: normalizedEmail,
        terms_accepted: true,
        source: "google_sheets",
        client_version: INTELLIGEX_GPT_CONFIG.CLIENT_VERSION
      }
    );

    const properties = getUserProperties_();

    properties.setProperties({
      [INTELLIGEX_GPT_CONFIG.PROPERTIES.FIRST_NAME]:
        firstName.trim(),
      [INTELLIGEX_GPT_CONFIG.PROPERTIES.LAST_NAME]:
        lastName.trim(),
      [INTELLIGEX_GPT_CONFIG.PROPERTIES.EMAIL]:
        normalizedEmail
    });

    ui.alert(
      "Verification code sent",
      result.message ||
        "Check your email for the verification code. Then select " +
        "Intelligex GPT → Verify email code.",
      ui.ButtonSet.OK
    );
  } catch (error) {
    ui.alert(
      "Registration failed",
      friendlyErrorMessage_(error),
      ui.ButtonSet.OK
    );
  }
}


/**
 * Verifies the emailed code and stores the server-issued access token.
 */
function verifyIntelligexEmail() {
  const ui = SpreadsheetApp.getUi();
  const properties = getUserProperties_();

  let email = (
    properties.getProperty(
      INTELLIGEX_GPT_CONFIG.PROPERTIES.EMAIL
    ) || ""
  ).trim().toLowerCase();

  if (!email) {
    email = promptRequiredText_(
      "Email address",
      "Enter the email address used during registration."
    );

    if (email === null) return;

    email = email.trim().toLowerCase();
  }

  if (!isValidEmail_(email)) {
    ui.alert(
      "Invalid email",
      "Enter a valid email address.",
      ui.ButtonSet.OK
    );
    return;
  }

  const code = promptRequiredText_(
    "Verification code",
    "Enter the verification code sent to " + email + "."
  );

  if (code === null) return;

  if (!/^\d{6}$/.test(code.trim())) {
    ui.alert(
      "Invalid code",
      "The verification code must contain six digits.",
      ui.ButtonSet.OK
    );
    return;
  }

  try {
    const result = callIntelligexPublicEndpoint_(
      "/verify",
      {
        email: email,
        code: code.trim(),
        source: "google_sheets",
        client_version: INTELLIGEX_GPT_CONFIG.CLIENT_VERSION
      }
    );

    if (!result.access_token) {
      throw new Error(
        "The Intelligex server did not return an access token."
      );
    }

    properties.setProperties({
      [INTELLIGEX_GPT_CONFIG.PROPERTIES.EMAIL]: email,
      [INTELLIGEX_GPT_CONFIG.PROPERTIES.ACCESS_TOKEN]:
        result.access_token,
      [INTELLIGEX_GPT_CONFIG.PROPERTIES.MODE]:
        INTELLIGEX_GPT_CONFIG.MODES.INTELLIGEX
    });

    ui.alert(
      "Email verified",
      "Intelligex free-credit mode is active.\n\n" +
        "Calls available: " +
        String(
          typeof result.calls_remaining === "number"
            ? result.calls_remaining
            : 100
        ) +
        '\n\nYou can now use formulas such as =GPT("Write a summary").',
      ui.ButtonSet.OK
    );
  } catch (error) {
    ui.alert(
      "Verification failed",
      friendlyErrorMessage_(error),
      ui.ButtonSet.OK
    );
  }
}


/**
 * Switches to the user's OpenAI API key without deleting the Intelligex token.
 */
function switchToOwnKeyMode() {
  const ui = SpreadsheetApp.getUi();
  const properties = getUserProperties_();

  const apiKey = properties.getProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.OPENAI_API_KEY
  );

  if (!apiKey) {
    setOwnOpenAIKey();
    return;
  }

  properties.setProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.MODE,
    INTELLIGEX_GPT_CONFIG.MODES.OWN_KEY
  );

  ui.alert(
    "Mode changed",
    "GPT will now use your own OpenAI API key.",
    ui.ButtonSet.OK
  );
}


/**
 * Switches to the previously verified Intelligex access token.
 */
function switchToIntelligexMode() {
  const ui = SpreadsheetApp.getUi();
  const properties = getUserProperties_();

  const token = properties.getProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.ACCESS_TOKEN
  );

  if (!token) {
    ui.alert(
      "Verification required",
      "Register and verify your email before using Intelligex free credits.",
      ui.ButtonSet.OK
    );
    registerForIntelligexFreeCalls();
    return;
  }

  properties.setProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.MODE,
    INTELLIGEX_GPT_CONFIG.MODES.INTELLIGEX
  );

  ui.alert(
    "Mode changed",
    "GPT will now use your Intelligex free-call balance.",
    ui.ButtonSet.OK
  );
}


/**
 * Displays the current mode and, for Intelligex mode, retrieves live usage.
 */
function showIntelligexGPTStatus() {
  const ui = SpreadsheetApp.getUi();
  const properties = getUserProperties_();
  const mode = getConfiguredMode_();

  if (!mode) {
    ui.alert(
      "Not configured",
      "Select Intelligex GPT → Set up or change mode.",
      ui.ButtonSet.OK
    );
    return;
  }

  if (mode === INTELLIGEX_GPT_CONFIG.MODES.OWN_KEY) {
    const hasKey = Boolean(
      properties.getProperty(
        INTELLIGEX_GPT_CONFIG.PROPERTIES.OPENAI_API_KEY
      )
    );

    ui.alert(
      "GPT status",
      "Mode: Your OpenAI API key\n" +
        "API key stored: " +
        (hasKey ? "Yes" : "No") +
        "\nBilling and limits: Managed in your OpenAI account",
      ui.ButtonSet.OK
    );
    return;
  }

  try {
    const status = getIntelligexStatus_();

    ui.alert(
      "Intelligex GPT status",
      formatIntelligexStatus_(status),
      ui.ButtonSet.OK
    );
  } catch (error) {
    ui.alert(
      "Could not retrieve status",
      friendlyErrorMessage_(error),
      ui.ButtonSet.OK
    );
  }
}


/**
 * Deletes credentials and registration details stored for the current user.
 */
function clearIntelligexGPTSettings() {
  const ui = SpreadsheetApp.getUi();

  const answer = ui.alert(
    "Clear all GPT settings?",
    "This removes your stored OpenAI key, Intelligex access token, " +
      "email, name, and selected mode from this Apps Script project.",
    ui.ButtonSet.YES_NO
  );

  if (answer !== ui.Button.YES) {
    return;
  }

  getUserProperties_().deleteAllProperties();
  ui.alert(
    "Settings cleared",
    "Your locally stored GPT settings have been removed.",
    ui.ButtonSet.OK
  );
}


/* -------------------------------------------------------------------------- */
/* Google Sheets custom functions                                             */
/* -------------------------------------------------------------------------- */

/**
 * Sends a prompt to GPT.
 *
 * Examples:
 * =GPT("Explain workflow automation in one sentence")
 * =GPT(A2)
 * =GPT(A2, "Rewrite professionally. Return only the rewritten text.")
 * =GPT(A2, "Return valid HTML only.", "gpt-5-nano", 2000)
 * =GPT(A2, "", "", 1000, $Z$1)
 *
 * In own-key mode, the model argument selects the OpenAI model.
 * In Intelligex mode, the server controls the model and may ignore this value.
 *
 * Change the refresh argument to force a new request instead of reusing a
 * cached or deduplicated response.
 *
 * @param {string|Array<Array<*>>} prompt Prompt, cell, or range.
 * @param {string} instructions Optional instructions.
 * @param {string} model Optional model for own-key mode.
 * @param {number} maxOutputTokens Optional maximum output tokens.
 * @param {*} refresh Optional value used to force a new request.
 * @return {string} Generated text or an error message.
 * @customfunction
 */
function GPT(
  prompt,
  instructions,
  model,
  maxOutputTokens,
  refresh
) {
  try {
    const promptText = normalizeInput_(prompt).trim();

    if (!promptText) {
      return "";
    }

    const instructionText =
      normalizeInput_(instructions).trim() ||
      "Provide a clear and accurate answer. Return only the requested output without introductory commentary.";

    const selectedModel =
      normalizeInput_(model).trim() ||
      INTELLIGEX_GPT_CONFIG.DEFAULT_DIRECT_MODEL;

    const tokenLimit = normalizeTokenLimit_(maxOutputTokens);
    const refreshValue = normalizeInput_(refresh);
    const mode = getConfiguredMode_();

    if (!mode) {
      throw new Error(
        "GPT is not configured. Reload the spreadsheet and select " +
          "Intelligex GPT → Set up or change mode."
      );
    }

    const requestData = {
      prompt: promptText,
      instructions: instructionText,
      model:
        mode === INTELLIGEX_GPT_CONFIG.MODES.OWN_KEY
          ? selectedModel
          : "server-controlled",
      max_output_tokens: tokenLimit,
      refresh: refreshValue,
      mode: mode
    };

    const requestId = createRequestId_(requestData);
    const cacheKey = "IXGPT_" + requestId;
    const cache = CacheService.getUserCache();
    const cached = cache.get(cacheKey);

    if (cached !== null) {
      return cached;
    }

    let output;

    if (mode === INTELLIGEX_GPT_CONFIG.MODES.OWN_KEY) {
      output = callOpenAIDirectly_(
        promptText,
        instructionText,
        selectedModel,
        tokenLimit
      );
    } else if (
      mode === INTELLIGEX_GPT_CONFIG.MODES.INTELLIGEX
    ) {
      output = callIntelligexGenerate_(
        promptText,
        instructionText,
        tokenLimit,
        requestId
      );
    } else {
      throw new Error("The stored GPT mode is invalid.");
    }

    if (output.length < 90000) {
      cache.put(
        cacheKey,
        output,
        INTELLIGEX_GPT_CONFIG.CACHE_SECONDS
      );
    }

    return output;
  } catch (error) {
    return "GPT error: " + friendlyErrorMessage_(error);
  }
}


/**
 * Returns account information as a spillable table.
 *
 * Example:
 * =GPT_USAGE()
 *
 * @return {Array<Array<*>>} Current mode and usage details.
 * @customfunction
 */
function GPT_USAGE() {
  try {
    const mode = getConfiguredMode_();

    if (!mode) {
      return [
        ["Setting", "Value"],
        ["Status", "Not configured"],
        [
          "Action",
          "Use Intelligex GPT → Set up or change mode"
        ]
      ];
    }

    if (mode === INTELLIGEX_GPT_CONFIG.MODES.OWN_KEY) {
      return [
        ["Setting", "Value"],
        ["Mode", "Your OpenAI API key"],
        ["Intelligex call limit", "Not applicable"],
        ["Billing", "Your OpenAI account"]
      ];
    }

    const status = getIntelligexStatus_();

    return [
      ["Setting", "Value"],
      ["Mode", "Intelligex free credits"],
      ["Email", status.email || ""],
      ["Account status", status.status || ""],
      ["Calls used", numberOrBlank_(status.calls_used)],
      ["Calls remaining", numberOrBlank_(status.calls_remaining)],
      ["Calls limit", numberOrBlank_(status.calls_limit)]
    ];
  } catch (error) {
    return [
      ["Setting", "Value"],
      ["Error", friendlyErrorMessage_(error)]
    ];
  }
}


/* -------------------------------------------------------------------------- */
/* OpenAI direct mode                                                         */
/* -------------------------------------------------------------------------- */

/**
 * Calls the OpenAI Responses API with the user's own key.
 */
function callOpenAIDirectly_(
  prompt,
  instructions,
  model,
  maxOutputTokens
) {
  const apiKey = getUserProperties_().getProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.OPENAI_API_KEY
  );

  if (!apiKey) {
    throw new Error(
      "Your OpenAI API key is missing. Select " +
        "Intelligex GPT → Use my OpenAI API key."
    );
  }

  const payload = {
    model: model,
    instructions: instructions,
    input: prompt,
    max_output_tokens: maxOutputTokens,
    store: false
  };

  // Use low reasoning effort for GPT-5-family spreadsheet requests.
  if (/^gpt-5/i.test(model)) {
    payload.reasoning = {
      effort: "low"
    };
  }

  const result = requestJson_(
    INTELLIGEX_GPT_CONFIG.OPENAI_RESPONSES_URL,
    {
      method: "post",
      headers: {
        Authorization: "Bearer " + apiKey
      },
      payload: payload
    }
  );

  const output = extractOpenAIOutputText_(result);

  if (!output) {
    if (result.status === "incomplete") {
      throw new Error(
        "OpenAI returned an incomplete response: " +
          JSON.stringify(result.incomplete_details || {})
      );
    }

    throw new Error("OpenAI returned no text output.");
  }

  return output;
}


/**
 * Extracts generated text from a raw Responses API response.
 */
function extractOpenAIOutputText_(responseData) {
  if (
    typeof responseData.output_text === "string" &&
    responseData.output_text.trim()
  ) {
    return responseData.output_text.trim();
  }

  const textParts = [];
  const outputItems = Array.isArray(responseData.output)
    ? responseData.output
    : [];

  outputItems.forEach(function(item) {
    if (!item || !Array.isArray(item.content)) {
      return;
    }

    item.content.forEach(function(contentItem) {
      if (
        contentItem &&
        contentItem.type === "output_text" &&
        typeof contentItem.text === "string"
      ) {
        textParts.push(contentItem.text);
      }

      if (
        contentItem &&
        contentItem.type === "refusal" &&
        typeof contentItem.refusal === "string"
      ) {
        textParts.push(contentItem.refusal);
      }
    });
  });

  return textParts.join("\n").trim();
}


/* -------------------------------------------------------------------------- */
/* Intelligex free-credit mode                                                */
/* -------------------------------------------------------------------------- */

/**
 * Calls the private Intelligex GPT proxy.
 *
 * The Intelligex OpenAI key must exist only on the server.
 */
function callIntelligexGenerate_(
  prompt,
  instructions,
  maxOutputTokens,
  requestId
) {
  const accessToken = getUserProperties_().getProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.ACCESS_TOKEN
  );

  if (!accessToken) {
    throw new Error(
      "Your Intelligex access token is missing. Register and verify " +
        "your email from the Intelligex GPT menu."
    );
  }

  const result = requestJson_(
    INTELLIGEX_GPT_CONFIG.INTELLIGEX_API_BASE + "/generate",
    {
      method: "post",
      headers: {
        Authorization: "Bearer " + accessToken,
        "X-Intelligex-Client": "google-sheets",
        "X-Intelligex-Client-Version":
          INTELLIGEX_GPT_CONFIG.CLIENT_VERSION
      },
      payload: {
        prompt: prompt,
        instructions: instructions,
        max_output_tokens: maxOutputTokens,
        request_id: requestId,
        source: "google_sheets",
        client_version:
          INTELLIGEX_GPT_CONFIG.CLIENT_VERSION
      }
    }
  );

  if (
    typeof result.output !== "string" ||
    !result.output.trim()
  ) {
    throw new Error(
      "The Intelligex server returned no text output."
    );
  }

  return result.output.trim();
}


/**
 * Retrieves current Intelligex account status without consuming a GPT call.
 */
function getIntelligexStatus_() {
  const accessToken = getUserProperties_().getProperty(
    INTELLIGEX_GPT_CONFIG.PROPERTIES.ACCESS_TOKEN
  );

  if (!accessToken) {
    throw new Error(
      "No Intelligex access token is stored."
    );
  }

  return requestJson_(
    INTELLIGEX_GPT_CONFIG.INTELLIGEX_API_BASE + "/status",
    {
      method: "get",
      headers: {
        Authorization: "Bearer " + accessToken,
        "X-Intelligex-Client": "google-sheets",
        "X-Intelligex-Client-Version":
          INTELLIGEX_GPT_CONFIG.CLIENT_VERSION
      }
    }
  );
}


/**
 * Calls an unauthenticated registration or verification endpoint.
 */
function callIntelligexPublicEndpoint_(path, payload) {
  return requestJson_(
    INTELLIGEX_GPT_CONFIG.INTELLIGEX_API_BASE + path,
    {
      method: "post",
      headers: {
        "X-Intelligex-Client": "google-sheets",
        "X-Intelligex-Client-Version":
          INTELLIGEX_GPT_CONFIG.CLIENT_VERSION
      },
      payload: payload
    }
  );
}


/* -------------------------------------------------------------------------- */
/* HTTP, storage, validation, and formatting helpers                          */
/* -------------------------------------------------------------------------- */

/**
 * Performs an HTTP request and parses a JSON response.
 */
function requestJson_(url, options) {
  const fetchOptions = {
    method: String(options.method || "get").toLowerCase(),
    muteHttpExceptions: true,
    followRedirects: true,
    headers: Object.assign(
      {
        Accept: "application/json"
      },
      options.headers || {}
    )
  };

  if (
    typeof options.payload !== "undefined" &&
    options.payload !== null
  ) {
    fetchOptions.contentType = "application/json";
    fetchOptions.payload = JSON.stringify(options.payload);
  }

  let response;

  try {
    response = UrlFetchApp.fetch(url, fetchOptions);
  } catch (error) {
    throw new Error(
      "The server could not be reached. " +
        friendlyErrorMessage_(error)
    );
  }

  const statusCode = response.getResponseCode();
  const responseText = response.getContentText();
  let data = {};

  if (responseText) {
    try {
      data = JSON.parse(responseText);
    } catch (error) {
      throw new Error(
        "The server returned invalid JSON. HTTP " +
          statusCode +
          "."
      );
    }
  }

  if (statusCode < 200 || statusCode >= 300) {
    const message =
      data &&
      data.error &&
      typeof data.error.message === "string"
        ? data.error.message
        : data && typeof data.message === "string"
          ? data.message
          : "Request failed.";

    if (statusCode === 401 || statusCode === 403) {
      throw new Error(
        message +
          " Reverify your account or update your API key."
      );
    }

    if (statusCode === 402) {
      throw new Error(
        message || "Your free-call balance has been used."
      );
    }

    if (statusCode === 429) {
      throw new Error(
        message ||
          "Too many requests. Try again after the server rate limit resets."
      );
    }

    throw new Error(
      message + " HTTP " + statusCode + "."
    );
  }

  if (data && data.success === false) {
    throw new Error(
      data.message || "The request was not successful."
    );
  }

  return data;
}


/**
 * Returns properties private to the current Google user.
 */
function getUserProperties_() {
  return PropertiesService.getUserProperties();
}


/**
 * Returns the selected mode, or an empty string when not configured.
 */
function getConfiguredMode_() {
  return (
    getUserProperties_().getProperty(
      INTELLIGEX_GPT_CONFIG.PROPERTIES.MODE
    ) || ""
  );
}


/**
 * Prompts for required text and returns null when cancelled.
 */
function promptRequiredText_(title, message) {
  const ui = SpreadsheetApp.getUi();

  const response = ui.prompt(
    title,
    message,
    ui.ButtonSet.OK_CANCEL
  );

  if (response.getSelectedButton() !== ui.Button.OK) {
    return null;
  }

  const value = response.getResponseText().trim();

  if (!value) {
    ui.alert(
      "Required field",
      "This field cannot be empty.",
      ui.ButtonSet.OK
    );
    return null;
  }

  return value;
}


/**
 * Converts a cell, scalar value, or two-dimensional range into text.
 */
function normalizeInput_(value) {
  if (value === null || typeof value === "undefined") {
    return "";
  }

  if (Array.isArray(value)) {
    return value
      .map(function(row) {
        if (!Array.isArray(row)) {
          return normalizeInput_(row);
        }

        return row
          .map(function(cell) {
            return normalizeInput_(cell);
          })
          .join("\t");
      })
      .join("\n");
  }

  if (value instanceof Date) {
    return value.toISOString();
  }

  return String(value);
}


/**
 * Normalizes and caps the output token limit.
 */
function normalizeTokenLimit_(value) {
  const parsed = Number(value);

  if (!Number.isFinite(parsed) || parsed <= 0) {
    return INTELLIGEX_GPT_CONFIG.DEFAULT_MAX_OUTPUT_TOKENS;
  }

  return Math.max(
    16,
    Math.min(
      Math.floor(parsed),
      INTELLIGEX_GPT_CONFIG.MAX_DIRECT_OUTPUT_TOKENS
    )
  );
}


/**
 * Creates a deterministic request ID.
 *
 * Identical requests use the same ID, allowing the Intelligex server to avoid
 * charging twice for spreadsheet recalculation or network retries.
 * Change the GPT refresh argument when a genuinely new answer is required.
 */
function createRequestId_(requestData) {
  const digest = Utilities.computeDigest(
    Utilities.DigestAlgorithm.SHA_256,
    JSON.stringify(requestData),
    Utilities.Charset.UTF_8
  );

  return Utilities.base64EncodeWebSafe(digest)
    .replace(/=+$/g, "");
}


/**
 * Basic API-key shape validation. The OpenAI server performs final validation.
 */
function isPlausibleOpenAIKey_(value) {
  return /^sk-[A-Za-z0-9_-]{20,}$/.test(value);
}


/**
 * Basic email validation. The server must also validate and normalize it.
 */
function isValidEmail_(value) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}


/**
 * Converts unknown errors into safe user-facing text.
 */
function friendlyErrorMessage_(error) {
  if (!error) {
    return "An unknown error occurred.";
  }

  if (typeof error === "string") {
    return error;
  }

  if (
    typeof error.message === "string" &&
    error.message.trim()
  ) {
    return error.message.trim();
  }

  return String(error);
}


/**
 * Formats the status response for the spreadsheet menu.
 */
function formatIntelligexStatus_(status) {
  return [
    "Mode: Intelligex free credits",
    "Email: " + String(status.email || ""),
    "Status: " + String(status.status || ""),
    "Calls used: " + String(numberOrBlank_(status.calls_used)),
    "Calls remaining: " +
      String(numberOrBlank_(status.calls_remaining)),
    "Calls limit: " + String(numberOrBlank_(status.calls_limit))
  ].join("\n");
}


/**
 * Preserves zero while returning an empty string for missing values.
 */
function numberOrBlank_(value) {
  return typeof value === "number" ? value : "";
}

i

Subscribing unlocks the source code. Activating the 100-call
allowance is a separate in-Sheet email-verification step.

Formula examples

Start simple. Add structure when you need it.

The same formula can process a prompt, a single cell, a range, or
combined spreadsheet context.

Basic prompt
=GPT("Explain workflow automation in one sentence")
Rewrite a cell
=GPT(A2,"Rewrite professionally. Return only the result.")
Structured extraction
=GPT(A2,"Extract company, contact, email, and phone as JSON.")
Force a new answer
=GPT(A2,"","",1000,$Z$1)
Check free usage
=GPT_USAGE()

Example output
Complete
{
  "company": "Northstar Logistics",
  "contact": "Jamie Chen",
  "email": "[email protected]",
  "phone": "+1 416 555 0142"
}

Parameter Required Purpose
prompt Yes Text, cell, range, or combined formula content.
instructions No Task, format, tone, and output rules.
model No Used in own-key mode; ignored when the server controls the model.
max_output_tokens No Requested output limit. Free mode may apply a lower cap.
refresh No A changing value that forces a new request ID.

Privacy and practical limits

Use it like a productivity tool, not a vault.

The setup is designed to minimize unnecessary storage, but AI prompts
should still be treated as data leaving your spreadsheet.

Own-key mode

Requests go directly from Google Apps Script to OpenAI. Intelligex
does not receive the key, prompt, or response in this mode.

Free-credit mode

Requests pass through the Intelligex server for authentication,
usage tracking, rate limiting, and forwarding to the AI provider.

What is retained

Registration details, hashed identifiers, usage totals, request
IDs, errors, and temporarily cached output for duplicate protection.

!

Do not submit passwords, API keys, payment-card details, private health
information, regulated personal information, or confidential client
records through the free-credit mode.

Frequently asked questions

Everything users usually ask before installing.

Is this an official Google or OpenAI product?

No. It is an independent Intelligex tool built with Google Apps
Script and the OpenAI API.

Do I need an OpenAI API key?

Not for the initial 100 Intelligex calls. A key is required only for
the own-key mode.

Does ChatGPT Plus include API usage?

No. ChatGPT subscriptions and OpenAI API billing are separate.

Can I use cells and ranges?

Yes. The function accepts direct text, one cell, or a two-dimensional
range.

Why did the same answer return again?

Identical requests may be cached. Change the fifth refresh parameter
to request a new generation.

Can I use it across thousands of rows?

Thousands of live custom formulas are not recommended. Use smaller
batches or a dedicated queued workflow for high-volume processing.

What happens after 100 calls?

Free mode stops generating new responses. Switch to your own OpenAI
API key for continued use.

Can I use it in a shared spreadsheet?

Yes, but use it only with trusted editors because editors can inspect
or modify the bound Apps Script project.

Beyond a single formula

Turn repetitive spreadsheet work into a real AI workflow.

Intelligex designs secure integrations, document-processing systems,
approval flows, data pipelines, and AI automations around the tools your
team already uses.


Talk to Intelligex