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.
View live usage with =GPT_USAGE()
Free Intelligex calls
Free credits or your API key
Use =GPT() anywhere in Sheets
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.
Intelligex free credits
Verify your email and use the Intelligex server without exposing or
purchasing your own API key.
- No OpenAI API key required
- First name, last name, and email verification
- Server-controlled low-cost model
- Live usage status with =GPT_USAGE()
- Rate limits and abuse protection included
Your own OpenAI API key
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.
=GPT() formula
Access token
Usage validation
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.
/**
* 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 : "";
}
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.
=GPT("Explain workflow automation in one sentence")
=GPT(A2,"Rewrite professionally. Return only the result.")
=GPT(A2,"Extract company, contact, email, and phone as JSON.")
=GPT(A2,"","",1000,$Z$1)
=GPT_USAGE()
{
"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.

