S Spector Guía técnica · API v1

Integrá Spector con tu infraestructura.

La API pública de Spector te permite leer tus formularios, respuestas y archivos adjuntos desde cualquier sistema externo — Zapier, Make, n8n, scripts Python o Node, herramientas BI. Autenticación por Bearer token. JSON limpio. Sin SDK obligatorio.

2
Endpoints · v1
8
Pasos · 1 integración Sheets
~12 min
Lectura
01·Token

Paso · Credenciales

Generá un token de API.

Cada token identifica a tu cuenta. Spector guarda solo el hash — el plano se muestra una sola vez y nunca más. Si lo perdés, revocás y generás uno nuevo.

  1. Andá a Dashboard → Perfil.
  2. Bajá hasta la sección "API pública".
  3. Escribí un label descriptivo (ej. zapier-prod, script-marketing) y apretá Crear token.
  4. Copiá el token que aparece en pantalla. Empieza con sk_live_.
Guardalo ahora El token plano se muestra UNA sola vez. Pegalo en tu manager de secretos (1Password, Bitwarden, .env del servidor) antes de cerrar el banner verde. Si lo perdés, revocá y creá uno nuevo — no hay forma de recuperarlo.
Nunca lo expongas en frontend El token da acceso de lectura a TODOS tus formularios y respuestas. Tratalo como password: server-side only, fuera de repositorios, fuera de logs.
02·Auth

Paso · Autenticación

Usalo como Bearer token.

Todas las requests necesitan el header Authorization: Bearer <token>. Si falta, está mal formado, fue revocado o tu plan deja de incluir API, recibís 401 con un mensaje en español.

GET /api/v1/forms curl
curl https://spector.dataminds.lat/api/v1/forms \
  -H "Authorization: Bearer sk_live_abcdef1234567890..."

Si todo está OK, recibís 200 con JSON. Si no, alguno de estos:

StatusBodyCausa
401Falta header BearerNo mandaste el header de auth
401Token inválido, revocado o el plan no incluye APIToken mal escrito / revocado / downgrade de plan
404Formulario no encontradoEl ID no existe o no es tuyo
500Error del servidorAlgo se rompió en nuestro lado. Reintentá con backoff.
03·Forms

Endpoint · Listar formularios

Listá tus formularios.

Devuelve todos los formularios de tu cuenta (no incluye los eliminados).

GET /api/v1/forms request
curl https://spector.dataminds.lat/api/v1/forms \
  -H "Authorization: Bearer sk_live_xxx"
200 OK response
{
  "forms": [
    {
      "id": "0c76d8a7-f4c8-4e79-9fa1-cf05ca06190c",
      "title": "Inspección de equipo",
      "status": "published",
      "created_at": "2026-06-06T00:54:16.943Z"
    },
    {
      "id": "f74bdd4b-0b75-4db0-b337-fc1188a542af",
      "title": "Encuesta de satisfacción",
      "status": "draft",
      "created_at": "2026-06-05T12:00:00.000Z"
    }
  ]
}

Campos por formulario:

CampoTipoSignificado
iduuidIdentificador único del formulario (usalo para llamar al endpoint de respuestas)
titlestringTítulo visible
statusenumpublished o draft
created_atISO 8601Fecha de creación
04·Responses

Endpoint · Respuestas de un formulario

Traé las respuestas.

Devuelve las respuestas del formulario, paginadas por limit y offset.

GET /api/v1/forms/{form_id}/responses request
curl "https://spector.dataminds.lat/api/v1/forms/0c76d8a7.../responses?limit=50&offset=0" \
  -H "Authorization: Bearer sk_live_xxx"

Query params opcionales:

ParamDefaultRangoDescripción
limit501 — 200Cuántas respuestas devolver
offset0≥ 0Desde qué fila empezar (paginación)
200 OK response
{
  "form_id": "0c76d8a7-f4c8-4e79-9fa1-cf05ca06190c",
  "limit": 50,
  "offset": 0,
  "responses": [
    {
      "id": "49685f62-8ee2-47e9-80a4-55f49381e522",
      "submitted_at": "2026-06-05T06:54:19.120Z",
      "answers": {
        "95354ee3-d882-47c6-8bb6-d5c90e6641b0": "Lucía Pérez",
        "b3f93965-769d-475f-be64-363c8e319351": 7834,
        "0b0b299a-0d52-420c-9108-c88ef66a7574": "Fuga menor en válvula 3."
      },
      "metadata": {
        "platform": "web",
        "user_agent": "Mozilla/5.0",
        "duration_seconds": 145
      },
      "is_late": false,
      "over_quota": false
    }
  ]
}

