읽기 전용 분석 API
api.jonot.io/v1/* HTTP API는 조직의 번호표와 대기열 데이터를 읽기 전용으로 제공합니다. Admin 로그인 대신 bearer 토큰으로 인증합니다. 데이터 웨어하우스, 비즈니스 인텔리전스 도구, 사용자 지정 대시보드로 데이터를 가져올 때 사용하세요.
토큰 발급받기
섹션 제목: “토큰 발급받기”- admin.jonot.io/settings/integrations를 엽니다.
- API 토큰 탭을 클릭합니다.
- 토큰 생성을 클릭하고 이름을 입력한 뒤(예: “Power BI”) 확인합니다.
- 토큰을 즉시 복사하세요. 한 번만 표시되며 복구할 수 없습니다. 분실하면 토큰을 폐기하고 새로 만드세요.
토큰은 jot_ 접두사가 붙으며 자체적으로 만료되지 않습니다. 더 이상 필요하지 않으면 동일한 탭에서 폐기하세요.
Authorization: Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| 상태 코드 | 의미 |
|---|---|
401 | 토큰이 없거나, 형식이 잘못되었거나, 알 수 없거나, 폐기된 토큰입니다. |
402 | 유효한 토큰이지만 조직에서 API 기능이 활성화되어 있지 않습니다. |
429 | 요청 한도를 초과했습니다 — 아래 요청 한도를 참고하세요. |
400 | 쿼리 매개변수가 잘못되었거나, 날짜 범위가 90일을 초과합니다. |
엔드포인트
섹션 제목: “엔드포인트”GET /v1/queues
섹션 제목: “GET /v1/queues”조직의 지점과 대기열을 필터링이나 페이지네이션 없이 반환합니다:
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" } ] } ]}반환된 id 값을 사용하여 /v1/tickets와 CSV 내보내기를 queueId / locationId로 필터링할 수 있습니다.
GET /v1/tickets
섹션 제목: “GET /v1/tickets”조직 범위로 제한된 페이지네이션 방식의 번호표 조회입니다.
| 매개변수 | 필수 여부 | 반복 가능 | 설명 |
|---|---|---|---|
from | 예 | 아니오 | ISO-8601, createdAt 기준 포함 하한값. |
to | 예 | 아니오 | ISO-8601, 제외 상한값. 최대 90일 범위. |
queueId | 아니오 | 예 | 여러 대기열을 필터링하려면 매개변수를 반복하세요. |
locationId | 아니오 | 예 | 여러 지점을 필터링하려면 매개변수를 반복하세요. |
status | 아니오 | 예 | WAITING, CALLED, COMPLETED, CANCELED, SKIPPED, NO_SHOW 중 하나. |
cursor | 아니오 | 아니오 | 이전 페이지의 nextCursor에서 받은 불투명 값. |
limit | 아니오 | 아니오 | 기본값 100, 최대 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…"}각 행에는 번호표의 bearer 해시나 고객이 입력한 개인정보(PII)인 이름, 메모, 인원 수가 포함되지 않습니다. ID, 상태, 상태 변경 시각만 포함됩니다.
페이지네이션: nextCursor가 null이 아니면 다음 요청의 cursor로 전달하세요. from, to, 필터 값은 그대로 유지합니다. nextCursor가 null이면 범위의 끝입니다.
GET /v1/exports/tickets.csv
섹션 제목: “GET /v1/exports/tickets.csv”이 엔드포인트는 /v1/tickets와 같은 필터를 사용합니다. from과 to는 필수이며 queueId, locationId, status는 반복해서 지정할 수 있습니다. cursor와 limit는 사용할 수 없습니다. 조건에 맞는 전체 범위가 하나의 CSV 응답으로 전송되므로 500행 페이지 제한이 적용되지 않습니다.
열 순서는 고정되어 있지만, 고정 위치가 아닌 헤더 이름으로 열을 파싱하세요. 헤더 행은 항상 존재하며 아래 목록과 정확히 일치합니다:
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.csv응답은 Transfer-Encoding: chunked 방식으로 전송됩니다. 90일 동안의 10만 행을 내보내도 클라이언트나 서버가 전체 데이터를 메모리에 저장할 필요가 없습니다. 파일이나 파서로 바로 보내세요.
90일 범위 제한
섹션 제목: “90일 범위 제한”from과 to를 받는 모든 엔드포인트는 90일을 초과하는 범위에 400을 반환합니다. 더 긴 기록이 필요하면 한 주씩 짧은 범위로 요청하세요. calledAt, completedAt 같은 상태 변경 시각으로 같은 행을 다시 요청하지 않고도 대기 및 서비스 시간을 계산할 수 있습니다.
요청 한도
섹션 제목: “요청 한도”각 토큰은 분당 60회 요청할 수 있습니다. IP 주소가 아니라 토큰별로 제한합니다. 모든 응답에는 다음 헤더가 포함됩니다.
X-RateLimit-Remaining: 42X-RateLimit-Reset: 1751328000000429에는 추가로 Retry-After(초 단위)가 포함됩니다. 해당 시간만큼 대기한 뒤 재시도하세요. 몇 분 간격의 예약 동기화라면 여유 있게 한도 이내로 유지됩니다.
데모 조직
섹션 제목: “데모 조직”데모 조직도 동일한 API를 사용할 수 있으며, 분당 요청 한도에 더해 하루 50회의 추가 할당량이 적용됩니다. 큐 목록 조회, 번호표 조회, CSV 내보내기 한 번 정도로 연동을 처음부터 끝까지 검증하기에는 충분하면서도, 운영 환경에서 그대로 사용하기에는 의도적으로 부족한 양입니다. 이 한도를 초과하면 일반적인 요청 한도 응답 대신, 원인을 알려주는 본문과 함께 429가 반환됩니다.
{ "error": "demo_quota_exceeded", "limit": 50, "resetAt": "2026-01-02T09:00:00.000Z"}데모 CSV 내보내기에도 표시가 추가되어, 내보낸 파일이 운영 데이터와 혼동되지 않습니다. 파일명에는 demo- 접두사가 붙고, 응답에는 X-Jonot-Demo: 1이 포함되며, CSV 끝에는 demo 열이 추가됩니다. 유료 내보내기는 변경되지 않습니다 — 추가 열도, 추가 헤더도 없습니다. 유료 플랜을 구독하면 일일 할당량이 해제되고 이러한 표시도 사라집니다.
Python + pandas
섹션 제목: “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())또는 CSV 내보내기를 직접 읽을 수도 있습니다 — pandas가 스트리밍 응답을 투명하게 처리합니다:
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 (Web 커넥터)
섹션 제목: “Power BI (Web 커넥터)”- Power BI Desktop에서: 데이터 가져오기 → 웹.
- 고급을 선택하고, 원하는 날짜 범위로 URL을 구성합니다. 예:
https://api.jonot.io/v1/exports/tickets.csv?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z. - HTTP 요청 헤더 매개변수에서 이름이
Authorization이고 값이Bearer jot_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx인 헤더를 추가합니다. - 확인을 클릭하면 — Power BI가 CSV를 인식하고 테이블 미리보기를 엽니다.
- 로드를 클릭합니다(열 형식을 지정하려면 먼저 데이터 변환을 클릭하세요 —
createdAt/calledAt등은 텍스트로 가져와지므로 Power Query에서날짜/시간으로 변환하세요). - 주기적으로 데이터를 가져온다면 Power BI 서비스에서 예약 새로 고침을 설정하세요. 새로 고침당 범위는 90일보다 여유 있게 짧게 유지하세요.