Sincroniza tus tickets con una hoja de Google

Una hoja que trae tus casos de CALADESK y se actualiza sola. Es la primera integración que pide cualquier cliente, y la puedes montar tú, entera: veinte minutos, sin instalar nada ni escribir un programa desde cero.

Antes de empezar

  • Tu usuario de administrador en CALADESK.
  • Una cuenta de Google, para crear la hoja.
  • No hace falta saber programar: el código se copia y se pega tal cual, no hay nada que adaptar.

Así quedará la hoja

# Asunto Estado Prioridad Cliente Creado
43Verificación de sonidonuevomediaCarlos Luque11/09/2026
42Hola botnuevomedia10/09/2026
41Prueba de correonuevomediaCarlos09/09/2026

1. Emite tu credencial en CALADESK

Entra como administrador y ve a Configuración → Acceso por API. Escribe un nombre que te diga para qué es —por ejemplo Hoja de Google— y pulsa Crear.

Cópiala ahora. Se muestra una sola vez: se guarda cifrada, así que ni desde el servidor se puede volver a leer. Si la pierdes no pasa nada grave — emites otra y borras esta — pero tendrás que repetir el paso 4.

Para esta integración no enciendas el permiso de escritura: la hoja solo va a leer. Una credencial que solo lee es una credencial que, si se filtra, no puede abrirte casos falsos.

2. Crea la hoja

Ve a sheets.new y ponle nombre, por ejemplo Tickets CALADESK. No hace falta crear columnas ni nada: el código las escribe solo.

3. Pega el código

En la hoja: menú Extensiones → Apps Script. Se abre una pestaña nueva con un archivo Código.gs que trae unas líneas de ejemplo. Bórralo todo y pega esto en su lugar. Después pulsa el icono de guardar y ponle un nombre al proyecto.

/**
 * CALADESK → Google Sheets
 *
 * Trae los tickets de tu empresa y los escribe en la hoja «Tickets».
 * Solo lee: no crea ni modifica nada en CALADESK.
 */

const API = 'https://caladesk.com/api/v1/tickets/';
const HOJA = 'Tickets';
const POR_PAGINA = 200;  // el máximo que admite la API

// Qué columna de la hoja sale de qué campo de CALADESK.
// Para quitar una columna, borra su línea. Para cambiarle el título,
// cambia el texto de la derecha.
const COLUMNAS = [
  ['id',                '#'],
  ['asunto',            'Asunto'],
  ['estado',            'Estado'],
  ['prioridad',         'Prioridad'],
  ['cliente_nombre',    'Cliente'],
  ['cliente_email',     'Correo'],
  ['area',              'Área'],
  ['categoria',         'Categoría'],
  ['agente',            'Agente'],
  ['creado',            'Creado'],
  ['ultima_actividad',  'Última actividad'],
  ['limite_resolucion', 'Vence'],
  ['resuelto_en',       'Resuelto'],
];


/** El menú «CALADESK» que aparece al abrir la hoja. */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('CALADESK')
    .addItem('Actualizar ahora', 'actualizar')
    .addSeparator()
    .addItem('Guardar mi credencial…', 'guardarCredencial')
    .addItem('Actualizar sola cada hora', 'activarAutomatico')
    .addItem('Dejar de actualizar sola', 'desactivarAutomatico')
    .addToUi();
}


/**
 * Guarda la credencial FUERA del código.
 *
 * Esto no es un adorno: si la pegaras arriba, cualquiera con quien compartas
 * la hoja podría abrir Apps Script y leerla — y con ella leer TODOS tus
 * tickets. Guardada aquí, viaja con el proyecto pero no se ve al compartir.
 */
function guardarCredencial() {
  const ui = SpreadsheetApp.getUi();
  const r = ui.prompt('Credencial de CALADESK',
    'Pega la credencial que copiaste (empieza por cal_):',
    ui.ButtonSet.OK_CANCEL);
  if (r.getSelectedButton() !== ui.Button.OK) return;

  const valor = r.getResponseText().trim();
  if (valor.indexOf('cal_') !== 0) {
    ui.alert('Eso no parece una credencial de CALADESK: empiezan por «cal_».');
    return;
  }
  PropertiesService.getScriptProperties().setProperty('CALADESK_TOKEN', valor);
  ui.alert('Guardada. Ya puedes usar «Actualizar ahora».');
}


function credencial_() {
  const t = PropertiesService.getScriptProperties().getProperty('CALADESK_TOKEN');
  if (!t) {
    throw new Error(
      'Todavía no has guardado la credencial. ' +
      'Menú CALADESK → «Guardar mi credencial…».');
  }
  return t;
}


/**
 * Pide UNA página de tickets.
 *
 * `muteHttpExceptions` es lo que permite leer el error en vez de que el
 * script reviente con un mensaje que no dice nada. Cada código de CALADESK
 * significa una cosa distinta, y cada uno se arregla de otra forma.
 */
function pedirPagina_(pagina) {
  const url = API + '?por_pagina=' + POR_PAGINA + '&pagina=' + pagina;
  const respuesta = UrlFetchApp.fetch(url, {
    method: 'get',
    headers: { Authorization: 'Bearer ' + credencial_() },
    muteHttpExceptions: true,
  });

  const codigo = respuesta.getResponseCode();
  if (codigo === 401) {
    throw new Error('CALADESK no reconoce la credencial. ' +
      '¿La copiaste entera? ¿Sigue activa en Configuración → Acceso por API?');
  }
  if (codigo === 403) {
    throw new Error('El plan de tu empresa no tiene la API habilitada.');
  }
  if (codigo === 429) {
    throw new Error('Demasiadas peticiones seguidas. Espera un minuto.');
  }
  if (codigo !== 200) {
    throw new Error('CALADESK respondió ' + codigo + ': ' +
      respuesta.getContentText().slice(0, 300));
  }
  return JSON.parse(respuesta.getContentText());
}