Campos por respuesta:

CampoTipoSignificado
iduuidIdentificador de la respuesta
submitted_atISO 8601Cuándo el respondiente envió el form
answersobjectMapa {field_id: valor}. Ver paso 5 para campos con imágenes/archivos.
metadataobjectPlataforma (web/mobile), user agent, duración en segundos
is_latebooleanTrue si llegó después del deadline del form
over_quotabooleanTrue si llegó mientras el plan estaba excedido (cuenta como over-quota)
Field IDs en answers Las claves de answers son UUIDs internos de cada campo. Para mapearlos a labels humanos (ej. "Nombre del operario"), necesitás conocer la definición del form. En la próxima versión de la API agregamos GET /api/v1/forms/{id}/fields que devuelve el schema completo.
05·Files

Paso · Imágenes y adjuntos

Descargá imágenes y archivos.

Cuando un campo es de tipo camera, signature, file o image, el valor en answers[field_id] no es un string suelto — es un objeto con la URL pública del archivo, su nombre original, tipo MIME y tamaño.

answers[field_id] — campo con archivo shape
{
  "url": "https://spector.dataminds.lat/storage/v1/object/public/form-attachments/0c76d8a7.../uuid.jpg",
  "name": "foto-equipo-3.jpg",
  "mime_type": "image/jpeg",
  "size": 245678
}

Para descargar el archivo desde tu lado:

GET <url del archivo> curl
# La URL es pública — no necesitás el Bearer token para descargar
curl -O "https://spector.dataminds.lat/storage/v1/object/public/form-attachments/0c76d8a7.../uuid.jpg"
URLs públicas, por UUID Las URLs son inmutables y públicas. Cualquiera con el link completo puede descargar el archivo. Los UUIDs son prácticamente imposibles de adivinar, pero tratá las URLs como datos sensibles: no las pegues en chat público ni las expongas en frontend de terceros.

Si necesitás URLs firmadas con TTL (caducan en X minutos) o un proxy autenticado, contactá soporte para evaluar el upgrade del endpoint.

06·Best

Paso · Buenas prácticas

Integrá como un profesional.

  • Paginación. Si esperás muchas respuestas, andá pidiéndolas en chunks de 100–200 y avanzá con offset hasta recibir menos del limit.
  • Rate limit. No hay límite duro hoy, pero evitá polling agresivo. Si necesitás real-time, mejor usá Webhooks (Settings → Integraciones del form).
  • Caché. El endpoint de listar formularios no cambia muy seguido. Cacheálo del lado tuyo por unos minutos.
  • Reintentos. En caso de 5xx, hacé backoff exponencial (2s, 4s, 8s…) hasta 3 intentos antes de fallar.
  • Logs. Loggeá el id de cada response procesada para hacer dedupe si tu integración no es idempotente.
  • Rotación de tokens. Si trabajás con un equipo, asigná un token por integración (uno para Zapier, otro para tu BI, etc.). Cuando alguien deja el equipo, revocás solo el suyo sin romper las otras.
  • Downgrade de plan. Si bajás de plan a uno sin api_access, el token queda inerte automáticamente (responde 401). Subir el plan vuelve a activarlo sin re-generar.
Webhooks vs API pull La API es para integraciones que consultan Spector. Si querés que Spector te notifique al instante cuando llega una respuesta nueva, configurá un Webhook en Settings → Integraciones del formulario. Tu endpoint recibe un POST con HMAC verificable.
07·Demo

Ejemplo · End-to-end

Un script Node.js completo.

Bajá todas las respuestas del primer form y guardá las imágenes adjuntas en una carpeta local. Sirve como plantilla para tus integraciones.

