Salta ai contenuti

API di analisi in sola lettura

L’API HTTP api.jonot.io/v1/* consente di leggere i dati su biglietti e code della tua organizzazione. Usa un bearer token al posto di un accesso Admin. Puoi importare i dati in un data warehouse, uno strumento di business intelligence o una dashboard personalizzata.

  1. Apri admin.jonot.io/settings/integrations.
  2. Fai clic sulla scheda API tokens.
  3. Fai clic su Create token, dagli un nome (es. “Power BI”) e conferma.
  4. Copia immediatamente il token — viene mostrato una sola volta e non può essere recuperato. Se lo perdi, revocalo e creane uno nuovo.

I token hanno il prefisso jot_ e non scadono mai da soli; revocali dalla stessa scheda quando non sono più necessari.

Authorization: Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
StatoSignificato
401Token mancante, malformato, sconosciuto o revocato.
402Token valido, ma l’organizzazione non ha la funzione API abilitata.
429Limite di frequenza superato — vedi Limiti di frequenza sotto.
400Parametri di query non validi, oppure un intervallo di date superiore a 90 giorni.

Restituisce le sedi e le code della tua organizzazione, non filtrate e non paginate:

Terminal window
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 i valori id restituiti per filtrare /v1/tickets e l’esportazione CSV per queueId / locationId.

Lettura paginata dei biglietti, limitata alla tua organizzazione.

ParametroObbligatorioRipetibileNote
fromnoISO-8601, limite inferiore incluso su createdAt.
tonoISO-8601, limite superiore escluso. Intervallo massimo di 90 giorni.
queueIdnoRipeti il parametro per filtrare più code.
locationIdnoRipeti il parametro per filtrare più sedi.
statusnoUno tra WAITING, CALLED, COMPLETED, CANCELED, SKIPPED, NO_SHOW.
cursornonoValore opaco da nextCursor della pagina precedente.
limitnonoPredefinito 100, massimo 500.
Terminal window
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…"
}

Le righe non includono mai l’hash bearer del biglietto o dati personali identificabili inseriti dal cliente, come nome, note o numero di persone. Includono solo ID, stato e timestamp del ciclo di vita.

Paginazione: quando nextCursor non è null, passalo come cursor nella richiesta successiva. Mantieni gli stessi valori di from, to e dei filtri. Un nextCursor null indica la fine dell’intervallo.

Questo endpoint accetta gli stessi filtri di /v1/tickets: from e to sono obbligatori, mentre queueId, locationId e status sono ripetibili. Non accetta cursor o limit. L’intero intervallo corrispondente viene trasmesso in un’unica risposta CSV, quindi il limite di 500 righe per pagina non si applica.

L’ordine delle colonne è stabile, ma analizza le colonne per nome di intestazione anziché per posizione fissa. La riga di intestazione è sempre presente e corrisponde esattamente a questo elenco:

id,number,queueId,locationId,status,createdAt,calledAt,completedAt,cancelledAt,skippedAt,noShowAt,calledByDeviceSessionId
Terminal window
curl "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.csv

La risposta usa Transfer-Encoding: chunked. Un’esportazione di 90 giorni con 100.000 righe non deve restare in memoria né sul server né sul client. Inviala direttamente a un file o a un parser.

Ogni endpoint che accetta from e to rifiuta con 400 un intervallo superiore a 90 giorni. Per una cronologia più lunga, richiedi intervalli più brevi, ad esempio una settimana alla volta. Usa i timestamp del ciclo di vita, come calledAt e completedAt, per calcolare i tempi di attesa e servizio senza richiedere due volte le stesse righe.

Ogni token consente 60 richieste al minuto. Il limite si applica al token, non all’indirizzo IP. Ogni risposta include:

X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1751328000000

Un 429 porta inoltre Retry-After (in secondi). Attendi quella finestra e riprova; una sincronizzazione pianificata ogni pochi minuti resta comodamente sotto il limite.

Un’organizzazione demo ha un limite aggiuntivo di 50 richieste al giorno. Si applica anche il limite al minuto. Puoi usare l’API demo per provare un’integrazione completa: elencare le code, richiedere alcuni biglietti ed esportare un CSV. Il limite giornaliero non basta per l’uso in produzione. Se lo superi, l’API restituisce 429 con un corpo che identifica la causa:

{
"error": "demo_quota_exceeded",
"limit": 50,
"resetAt": "2026-01-02T09:00:00.000Z"
}

Anche le esportazioni CSV demo vengono contrassegnate, così un file esportato non può essere confuso con dati di produzione: il nome del file ha il prefisso demo-, la risposta porta l’intestazione X-Jonot-Demo: 1, e viene aggiunta una colonna demo finale al CSV. Le esportazioni a pagamento restano invariate — nessuna colonna né intestazione aggiuntiva. Sottoscrivere un piano a pagamento rimuove la quota giornaliera e i contrassegni.

import requests
import 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())

Oppure leggi direttamente l’esportazione CSV — pandas gestisce la risposta in streaming in modo trasparente:

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}"},
)
  1. In Power BI Desktop: Get Data → Web.
  2. Scegli Advanced e costruisci l’URL con il tuo intervallo di date, es. https://api.jonot.io/v1/exports/tickets.csv?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z.
  3. Sotto HTTP request header parameters, aggiungi un header chiamato Authorization con il valore Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
  4. Fai clic su OK — Power BI rileva il CSV e apre l’anteprima della tabella.
  5. Fai clic su Load (oppure prima su Transform Data se vuoi impostare i tipi di colonna — createdAt/calledAt/ecc. vengono importati come testo; convertili in Date/Time in Power Query).
  6. Imposta un aggiornamento pianificato nel servizio Power BI se prelevi dati con una cadenza regolare; mantieni l’intervallo comodamente sotto i 90 giorni per aggiornamento.