1. Introducción
Google Workspace Studio te permite automatizar procesos empresariales en las aplicaciones de Google Workspace, como Gmail, Google Chat, Google Drive y Hojas de cálculo de Google. Cuando tus flujos de trabajo necesiten iniciarse desde una aplicación web externa o ejecutar lógica personalizada en tu propio entorno de nube, puedes extender Google Workspace Studio con un complemento de Google Workspace HTTP en Node.js en Cloud Run:
- Paso de inicio personalizado (
workflowTrigger): Inicia un flujo de Google Workspace Studio cuando ocurre un evento externo, como un cliente que envía un ticket en un portal web interno, llamando a la API de REST detriggers.firede Google Workspace Studio. - Paso de acción personalizada (
workflowAction): Recibe variables de pasos anteriores del flujo, ejecuta lógica personalizada o inferencia de IA en tu servicio de Node.js y devuelve variables de salida a Google Workspace Studio.
En este codelab, compilarás e implementarás una extensión HTTP completa de Node.js para Google Workspace Studio en Cloud Run, usarás Cloud Firestore para conservar las suscripciones activas a los activadores de flujo y los tickets enviados en las revisiones de contenedores, y clasificarás la urgencia de los tickets en tu paso de acción personalizado con Gemini 3.8 Flash (gemini-3.8-flash) en Gemini Enterprise Agent Platform con una alternativa resistente de coincidencia de palabras clave.
Qué compilarás
- Una app web de Support Quick Portal y un complemento de Google Workspace HTTP compilados con Node.js, Express y Firestore en Cloud Run.
- Un paso de activador personalizado New Support Ticket (
supportTicketTrigger) que conserva las suscripciones activas al flujo en una colección destudio_triggersFirestore, registra los tickets de asistencia enviados en una colección desupport_ticketsFirestore y se activa cada vez que se envía un ticket de asistencia en el portal web, pasandoticketTitleyticketDescriptiona un flujo de Google Workspace Studio. - Un paso de acción personalizada Detect Urgency (
urgencyDetectorStep) que clasifica la urgencia del ticket con Gemini 3.8 Flash (gemini-3.8-flash) en Gemini Enterprise Agent Platform (aiplatform.googleapis.com), con una opción automática de respaldo para la coincidencia de palabras clave (urgent,blocker,critical).
Qué aprenderás
- Cómo configurar un manifiesto de implementación de complemento de Google Workspace HTTP (
deployment.json) con elementosworkflowTriggeryworkflowAction - Cómo conservar las suscripciones a activadores de Google Workspace Studio (
triggerId,notifyUriyuserOAuthToken) en una colección destudio_triggersFirestore y activar flujos con la API de REST detriggers.fire - Cómo crear un paso personalizado de
workflowActionen Node.js con la salida estructurada de Gemini 3.8 Flash (gemini-3.8-flash) en Gemini Enterprise Agent Platform con una palabra clave de resguardo
Requisitos previos
- Una cuenta de Google Workspace con acceso a Google Workspace Studio
- Un proyecto de Google Cloud con la facturación habilitada, creado dentro de la misma organización de Google Workspace que tu cuenta de usuario para que puedas instalar implementaciones de complementos no publicadas para realizar pruebas.
2. Configura tu proyecto de Google Cloud y Firestore
En este paso, configurarás la pantalla de consentimiento de OAuth de tu proyecto, habilitarás las APIs de Google Cloud y Google Workspace necesarias en Cloud Shell, aprovisionarás tu base de datos de (default) Firestore y, luego, inicializarás tu cuenta de servicio de autorización del complemento.
- Abre la consola de Google Cloud, selecciona tu proyecto de Google Cloud y haz clic en el ícono Activar Cloud Shell en la barra de navegación superior.
- Configura la pantalla de consentimiento de OAuth para que Google Workspace pueda aprovisionar un cliente de OAuth para tu complemento:
- Abre la página de desarrollo de la marca de Google Auth Platform.
- Si se te solicita, haz clic en Comenzar. (Si tu pantalla de consentimiento ya está configurada, ve al paso 3).
- En Información de la app, ingresa
Support Extensionen el Nombre de la app, selecciona tu Correo electrónico de asistencia al usuario y haz clic en Siguiente. - En Público, selecciona Interno y haz clic en Siguiente.
- En Información de contacto, ingresa tu dirección de correo electrónico y haz clic en Siguiente.
- En Finalizar, acepta la Política de Datos del Usuario de los Servicios de las APIs de Google, haz clic en Continuar y, luego, en Crear.
- En Cloud Shell, establece tu proyecto activo (reemplaza
YOUR_PROJECT_IDpor tu ID del proyecto de Google Cloud) y habilita las APIs necesarias:gcloud config set project YOUR_PROJECT_ID gcloud services enable \ aiplatform.googleapis.com \ firestore.googleapis.com \ run.googleapis.com \ cloudbuild.googleapis.com \ artifactregistry.googleapis.com \ appsmarket-component.googleapis.com \ gsuiteaddons.googleapis.com \ workspacestudio.googleapis.com - Aprovisiona tu base de datos de
(default)Firestore enus-central1(si aún no existe) y, luego, inicializa tu cuenta de servicio de complementos de Google Workspace:gcloud firestore databases describe --database="(default)" >/dev/null 2>&1 || \ gcloud firestore databases create \ --database="(default)" \ --location=us-central1 \ --type=firestore-native \ --quiet gcloud workspace-add-ons get-authorization
3. Compila la app de Node.js con Firestore y Gemini 3.8 Flash
En este paso, compilarás la aplicación de Node.js en ~/studio-support-addon. Para que el código sea modular y legible, organizarás el proyecto en cinco archivos enfocados:
firestore.js: Inicializa el cliente de Cloud Firestore (coleccionesstudio_triggersysupport_tickets) y actualiza los tokens de OAuth almacenados en caché.starter.js: Implementa los webhooks del paso de inicio personalizado Nuevo ticket de asistencia (/onConfigTriggery/onManageTrigger).action.js: Implementa los webhooks del paso de acción personalizada Detect Urgency (/onConfigUrgencyy/onExecuteUrgency) con Gemini 3.8 Flash (gemini-3.8-flash) con una alternativa de coincidencia de palabras clave.portal.js: Publica la IU web del Portal de asistencia rápida (GET /) y activa los activadores de Google Workspace Studio (POST /api/submit-ticket).index.js: Conecta todas las rutas de Express y, luego, inicia el servidor HTTP.
Inicializa el proyecto y package.json
- Crea un directorio de proyecto y navega a él:
mkdir -p ~/studio-support-addon && cd ~/studio-support-addon - Crea
package.jsonconexpress,@google-cloud/firestorey@google/genai:cat << 'EOF' > package.json { "name": "studio-support-addon", "version": "1.0.0", "type": "module", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "@google-cloud/firestore": "^7.11.0", "@google/genai": "^1.0.0", "express": "^4.21.2" } } EOF
firestore.js: Estado compartido de Firestore y actualización del token de OAuth
Crea firestore.js para administrar tus dos colecciones de Firestore y mantener actualizados los tokens de OAuth del usuario almacenados en caché:
studio_triggers: Almacena suscripciones activas a flujos de Google Workspace Studio (triggerId,notifyUriyuserOAuthToken) para que sobrevivan a los reinicios de contenedores de Cloud Run y a los eventos de reducción de escala a cero.support_tickets: Almacena un registro de cada ticket de asistencia que se envía a través del portal.refreshTokenInFirestore(req): Cada vez que Google Workspace Studio llama a cualquiera de tus extremos de webhook, incluye unauthorizationEventObject.userOAuthTokende corta duración. Este asistente actualiza los documentos de activación activos con el token más reciente para que las llamadas detriggers.fireen segundo plano sigan autorizadas.
cat << 'EOF' > firestore.js
import { Firestore } from '@google-cloud/firestore';
export const db = new Firestore({
projectId: process.env.GOOGLE_CLOUD_PROJECT || undefined,
databaseId: '(default)',
ignoreUndefinedProperties: true,
});
export const triggersCollection = db.collection(
process.env.FIRESTORE_TRIGGERS_COLLECTION || 'studio_triggers'
);
export const ticketsCollection = db.collection(
process.env.FIRESTORE_TICKETS_COLLECTION || 'support_tickets'
);
// Keep cached userOAuthToken fresh across active trigger documents
export async function refreshTokenInFirestore(req) {
const freshToken = req.body?.authorizationEventObject?.userOAuthToken;
if (!freshToken) return;
try {
const snapshot = await triggersCollection.get();
if (snapshot.empty) return;
const batch = db.batch();
snapshot.docs.forEach((doc) => {
batch.set(
doc.ref,
{ userOAuthToken: freshToken, updatedAt: new Date().toISOString() },
{ merge: true }
);
});
await batch.commit();
} catch (err) {
console.error('Error refreshing OAuth token in Firestore:', err.message);
}
}
EOF
starter.js: Paso de activador personalizado (workflowTrigger)
Crea starter.js para implementar los dos extremos de webhook que requiere un paso inicial personalizado de Google Workspace Studio:
onConfigTrigger(POST /onConfigTrigger): Se llama cuando un usuario selecciona New Support Ticket como el iniciador de su flujo en Google Workspace Studio. Devuelve una acción de navegaciónpushCardque describe el iniciador.onManageTrigger(POST /onManageTrigger): Se llama cuando un usuario activa (req.body.workflow.triggerCreation) o desactiva / borra (req.body.workflow.triggerDeletion) un flujo. Guarda o borra el documento de suscripción en la colecciónstudio_triggersde Firestore.
cat << 'EOF' > starter.js
import { triggersCollection, refreshTokenInFirestore } from './firestore.js';
// Custom Starter Configuration Card
export async function onConfigTrigger(req, res) {
await refreshTokenInFirestore(req);
res.status(200).json({
action: {
navigations: [
{
pushCard: {
sections: [
{
header: 'Support Ticket Listener',
widgets: [
{
textParagraph: {
text: 'This starter listens for new tickets submitted through the Support Quick Portal and stores subscriptions in Firestore.',
},
},
],
},
],
},
},
],
},
});
}
// Custom Starter Subscription Lifecycle (Firestore studio_triggers collection)
export async function onManageTrigger(req, res) {
try {
const triggerCreation = req.body?.workflow?.triggerCreation;
const triggerDeletion = req.body?.workflow?.triggerDeletion;
const userOAuthToken =
req.body?.authorizationEventObject?.userOAuthToken || null;
if (triggerCreation?.triggerId) {
const triggerId = String(triggerCreation.triggerId);
const docId = encodeURIComponent(triggerId);
await triggersCollection.doc(docId).set(
{
triggerId,
notifyUri: triggerCreation.notifyUri || null,
...(userOAuthToken ? { userOAuthToken } : {}),
updatedAt: new Date().toISOString(),
},
{ merge: true }
);
}
if (triggerDeletion?.triggerId) {
const triggerId = String(triggerDeletion.triggerId);
const docId = encodeURIComponent(triggerId);
await triggersCollection.doc(docId).delete();
}
if (!triggerCreation && !triggerDeletion && userOAuthToken) {
await refreshTokenInFirestore(req);
}
} catch (err) {
console.error('Error in /onManageTrigger:', err.message);
}
res.status(200).json({});
}
EOF
action.js: Paso de acción personalizado (workflowAction) con Gemini 3.8 Flash
Crea action.js para implementar los dos extremos de webhook que requiere un paso de acción personalizada de Google Workspace Studio:
onConfigUrgency(POST /onConfigUrgency): Muestra una tarjeta de configuración con un widgettextInputque establecehostAppDataSource.workflowDataSource.includeVariables: true, lo que permite a los usuarios seleccionar la variableticketDescriptionque genera el paso inicial.onExecuteUrgency(POST /onExecuteUrgency): Lee la entradaticketDescriptiondereq.body.workflow.actionInvocation.inputs, clasifica su impacto y urgencia en el negocio con Gemini 3.8 Flash (gemini-3.8-flash) en Gemini Enterprise Agent Platform con salida estructurada (enumresponseSchema["High", "Normal"]), recurre adetectKeywordUrgency()(urgent,blocker,critical) si falla la llamada al modelo y devuelve la variable de salidaurgencyLevelconhostAppAction.workflowAction.returnOutputVariablesAction.
cat << 'EOF' > action.js
import { GoogleGenAI, Type } from '@google/genai';
import { refreshTokenInFirestore } from './firestore.js';
// Initialize Google GenAI SDK for the Gemini Enterprise Agent Platform
const ai = new GoogleGenAI({
vertexai: true,
project: process.env.GOOGLE_CLOUD_PROJECT,
location: 'global',
});
// Fallback keyword classifier if the Gemini API call is unavailable
export function detectKeywordUrgency(description) {
const lower = (description || '').toLowerCase();
return lower.includes('urgent') ||
lower.includes('blocker') ||
lower.includes('critical')
? 'High'
: 'Normal';
}
// Custom Action Step Configuration Card
export async function onConfigUrgency(req, res) {
await refreshTokenInFirestore(req);
res.status(200).json({
action: {
navigations: [
{
pushCard: {
sections: [
{
header: 'Urgency Detector Step',
widgets: [
{
textInput: {
name: 'ticketDescription',
label: 'Source Description',
hintText:
'Select the description variable outputted by the starter',
hostAppDataSource: {
workflowDataSource: {
includeVariables: true,
},
},
},
},
],
},
],
},
},
],
},
});
}
// Custom Action Step Execution: Gemini 3.8 Flash with keyword fallback
export async function onExecuteUrgency(req, res) {
await refreshTokenInFirestore(req);
const inputs = req.body?.workflow?.actionInvocation?.inputs || {};
const description = inputs.ticketDescription?.stringValues?.[0] || '';
let urgencyLevel = detectKeywordUrgency(description);
try {
const response = await ai.models.generateContent({
model: 'gemini-3.8-flash',
contents: `Evaluate the business impact and time sensitivity of this support ticket description and classify its urgency:\n\n${description}`,
config: {
responseMimeType: 'text/x.enum',
responseSchema: {
type: Type.STRING,
enum: ['High', 'Normal'],
},
},
});
const candidate = (response.text || '').trim();
if (candidate === 'High' || candidate === 'Normal') {
urgencyLevel = candidate;
}
} catch (err) {
console.error('Gemini fallback to keyword matching:', err.message);
}
res.status(200).json({
hostAppAction: {
workflowAction: {
returnOutputVariablesAction: {
variables: {
urgencyLevel: {
stringValues: [urgencyLevel],
},
},
},
},
},
});
}
EOF
portal.js: Admite la IU de Quick Portal y el dispatcher de triggers.fire
Crea portal.js para publicar el portal web y enviar las solicitudes de tickets a Google Workspace Studio:
renderPortal(GET /): Consulta la colecciónstudio_triggersde Firestore para mostrar cuántos activadores activos de Google Workspace Studio están registrados y publica el formulario de envío de tickets en HTML.submitTicket(POST /api/submit-ticket): Itera todos los documentos de activación activos enstudio_triggers, invoca el extremotriggers.firede Google Workspace Studio (https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire) con las variables de salidaticketTitleyticketDescription, y unrequestIdúnico, y guarda el ticket en la colecciónsupport_ticketsde Firestore.
cat << 'EOF' > portal.js
import { triggersCollection, ticketsCollection } from './firestore.js';
// Support Quick Portal UI
export async function renderPortal(req, res) {
let activeCount = 0;
try {
const snapshot = await triggersCollection.get();
activeCount = snapshot.size;
} catch (err) {
console.error('Error reading studio_triggers collection:', err.message);
}
res.status(200).send(`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Support Quick Portal</title>
<style>
body { font-family: Arial, sans-serif; padding: 24px; max-width: 540px; margin: auto; color: #202124; }
.badge { background: #e8f0fe; color: #1967d2; padding: 8px 12px; border-radius: 6px; font-size: 14px; margin-bottom: 16px; }
label { font-weight: bold; display: block; margin-top: 16px; }
input, textarea { width: 100%; margin-top: 6px; padding: 10px; box-sizing: border-box; border: 1px solid #dadce0; border-radius: 4px; }
button { background-color: #1a73e8; color: white; border: none; padding: 10px 18px; margin-top: 16px; font-weight: bold; cursor: pointer; border-radius: 4px; }
button:hover { background-color: #1557b0; }
#status { margin-top: 16px; font-weight: bold; }
</style>
</head>
<body>
<h2>Support Quick Portal</h2>
<div class="badge">
Active Google Workspace Studio triggers in Firestore (<code>studio_triggers</code>):
<strong id="trigger-count">${activeCount}</strong>
</div>
<p>Submit a support ticket below to fire your Google Workspace Studio flow.</p>
<label for="title">Ticket Title</label>
<input type="text" id="title" placeholder="e.g. Database failure">
<label for="description">Issue Description</label>
<textarea id="description" rows="4" placeholder="Describe the issue..."></textarea>
<button onclick="submitTicket()">Submit Ticket</button>
<div id="status"></div>
<script>
async function submitTicket() {
const title = document.getElementById('title').value.trim();
const description = document.getElementById('description').value.trim();
const statusEl = document.getElementById('status');
if (!title || !description) {
statusEl.style.color = '#d93025';
statusEl.textContent = 'Please fill out both fields.';
return;
}
statusEl.style.color = '#188038';
statusEl.textContent = 'Submitting ticket...';
try {
const res = await fetch('/api/submit-ticket', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, description }),
});
const data = await res.json();
if (data.ok) {
statusEl.textContent =
'Ticket saved to Firestore! Fired ' + data.triggersFired + ' Google Workspace Studio trigger(s).';
} else {
statusEl.style.color = '#d93025';
statusEl.textContent = 'Error: ' + (data.error || 'Unknown error');
}
} catch (err) {
statusEl.style.color = '#d93025';
statusEl.textContent = 'Request failed: ' + err.message;
}
}
</script>
</body>
</html>`);
}
// Portal Form Submission -> Fire Active Google Workspace Studio Triggers
export async function submitTicket(req, res) {
const title = req.body?.title || 'Untitled Ticket';
const description = req.body?.description || '';
const results = [];
try {
const snapshot = await triggersCollection.get();
for (const doc of snapshot.docs) {
const trigger = doc.data();
if (!trigger.triggerId || !trigger.userOAuthToken) continue;
const response = await fetch(
`https://workspacestudio.googleapis.com/v1/triggers/${encodeURIComponent(trigger.triggerId)}:fire`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${trigger.userOAuthToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'triggers/' + trigger.triggerId,
outputs: {
ticketTitle: { stringValues: [title] },
ticketDescription: { stringValues: [description] },
},
requestId:
'req_' + Date.now() + '_' + Math.floor(Math.random() * 1000),
}),
}
);
results.push({ triggerId: trigger.triggerId, status: response.status });
}
await ticketsCollection.add({
title,
description,
triggersFired: results.length,
createdAt: new Date().toISOString(),
});
res.status(200).json({ ok: true, triggersFired: results.length, results });
} catch (err) {
console.error('Error in /api/submit-ticket:', err.message);
res.status(500).json({ ok: false, error: err.message });
}
}
EOF
index.js: Punto de entrada del servidor de Express
Crea index.js para conectar las rutas de tu portal y los endpoints de webhook de Google Workspace Studio en un servidor HTTP de Express que escucha en PORT (8080 predeterminado en Cloud Run):
cat << 'EOF' > index.js
import express from 'express';
import { onConfigTrigger, onManageTrigger } from './starter.js';
import { onConfigUrgency, onExecuteUrgency } from './action.js';
import { renderPortal, submitTicket } from './portal.js';
const app = express();
app.use(express.json());
// Support Quick Portal routes
app.get('/', renderPortal);
app.post('/api/submit-ticket', submitTicket);
// Google Workspace Studio Custom Starter Step webhooks
app.post('/onConfigTrigger', onConfigTrigger);
app.post('/onManageTrigger', onManageTrigger);
// Google Workspace Studio Custom Action Step webhooks
app.post('/onConfigUrgency', onConfigUrgency);
app.post('/onExecuteUrgency', onExecuteUrgency);
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Support Extension listening on port ${PORT}`);
});
EOF
4. Implementa en Cloud Run y, luego, instala el complemento
En este paso, implementarás el servicio de Node.js en Cloud Run, otorgarás permisos de IAM para el invocador de complementos de Google Workspace y el acceso al tiempo de ejecución de Firestore y Agent Platform, generarás el manifiesto deployment.json y, luego, instalarás la implementación del complemento.
- Implementa el servicio
support-extensionen Cloud Run con las variables de entorno de tu proyecto y la colección de Firestore:gcloud run deploy support-extension \ --source . \ --region us-central1 \ --allow-unauthenticated \ --set-env-vars="GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project),FIRESTORE_TRIGGERS_COLLECTION=studio_triggers,FIRESTORE_TICKETS_COLLECTION=support_tickets" \ --quiet - Otorga
roles/run.invokeren el servicio de Cloud Run a la cuenta de servicio de los complementos de Google Workspace para que Google Workspace Studio pueda llamar a tus extremos de webhook:ADDON_SA=$(gcloud workspace-add-ons get-authorization --format="value(serviceAccountEmail)") gcloud run services add-iam-policy-binding support-extension \ --region=us-central1 \ --member="serviceAccount:${ADDON_SA}" \ --role="roles/run.invoker" \ --quiet - Otorga
roles/datastore.user(para leer y escribir las coleccionesstudio_triggersysupport_ticketsde Firestore) yroles/aiplatform.user(para llamar a Gemini en Gemini Enterprise Agent Platform) a la cuenta de servicio del entorno de ejecución de Cloud Run:PROJECT_ID=$(gcloud config get-value project) PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)") RUNTIME_SA=$(gcloud run services describe support-extension --region=us-central1 --format="value(spec.template.spec.serviceAccountName)") RUNTIME_SA=${RUNTIME_SA:-"${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"} gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member="serviceAccount:${RUNTIME_SA}" \ --role="roles/datastore.user" \ --condition=None \ --quiet gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member="serviceAccount:${RUNTIME_SA}" \ --role="roles/aiplatform.user" \ --condition=None \ --quiet - Recupera la URL de Cloud Run implementada y genera el manifiesto
deployment.jsonque define los elementossupportTicketTrigger(workflowTrigger) yurgencyDetectorStep(workflowAction):SERVICE_URL=$(gcloud run services describe support-extension --region=us-central1 --format="value(status.url)") cat << EOF > deployment.json { "oauthScopes": [ "https://www.googleapis.com/auth/workspace.studio.trigger", "https://www.googleapis.com/auth/userinfo.email" ], "addOns": { "common": { "name": "Support Extension", "logoUrl": "https://www.gstatic.com/images/branding/productlogos/buganizer/v1/192px.svg" }, "studio": { "flows": { "workflowElements": [ { "id": "supportTicketTrigger", "state": "ACTIVE", "name": "New Support Ticket", "description": "Fires when someone submits a ticket in the Support Quick Portal.", "workflowTrigger": { "inputs": [], "outputs": [ { "id": "ticketTitle", "description": "Ticket Title", "cardinality": "SINGLE", "dataType": { "basicType": "STRING" } }, { "id": "ticketDescription", "description": "Ticket Description", "cardinality": "SINGLE", "dataType": { "basicType": "STRING" } } ], "onConfigFunction": "${SERVICE_URL}/onConfigTrigger", "onManageFunction": "${SERVICE_URL}/onManageTrigger" } }, { "id": "urgencyDetectorStep", "state": "ACTIVE", "name": "Detect Urgency", "description": "Evaluates the ticket description to detect High or Normal urgency.", "workflowAction": { "inputs": [ { "id": "ticketDescription", "description": "Description to scan", "cardinality": "SINGLE", "dataType": { "basicType": "STRING" } } ], "outputs": [ { "id": "urgencyLevel", "description": "Detected Urgency (High / Normal)", "cardinality": "SINGLE", "dataType": { "basicType": "STRING" } } ], "onConfigFunction": "${SERVICE_URL}/onConfigUrgency", "onExecuteFunction": "${SERVICE_URL}/onExecuteUrgency" } } ] } }, "httpOptions": { "authorizationHeader": "SYSTEM_ID_TOKEN" } } } EOF - Crea (o reemplaza) e instala la implementación del complemento de Google Workspace para tu cuenta, y luego imprime la URL del Portal de asistencia rápida:
gcloud workspace-add-ons deployments create support-extension --deployment-file=deployment.json || \ gcloud workspace-add-ons deployments replace support-extension --deployment-file=deployment.json gcloud workspace-add-ons deployments install support-extension echo "Support Quick Portal URL: ${SERVICE_URL}"
5. Prueba tus pasos personalizados en Google Workspace Studio
- Abre Google Workspace Studio (actualiza la pestaña del navegador si ya estaba abierta) y haz clic en New Flow.
- Haz clic en Select a starter > Support Extension > New Support Ticket.
- Haz clic en Agregar paso (
+) > Extensión de asistencia > Detectar urgencia. En el campo Descripción de la fuente, haz clic en + Variables y selecciona Descripción del ticket. - Haz clic en Agregar paso (
+) y agrega un paso de Chat > Notificarme en Chat que incluya las variables Título del ticket, Nivel de urgencia y Descripción del ticket en el mensaje. - Activa el flujo On en la esquina superior derecha. Google Workspace Studio llama de inmediato a tu extremo
/onManageTriggeren Cloud Run, que escribe la suscripción activa al activador en la colecciónstudio_triggersen Firestore. - Abre la URL del portal de asistencia rápida (
https://support-extension-...run.app) en una pestaña nueva, confirma que el portal muestre el activador activo1en Firestore y envía dos tickets para probar la clasificación de Gemini 3.8 Flash (gemini-3.8-flash):- Boleto de alto impacto (Título del boleto:
London office lockout, Descripción del problema:All 500 employees in the London office are locked out of their workstations 20 minutes before the board presentation.): Incluso sin palabras clave codificadas, comourgentoblocker,gemini-3.8-flashclasifica este boleto como de urgenciaHigh. - Ticket de rutina (Título del ticket:
Conference room adapters, Descripción del problema:Could we order two extra HDMI adapters for the third-floor conference room next month?):gemini-3.8-flashclasifica este ticket como de urgenciaNormal.
- Boleto de alto impacto (Título del boleto:
- Verifica en Google Chat y en la pestaña Actividad de Google Workspace Studio que tu flujo se haya ejecutado para ambos tickets con
Urgency Level: HighyUrgency Level: Normal.
6. Limpia
Para evitar que se apliquen cargos a tu cuenta de Google Cloud, quita los recursos implementados en Cloud Shell:
gcloud workspace-add-ons deployments uninstall support-extension
gcloud workspace-add-ons deployments delete support-extension --quiet
gcloud run services delete support-extension --region=us-central1 --quiet
gcloud firestore databases delete --database="(default)" --quiet
7. ¡Felicitaciones!
Compilaste, implementaste y probaste una extensión HTTP de Node.js para Google Workspace Studio con Firestore, Cloud Run y Gemini 3.8 Flash en Gemini Enterprise Agent Platform.
Temas abordados
- Configuración de un manifiesto de complemento HTTP de Google Workspace Studio (
deployment.json) con elementosworkflowTriggeryworkflowAction. - Persiste las suscripciones de Google Workspace Studio
workflowTriggeren la colecciónstudio_triggersde Firestore y registra los envíos del portal ensupport_ticketspara que los activadores sobrevivan al ajuste de escala y a las reimplementaciones de Cloud Run. - Compila un paso de
workflowActionque clasifica los tickets de asistencia al cliente con un resultado estructurado de Gemini 3.8 Flash (gemini-3.8-flash) en Gemini Enterprise Agent Platform, con una alternativa de coincidencia de palabras clave resistente.