API de analítica de solo lectura
La API HTTP api.jonot.io/v1/* ofrece acceso de solo lectura a los datos de turnos y colas de tu organización — sin necesidad de iniciar sesión como administrador, solo un token bearer. Úsala para llevar datos a un data warehouse, una herramienta de BI o un panel personalizado.
Obtener un token
Sección titulada «Obtener un token»- Abre admin.jonot.io/settings/integrations.
- Haz clic en la pestaña API tokens.
- Haz clic en Create token, dale un nombre (p. ej. «Power BI») y confirma.
- Copia el token de inmediato — se muestra una sola vez y no se puede recuperar. Si lo pierdes, revócalo y crea uno nuevo.
Los tokens llevan el prefijo jot_ y no caducan por sí solos; revócalos desde la misma pestaña cuando ya no los necesites.
Autenticación
Sección titulada «Autenticación»Authorization: Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| Estado | Significado |
|---|---|
401 | Token ausente, mal formado, desconocido o revocado. |
402 | Token válido, pero la organización no tiene activada la función de API. |
429 | Límite de tasa superado — consulta Límites de tasa más abajo. |
400 | Parámetros de consulta inválidos, o un rango de fechas de más de 90 días. |
Endpoints
Sección titulada «Endpoints»GET /v1/queues
Sección titulada «GET /v1/queues»Devuelve las ubicaciones y colas de tu organización, sin filtrar ni paginar:
curl https://api.jonot.io/v1/queues \ -H "Authorization: Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"{ "locations": [ { "id": "loc_…", "name": "Downtown", "slug": "downtown", "queues": [ { "id": "q_…", "name": "Main Queue", "slug": "main-queue", "status": "ACTIVE" } ] } ]}Usa los valores id devueltos para filtrar /v1/tickets y la exportación CSV por queueId / locationId.
GET /v1/tickets
Sección titulada «GET /v1/tickets»Lectura paginada de turnos, limitada a tu organización.
| Parámetro | Obligatorio | Repetible | Notas |
|---|---|---|---|
from | sí | no | ISO-8601, límite inferior inclusivo sobre createdAt. |
to | sí | no | ISO-8601, límite superior exclusivo. Máximo 90 días de rango. |
queueId | no | sí | Repite el parámetro para filtrar varias colas. |
locationId | no | sí | Repite el parámetro para filtrar varias ubicaciones. |
status | no | sí | Uno de WAITING, CALLED, COMPLETED, CANCELED, SKIPPED, NO_SHOW. |
cursor | no | no | Valor opaco tomado del nextCursor de la página anterior. |
limit | no | no | Por defecto 100, máximo 500. |
curl "https://api.jonot.io/v1/tickets?from=2026-06-01T00:00:00Z&to=2026-06-08T00:00:00Z&status=COMPLETED" \ -H "Authorization: Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"{ "items": [ { "id": "tkt_…", "number": 42, "queueId": "q_…", "locationId": "loc_…", "status": "COMPLETED", "createdAt": "2026-06-01T09:14:02.000Z", "calledAt": "2026-06-01T09:20:11.000Z", "completedAt": "2026-06-01T09:24:47.000Z", "cancelledAt": null, "skippedAt": null, "noShowAt": null, "calledByDeviceSessionId": "dev_…" } ], "nextCursor": "eyJjcmVhdGVkQXQi…"}Las filas nunca incluyen el hash bearer del turno ni datos personales introducidos por el cliente (nombre, notas, tamaño del grupo) — solo ids, estado y las marcas de tiempo del ciclo de vida.
Paginación: cuando nextCursor no es nulo, pásalo como cursor en la siguiente solicitud para continuar donde lo dejaste (mismos from/to/filtros). Un nextCursor con valor null significa que has llegado al final del rango.
GET /v1/exports/tickets.csv
Sección titulada «GET /v1/exports/tickets.csv»Los mismos filtros que /v1/tickets (from/to obligatorios, queueId/locationId/status repetibles) sin cursor/limit — todo el rango coincidente se transmite como una única respuesta CSV, así que no hay que sortear un límite de 500 filas por página en una extracción masiva.
El orden de las columnas es estable, pero analiza las columnas por nombre de encabezado, no por posición fija. La fila de encabezado siempre está presente y coincide exactamente con esta lista:
id,number,queueId,locationId,status,createdAt,calledAt,completedAt,cancelledAt,skippedAt,noShowAt,calledByDeviceSessionIdcurl "https://api.jonot.io/v1/exports/tickets.csv?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z" \ -H "Authorization: Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -o tickets.csvLa respuesta se transmite en flujo (Transfer-Encoding: chunked), de modo que una exportación de 90 días / 100 000 filas no necesita almacenarse en memoria en ninguno de los dos extremos — pásala directamente a un archivo o a un analizador.
Límite de 90 días por rango
Sección titulada «Límite de 90 días por rango»Todo endpoint que acepta from/to rechaza con 400 un rango de más de 90 días. Extrae los datos de forma incremental (p. ej. una llamada por semana) si necesitas un historial más largo — las marcas de tiempo del ciclo de vida del turno (calledAt, completedAt, …) permiten reconstruir la duración de espera/atención sin volver a extraer las mismas filas dos veces.
Límites de tasa
Sección titulada «Límites de tasa»60 solicitudes por minuto y por token (no por IP — el presupuesto viaja con el token). Cada respuesta incluye:
X-RateLimit-Remaining: 42X-RateLimit-Reset: 1751328000000Un 429 incluye además Retry-After (en segundos). Espera esa ventana antes de reintentar; una sincronización programada cada pocos minutos se mantiene cómodamente por debajo del límite.
Python + pandas
Sección titulada «Python + pandas»import requestsimport pandas as pd
TOKEN = "jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"BASE = "https://api.jonot.io/v1"HEADERS = {"Authorization": f"Bearer {TOKEN}"}
def fetch_tickets(frm: str, to: str) -> pd.DataFrame: rows = [] cursor = None while True: params = {"from": frm, "to": to, "limit": 500} if cursor: params["cursor"] = cursor res = requests.get(f"{BASE}/tickets", headers=HEADERS, params=params, timeout=30) res.raise_for_status() body = res.json() rows.extend(body["items"]) cursor = body["nextCursor"] if not cursor: break return pd.DataFrame(rows)
df = fetch_tickets("2026-06-01T00:00:00Z", "2026-07-01T00:00:00Z")df["waitSeconds"] = ( pd.to_datetime(df["calledAt"]) - pd.to_datetime(df["createdAt"])).dt.total_seconds()print(df.groupby("queueId")["waitSeconds"].mean())O lee directamente la exportación CSV — pandas gestiona la respuesta en flujo de forma transparente:
df = pd.read_csv( f"{BASE}/exports/tickets.csv?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z", storage_options={"Authorization": f"Bearer {TOKEN}"},)Power BI (conector web)
Sección titulada «Power BI (conector web)»- En Power BI Desktop: Get Data → Web.
- Elige Advanced y construye la URL con tu rango de fechas, p. ej.
https://api.jonot.io/v1/exports/tickets.csv?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z. - En HTTP request header parameters, añade un encabezado llamado
Authorizationcon el valorBearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. - Haz clic en OK — Power BI detecta el CSV y abre la vista previa de la tabla.
- Haz clic en Load (o antes en Transform Data si quieres fijar los tipos de columna —
createdAt/calledAt/etc. se importan como texto; conviértelos aDate/Timeen Power Query). - Configura una actualización programada en el servicio Power BI si extraes datos de forma periódica; mantén el rango cómodamente por debajo de 90 días por actualización.