Sync your tickets to a Google Sheet
A sheet that pulls in your CALADESK cases and updates itself. It is the first integration any customer asks for, and you can build the whole thing yourself: twenty minutes, with nothing to install and no program to write from scratch.
Before you start
- Your administrator account in CALADESK.
- A Google account, to create the sheet.
- You don't need to know how to program: the code is copied and pasted as is, nothing to adapt.
Here's how the sheet will look
| # | Subject | Status | Priority | Customer | Created |
|---|---|---|---|---|---|
| 43 | Verificación de sonido | nuevo | media | Carlos Luque | 11/09/2026 |
| 42 | Hola bot | nuevo | media | — | 10/09/2026 |
| 41 | Prueba de correo | nuevo | media | Carlos | 09/09/2026 |
1. Issue your credential in CALADESK
Log in as an administrator and go to Settings → API access. Write a name that tells you what it is for — for example Google Sheet — and click Create.
Copy it now. It is shown only once: it is stored encrypted, so it cannot be read again even from the server. If you lose it, nothing bad happens — you issue another one and delete this one — but you will have to repeat step 4.
For this integration do not turn on the write permission: the sheet is only going to read. A credential that only reads is a credential that, if it leaks, cannot open fake cases for you.
2. Create the sheet
Go to sheets.new and name it, for example CALADESK Tickets. No need to create any columns: the code writes them on its own.
3. Paste the code
In the sheet: menu Extensions → Apps Script. A new tab opens with a Code.gs file that has some sample lines. Delete all of it and paste this in its place. Then click the save icon and give the project a name.
/**
* 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. Go back to the sheet and save the credential
Close the Apps Script tab, go back to the sheet and reload it (F5). At the top, next to "Help", a new menu will appear: CALADESK.
Click CALADESK → Save my credential… and paste the one you copied in step 1.
Google will ask for permission the first time, and the warning looks alarming: it will say the app is "unverified". That is normal — the app is you, you just wrote it. Click Advanced → Go to (project name) → Allow. You are giving your own script permission to read your sheet and reach the internet.
5. Pull in the tickets
CALADESK → Update now. A few seconds later the "Tickets" sheet appears full, with a note at the bottom saying how many it brought in.
That's it: you just built an API integration from start to finish.
6. Make it update itself
CALADESK → Refresh itself every hour. From then on it refreshes even if you don't have the sheet open or your computer on — it runs on Google's servers.
That is what turns this into a sync, and not a one-off lookup.
If something goes wrong
| What you see | What to do |
|---|---|
| The CALADESK menu is missing | The sheet hasn't reloaded since you pasted the code. Reload it with F5. |
| "You haven't saved the credential yet" | Step 4 is missing: CALADESK → Save my credential… |
| "CALADESK doesn't recognize the credential" | It was copied incomplete, or was deleted on the platform. Issue another one in API access. |
| "Too many requests in a row" | Wait a minute. With the hourly refresh it won't happen again. |
| The sheet comes out empty, with no error | That company doesn't have any tickets yet. Create one and refresh again. |
Things you can try afterwards
- Filter before fetching. In
pedirPagina_, add something like&estado=abiertoto theurl. It brings in less and runs faster. - Remove columns. Delete its line from
COLUMNAS. That's all. - A pivot table on top. With the dates already as real dates, Sheets lets you group by month, by status or by agent without touching the code.
- Put it on a dashboard. Google Looker Studio reads from a sheet: connect this one and you get charts that update themselves.
What you just did is a real API integration, without relying on your technical team or writing an application from scratch.
If you want to understand the API in depth before building something of your own, read how it worksNot using CALADESK yet?
This sheet fills up with your real tickets, whether they come from email, Telegram, WhatsApp or your website chat.
Let's talk