/** Trae TODOS los tickets y los escribe en la hoja. */
function actualizar() {
  const libro = SpreadsheetApp.getActiveSpreadsheet();
  let hoja = libro.getSheetByName(HOJA);
  if (!hoja) hoja = libro.insertSheet(HOJA);

  // Se recorren las páginas hasta la última: la API nunca devuelve la tabla
  // entera de golpe, y quedarse en la primera es el error clásico — funciona
  // de maravilla hasta que la empresa pasa de 200 tickets.
  const filas = [];
  let pagina = 1;
  let paginas = 1;
  do {
    const datos = pedirPagina_(pagina);
    paginas = datos.paginas || 1;
    datos.resultados.forEach(function (t) {
      filas.push(COLUMNAS.map(function (c) { return valor_(t[c[0]]); }));
    });
    pagina++;
  } while (pagina <= paginas);

  // Se escribe de una sola vez, no celda a celda: en Apps Script cada
  // escritura es una llamada al servidor de Google, y 200 filas × 13 columnas
  // serían 2.600 llamadas.
  hoja.clear();
  hoja.getRange(1, 1, 1, COLUMNAS.length)
      .setValues([COLUMNAS.map(function (c) { return c[1]; })])
      .setFontWeight('bold');
  if (filas.length) {
    hoja.getRange(2, 1, filas.length, COLUMNAS.length).setValues(filas);
  }
  hoja.setFrozenRows(1);
  hoja.autoResizeColumns(1, COLUMNAS.length);

  libro.toast(filas.length + ' tickets traídos de CALADESK.', 'Listo', 5);
}


/**
 * Una fecha en texto no se puede ordenar ni filtrar; una fecha de verdad, sí.
 * CALADESK las manda en formato ISO («2026-09-11T14:03:00+00:00»).
 */
function valor_(v) {
  if (v === null || v === undefined) return '';
  if (typeof v === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(v)) {
    return new Date(v);
  }
  return v;
}


/** Que se actualice sola cada hora, aunque no tengas la hoja abierta. */
function activarAutomatico() {
  desactivarAutomatico();  // primero se limpian los que hubiera
  ScriptApp.newTrigger('actualizar').timeBased().everyHours(1).create();
  SpreadsheetApp.getUi().alert('Listo: se actualizará sola cada hora.');
}

function desactivarAutomatico() {
  ScriptApp.getProjectTriggers().forEach(function (t) {
    if (t.getHandlerFunction() === 'actualizar') ScriptApp.deleteTrigger(t);
  });
}

4. Vuelve a la hoja y guarda la credencial

Cierra la pestaña de Apps Script, vuelve a la hoja y recárgala (F5). Arriba, junto a «Ayuda», aparecerá un menú nuevo: CALADESK.

Pulsa CALADESK → Guardar mi credencial… y pega la que copiaste en el paso 1.

Google te pedirá permiso la primera vez, y el aviso da respeto: dirá que la app «no está verificada». Es normal — la app eres tú, la acabas de escribir. Pulsa Configuración avanzada → Ir a (nombre del proyecto) → Permitir. Le estás dando permiso a tu propio script para leer tu hoja y salir a internet.

5. Trae los tickets

CALADESK → Actualizar ahora. Unos segundos después aparece la hoja «Tickets» llena, y abajo un aviso diciendo cuántos trajo.

Ya está: acabas de hacer una integración por API de principio a fin.

6. Que se actualice sola

CALADESK → Actualizar sola cada hora. A partir de ahí se refresca aunque no tengas la hoja abierta ni el ordenador encendido — corre en los servidores de Google.

Eso es lo que convierte esto en una sincronización y no en una consulta suelta.

Si algo falla

Lo que ves Qué hacer
No aparece el menú CALADESK La hoja no se ha recargado desde que pegaste el código. Recárgala con F5.
«Todavía no has guardado la credencial» Falta el paso 4: CALADESK → Guardar mi credencial…
«CALADESK no reconoce la credencial» Se copió cortada, o se borró en la plataforma. Emite otra en Acceso por API.
«Demasiadas peticiones seguidas» Espera un minuto. Con la actualización horaria no volverá a pasar.
La hoja sale vacía, sin error Esa empresa no tiene tickets todavía. Crea uno y vuelve a actualizar.

Cosas que puedes probar después

  • Filtrar antes de traer. En pedirPagina_, añade a la url algo como &estado=abierto. Trae menos y va más rápido.
  • Quitar columnas. Borra su línea de COLUMNAS. Nada más.
  • Una tabla dinámica encima. Con las fechas ya como fechas de verdad, Sheets te deja agrupar por mes, por estado o por agente sin tocar el código.
  • Ponerlo en un panel. Google Looker Studio lee de una hoja: conectas esta y tienes gráficos que se actualizan solos.

Lo que acabas de hacer es una integración por API real, sin depender de tu equipo técnico ni escribir una aplicación desde cero.

Si quieres entender la API a fondo antes de construir algo tuyo, lee cómo funciona

¿Todavía no usas CALADESK?

Esta hoja se llena con tus tickets reales, vengan de correo, Telegram, WhatsApp o el chat de tu web.

Hablemos