Skip to Content

빠른 시작

이 가이드는 약 15분이 걸립니다. 먼저 AI 직원에게 첫 Turn을 보내고, 이어서 워크플로 Run과 웹훅까지 한 번씩 해 봅니다.

준비물

  • 조직 OWNER 권한이 있는 UpServe 계정입니다. Project 생성, 키 발급, 웹훅 등록은 OWNER만 할 수 있습니다.
  • Private Beta에 허용된 Project입니다. 허용 전에는 모든 /v1 경로가 404를 돌려줍니다. 참여를 원하면 UpServe 팀에 문의하세요.
  • 호출할 Deployment 하나 이상. 조직의 AI 직원을 발행해서 만듭니다.
  • 해당 Deployment를 허용한 API 키입니다. 키는 생성할 때 한 번만 보입니다.

Project 만들기, Deployment 발행, 키 발급, 웹훅 등록은 웹 앱의 설정 > 개발자(https://upserve.app/settings/developer) 화면에서 합니다. 이 화면은 로그인한 소유자의 권한으로 동작하는 관리 화면이고, API 키로는 관리 작업을 할 수 없습니다.

키는 환경변수로만 다룹니다. 코드, 로그, 저장소에 남기지 마세요.

1단계: 첫 Turn 호출하기

호출 흐름은 네 단계입니다.

  1. GET /v1/deployments로 이 키가 부를 수 있는 Deployment를 확인합니다.
  2. POST /v1/deployments/{deployment_id}/sessions로 Session과 첫 Turn을 만듭니다. 서버는 접수를 저장한 뒤 202로 응답합니다.
  3. 같은 요청을 같은 Idempotency-Key로 다시 보내 봅니다. 새로 실행하지 않고 같은 접수를 돌려줍니다.
  4. GET /v1/sessions/{session_id}/turns/{turn_id}를 폴링해 종결 상태를 기다립니다.

curl

export UPSERVE_API_KEY="up_..." export BASE="https://api.upserve.app" # 1. List the Deployments this key may call curl -s "$BASE/v1/deployments" \ -H "Authorization: Bearer $UPSERVE_API_KEY" # 2. Create a Session and its first Turn (202 Accepted) curl -s -X POST "$BASE/v1/deployments/$DEPLOYMENT_ID/sessions" \ -H "Authorization: Bearer $UPSERVE_API_KEY" \ -H "Idempotency-Key: demo-0001" \ -H "Content-Type: application/json" \ -d '{"input":[{"type":"TEXT","text":"Summarize this month sales."}]}' # 3. Send the exact same request again: the same receipt comes back # 4. Poll the Turn until it reaches a terminal status curl -s "$BASE/v1/sessions/$SESSION_ID/turns/$TURN_ID" \ -H "Authorization: Bearer $UPSERVE_API_KEY"

202 응답(receipt)은 아래 모양입니다.

{ "session_id": "6f1c0a52-0000-4000-8000-000000000001", "turn_id": "6f1c0a52-0000-4000-8000-000000000002", "status": "QUEUED", "idempotency_expires_at": "2026-10-06T09:00:00+00:00", "status_url": "/v1/sessions/6f1c0a52-0000-4000-8000-000000000001/turns/6f1c0a52-0000-4000-8000-000000000002", "events_url": "/v1/sessions/6f1c0a52-0000-4000-8000-000000000001/events" }

Idempotency-Key는 생성, 입력, 취소, 업로드 요청에 필수입니다. 통신 실패로 응답을 못 받았다면 같은 키로 같은 본문을 다시 보내세요. 이미 접수된 요청이면 같은 receipt가 돌아오고 작업이 두 번 실행되지 않습니다. 다른 본문을 같은 키로 보내면 409 IDEMPOTENCY_CONFLICT입니다. 완료한 일을 새로 시키려면 새 키를 쓰세요.

Python

import os import time import uuid import httpx BASE = "https://api.upserve.app" AUTH = {"Authorization": "Bearer " + os.environ["UPSERVE_API_KEY"]} DONE = {"SUCCEEDED", "FAILED", "CANCELED", "EXPIRED", "REQUIRES_ACTION"} def create_session(deployment_id: str, text: str, key: str) -> dict: response = httpx.post( f"{BASE}/v1/deployments/{deployment_id}/sessions", headers={**AUTH, "Idempotency-Key": key}, json={"input": [{"type": "TEXT", "text": text}]}, timeout=30, ) response.raise_for_status() return response.json() def wait_turn(session_id: str, turn_id: str) -> dict: while True: response = httpx.get(f"{BASE}/v1/sessions/{session_id}/turns/{turn_id}", headers=AUTH, timeout=30) response.raise_for_status() turn = response.json() if turn["status"] in DONE: return turn time.sleep(2) # Reuse the same key when you resend after a network failure key = str(uuid.uuid4()) receipt = create_session(os.environ["DEPLOYMENT_ID"], "Summarize this month sales.", key) turn = wait_turn(receipt["session_id"], receipt["turn_id"]) print(turn["status"], turn["usage"]) for output in turn["outputs"]: if output["type"] == "TEXT": print(output["text"])

TypeScript

Node 22 이상의 내장 fetch를 씁니다.

const BASE = 'https://api.upserve.app'; const AUTH = { Authorization: `Bearer ${process.env.UPSERVE_API_KEY}` }; const DONE = new Set(['SUCCEEDED', 'FAILED', 'CANCELED', 'EXPIRED', 'REQUIRES_ACTION']); async function createSession(deploymentId: string, text: string, key: string) { const response = await fetch(`${BASE}/v1/deployments/${deploymentId}/sessions`, { method: 'POST', headers: { ...AUTH, 'Idempotency-Key': key, 'Content-Type': 'application/json' }, body: JSON.stringify({ input: [{ type: 'TEXT', text }] }), }); if (!response.ok) throw new Error(JSON.stringify(await response.json())); return response.json(); } async function waitTurn(sessionId: string, turnId: string) { for (;;) { const response = await fetch(`${BASE}/v1/sessions/${sessionId}/turns/${turnId}`, { headers: AUTH }); if (!response.ok) throw new Error(JSON.stringify(await response.json())); const turn = await response.json(); if (DONE.has(turn.status)) return turn; await new Promise((resolve) => setTimeout(resolve, 2000)); } } // Reuse the same key when you resend after a network failure const receipt = await createSession(process.env.DEPLOYMENT_ID!, 'Summarize this month sales.', crypto.randomUUID()); const turn = await waitTurn(receipt.session_id, receipt.turn_id); console.log(turn.status, turn.usage);

성공한 Turn의 응답은 아래처럼 outputs에 결과가 담깁니다. usage.status가 PENDING이면 비용이 아직 확정되지 않았다는 뜻입니다.

{ "id": "6f1c0a52-0000-4000-8000-000000000002", "session_id": "6f1c0a52-0000-4000-8000-000000000001", "status": "SUCCEEDED", "usage": {"status": "FINAL", "total_su": 1.25}, "error": null, "created_at": "2026-10-05T09:00:00+00:00", "started_at": "2026-10-05T09:00:02+00:00", "completed_at": "2026-10-05T09:00:20+00:00", "outputs": [ {"id": "o1", "key": "answer", "type": "TEXT", "visibility": "FINAL", "availability": "READY", "item_id": "i1", "text": "This month sales were up 8%."} ], "next_cursor": null }

재시도와 백오프, SSE 재연결까지 포함한 전체 예제는 runtime_client.py, runtime_client.ts, runtime_quickstart.sh에서 받을 수 있습니다. 웹훅 수신 예제는 runtime_webhook_receiver.py, runtime_webhook_receiver.ts입니다.

2단계: 워크플로 Run 실행하기

게시된 워크플로는 Session이 아니라 Run으로 호출합니다. 흐름은 다음과 같습니다.

  1. (선택) POST /v1/files로 입력 파일을 올리고 file_id를 받습니다.
  2. POST /v1/workflows/{workflow_id}/runs로 게시 버전과 입력을 보냅니다. 202와 run_id를 받습니다.
  3. GET /v1/runs/{run_id}로 상태와 진행률을 폴링하거나 SSE로 받습니다. 종결 전에는 최종 결과가 없습니다.
  4. 종결하면 GET /v1/runs/{run_id}/outputs로 JSON, 파일, URL 결과를 읽습니다.
  5. 파일 결과는 POST /v1/artifacts/{artifact_id}/download로 60초짜리 주소를 발급받아 바로 내려받습니다.
# 1. (Optional) Upload an input file and keep the returned file_id curl -s -X POST "$BASE/v1/files" \ -H "Authorization: Bearer $UPSERVE_API_KEY" \ -H "Idempotency-Key: file-0001" \ -F "file=@report.csv;type=text/csv" # 2. Start a Run on a published version (202 Accepted) curl -s -X POST "$BASE/v1/workflows/$WORKFLOW_ID/runs" \ -H "Authorization: Bearer $UPSERVE_API_KEY" \ -H "Idempotency-Key: run-0001" \ -H "Content-Type: application/json" \ -d '{"published_version":3,"input":{"count":3},"file_ids":["'"$FILE_ID"'"]}' # 3. Poll the Run status and progress curl -s "$BASE/v1/runs/$RUN_ID" \ -H "Authorization: Bearer $UPSERVE_API_KEY" # 4. Read the outputs (JSON, FILE and URL items), following next_cursor if present curl -s "$BASE/v1/runs/$RUN_ID/outputs?limit=50" \ -H "Authorization: Bearer $UPSERVE_API_KEY" # 5. Issue a short-lived download URL for a FILE output, then fetch it right away curl -s -X POST "$BASE/v1/artifacts/$ARTIFACT_ID/download" \ -H "Authorization: Bearer $UPSERVE_API_KEY" curl -s -o report.pdf "$BASE$DOWNLOAD_URL"

각 단계의 자세한 규칙은 Workflow와 Run에 있습니다.

3단계: 완료 알림 받기

폴링 대신 완료 시점에 알림을 받으려면 웹훅을 씁니다. 웹훅은 개발자 화면에서 HTTPS 주소로 등록하고, 등록할 때 한 번 보이는 secret을 수신 서버에 보관합니다. 수신 서버는 서명을 검증하고, event_id로 중복을 거른 뒤, 인증된 조회로 현재 상태를 다시 읽습니다. 자세한 검증 코드는 웹훅을 보세요.

다음으로