sync-respuestas.mjs Node 18+
import { writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';

const API = 'https://spector.dataminds.lat/api/v1';
const TOKEN = process.env.SPECTOR_TOKEN; // pegalo en .env

if (!TOKEN) {
  console.error('Falta SPECTOR_TOKEN en el environment');
  process.exit(1);
}

const headers = { Authorization: `Bearer ${TOKEN}` };

// 1. Listar formularios
const { forms } = await fetch(`${API}/forms`, { headers }).then(r => r.json());
console.log(`Tenés ${forms.length} formularios`);

if (forms.length === 0) process.exit(0);

const form = forms[0];
console.log(`Procesando: ${form.title}`);

// 2. Traer todas las respuestas (con paginación)
const all = [];
let offset = 0;
while (true) {
  const url = `${API}/forms/${form.id}/responses?limit=200&offset=${offset}`;
  const { responses } = await fetch(url, { headers }).then(r => r.json());
  all.push(...responses);
  if (responses.length < 200) break;
  offset += 200;
}
console.log(`Total respuestas: ${all.length}`);

// 3. Descargar imágenes adjuntas
const dir = './adjuntos';
await mkdir(dir, { recursive: true });

for (const r of all) {
  for (const [fieldId, value] of Object.entries(r.answers)) {
    if (value && typeof value === 'object' && 'url' in value && value.mime_type?.startsWith('image/')) {
      const ext = value.name.split('.').pop() || 'bin';
      const filename = `${r.id}-${fieldId}.${ext}`;
      const bin = await fetch(value.url).then(r => r.arrayBuffer());
      await writeFile(join(dir, filename), Buffer.from(bin));
      console.log(`✓ ${filename} (${(value.size / 1024).toFixed(0)} KB)`);
    }
  }
}

console.log('Listo.');
Correr: SPECTOR_TOKEN=sk_live_xxx node sync-respuestas.mjs
08·Sheets

Integración · Google Sheets sin código

Conectá con Google Sheets en 5 minutos.

Si lo que querés es ver tus respuestas en una Sheet (para pivotar, compartir con tu equipo, conectar a Looker Studio, hacer gráficos), no hace falta que escribas código. Te armamos un Apps Script template que se pega en tu propia Sheet y se actualiza solo cada 15 minutos.

Descargar spector-sync.gs ~8 KB · Apps Script

Qué hace el template

Un tab por formulario Crea automáticamente un tab con el título de cada form. Si no existe, lo crea.
Sync incremental Solo trae las respuestas nuevas desde la última corrida. No duplica filas.
Imágenes inline Las fotos de campos camera / file se ven con =IMAGE(), no como links.
Trigger cada 15 min Una vez configurado, se autosincroniza. Sin abrir la Sheet ni hacer nada.
Menú "Spector → Sincronizar ahora" Botón manual en la barra superior de la Sheet por si querés forzar una sync.
Reset de tracking Opción extra del menú para re-sincronizar todo desde cero (útil si cambiás columnas).

Setup paso a paso

  1. Descargá el archivo arriba (botón azul). Te baja un spector-sync.gs.
  2. Creá una Google Sheet nueva (o usá una existente). En el menú superior, andá a Extensiones → Apps Script.
  3. En el editor que se abre, borrá el código por default (function myFunction() {}).
  4. Abrí el archivo spector-sync.gs que descargaste en cualquier editor de texto, copialo entero y pegalo en el editor de Apps Script.
  5. Apretá el ícono de guardar (💾) o Ctrl + S. Te va a pedir un nombre para el proyecto — ponele "Spector Sync".
  6. Andá a Configuración del proyecto (ícono ⚙️ engranaje a la izquierda) → Propiedades del script"Agregar propiedad de script". Poné:
    Propiedad: SPECTOR_TOKEN
    Valor:     sk_live_xxxxxxxxxxxxxxxx
    Si no tenés un token todavía, andá a tu perfil de Spector y creá uno desde la card "API pública".
  7. Guardá. Volvé al editor (ícono <> de la izquierda).
  8. Andá a Activadores (ícono ⏰ reloj a la izquierda) → "+ Agregar activador". Configurá:
    Función a ejecutar:    syncSpector
    Origen del evento:     Basado en el tiempo
    Tipo:                  Temporizador de minutos
    Intervalo:             Cada 15 minutos
    Guardar. Te va a pedir permisos para acceder a la Sheet y a internet — aceptá con tu cuenta de Google.
Listo Cerrá las pestañas de Apps Script y volvé a tu Sheet. En la barra superior ahora vas a ver el menú "Spector". Apretá "Sincronizar ahora" para correr el primer sync manualmente y confirmar que todo está OK. Después se actualiza solo cada 15 minutos.

Si algo falla

Toast que vesCausaSolución
Falta SPECTOR_TOKEN No guardaste la propiedad del script Configuración del proyecto → Propiedades del script → agregar SPECTOR_TOKEN
Token inválido o el plan no incluye API Token revocado, mal copiado, o bajaste de plan Regenerá un token en /dashboard/profile y actualizá la propiedad
Error de red Spector está caído o problema temporal Esperá 5 min y reintentá. Si persiste, escribinos.
No tenés formularios Tu cuenta no tiene formularios todavía Creá uno desde el dashboard, después sincronizá.
Comparte la Sheet con tu equipo Una vez sincronizada, podés compartir la Google Sheet con quien quieras — sin que tenga acceso a Spector. Tu Sheet también funciona como fuente de datos para Looker Studio, Power BI o tu propio dashboard custom.