REST API

기본 주소 https://rerent.ai · 인증 Authorization: Bearer <토큰> (설정 → API 토큰) · 요청·응답 JSON · 시각은 epoch 초(분 단위 정렬, 60의 배수) · 금액·가격은 KRW 정수.

응답 규약: 단건은 { "object": "order", "id": "ordr_…", … }, 목록은 { "object": "list", "data": [...], "has_more": false, "cursor": null }. 오류는 { "error": { "code": "insufficient_balance", "message": "…" } }.

스케줄(*_allocation_schedule_delta)은 [{start_at, end_at, node_count}] 배열이고 응답에서는 마지막에 end_at: null, node_count: 0 종결자가 붙어요. 반열림 구간 [start_at, end_at).

계정

메서드경로설명
GET/v1/account조직·사용자·잔액·홀드·가용
GET/v1/stats/realized실현 할인 통계 + 공급자 매출

SKU

메서드경로설명
GET/v1/skus거래 중인 SKU. ?all=1 이면 중단 포함
GET/v1/skus/{id|alias}상세
GET/v1/skus/{id|alias}/availability?range_start_at&range_end_at살 수 있는 노드 수 스케줄(가격 무관) + 최저·최고 매도가

풀

메서드경로설명
GET/v1/pools?full=1내 풀 목록(full이면 스케줄 포함)
POST/v1/pools{name}
GET/v1/pools/{id|name}스케줄 · 매도 중 · 팔 수 있는 시간(SKU 별)
PATCH/v1/pools/{id}{name}
DELETE/v1/pools/{id}열린 주문·남은 할당이 없을 때만
GET/v1/pools/{id}/sellable?sku=팔 수 있는 스케줄
POST/v1/pools/{id}/extend{sku, duration_hours|end_at, max_price, allow_standing?, estimate?}
GET / POST/v1/pools/transfers{from_pool, to_pool, sku, start_at, end_at, node_count}
GET/v1/pools/transfers/{id}

주문

메서드경로설명
GET/v1/orders?side=&state=&pool=&limit=&starting_after=&fills=1내 주문. state 반복 가능
POST/v1/orders주문 생성(아래) · Idempotency-Key 헤더 지원
POST/v1/orders/estimate같은 본문으로 견적(체결 없음)
GET/v1/orders/{id}상세 + fills
POST/v1/orders/{id}/cancel204 · 남은 부분만 취소

주문 본문:

{
  "side": "buy",                 // buy | sell
  "pool": "default",             // 풀 ID 또는 이름
  "sku": "icn-1-h100",           // SKU ID 또는 alias
  "start_at": "now",             // epoch 초 또는 "now"
  "end_at": 1789380000,          // 또는 duration_hours
  "node_count": 2,
  "max_price": 30000,            // buy: max_price · sell: min_price (또는 limit_price)
  "allow_standing": false,
  "allow_partial": false,
  "memo": "nightly-train"
}

allocation_schedule_delta: [{start_at, end_at, node_count}] 한 구간으로 줘도 돼요(SF Compute 호환 꼴).

주문 응답의 주요 필드: state, allocation_schedule_delta(요청), remaining_allocation_schedule_delta(호가에 남은 것), filled_allocation_schedule_delta(체결된 것), fills[](각 체결의 price_krw_per_node_hour, gross_krw, fee_krw, net_krw, maker_side), filled_average_price_krw_per_node_hour.

오류 코드: insufficient_balance(402) · insufficient_allocation(409) · invalid_window · start_in_past · end_too_far · invalid_price · sku_not_found · pool_not_found · idempotency_mismatch(409).

공개 오더북(익명)

메서드경로설명
GET/v1/orderbook/summarySKU 별 최저 매도·가능 노드·24h 체결
GET/v1/orderbook/windows?sku=&range_start_at=&range_end_at=열린 주문이 있는 SKU×윈도우와 best bid/ask
GET/v1/orderbook/quote?sku=&start_at=&end_at=한 윈도우의 톱오브북
GET/v1/orderbook/depth?sku=&start_at=&end_at=&depth=20가격 레벨별 노드 수
GET/v1/orderbook/fills?sku=&start_at=&end_at=&since_at=&limit=체결 내역

하드웨어 필터는 sku= 대신 requirements=accelerator:h100;region:kr 꼴도 받아요.

인스턴스(실행층)

메서드경로설명
GET / POST/v1/instances목록(?pool=&state=&all=1) / 생성 {pool, sku?, count?, name?, image?, mode?, command?, startup_script?, env?, ports?, priority?}
GET / DELETE/v1/instances/{id}단건 / 종료
POST/v1/instances/{id}/replace종료 + 같은 설정으로 새 인스턴스 대기열
GET/v1/instances/{id}/logs?tail=컨테이너 로그
POST/v1/instances/{id}/exec{cmd, timeout_secs?} 명령 한 번(≤50초)
GET/v1/instances/presets(공개) 이미지 프리셋·기본값
PATCH/v1/pools/{id}{launch_template:{enabled, image, mode, command?, startup_script?, env?, ports?, priority}} 자동 시작
GET/v1/nodes내 배정 노드(현재·예정) — 노드 상태·감지 GPU·그 위의 인스턴스와 ssh_command
GET / POST/v1/ssh-keys · DELETE /v1/ssh-keys/{id}조직 SSH 공개키(돌고 있는 인스턴스에 즉시 반영)

자동 매수(배포)

풀에 「이 SKU 노드 N대를 앞으로 H시간 동안 항상」 걸어 두면 매 분 보유 스케줄의 빈 구간을 상한가 이하 매물에서 즉시 사요(부분 체결 허용 · 호가에 남기지 않음). 공급자 자동 매도의 반대편이고, 풀의 자동 시작 템플릿과 함께 쓰면 사자마자 인스턴스가 떠요.

메서드경로설명
GET / POST/v1/deployments목록(?pool=) / 생성 {pool, sku, name?, target_node_count, max_price, horizon_hours?=6, block_hours?=1, enabled?}
GET / PATCH / DELETE/v1/deployments/{id|name}단건 / {enabled?, target_node_count?, max_price?, horizon_hours?, block_hours?, name?} / 중단(산 시간·주문은 남음)
POST/v1/deployments/{id}/run지금 한 번 확보 시도(꺼져 있어도) → {bought_node_hours, buy_orders, error, deployment}
  • horizon_hours 1~168: 확보 기간 — 지금부터 몇 시간 앞까지 항상 확보해 둘지. block_hours 1~24: 한 번에 사는 최소 길이 — 확보 기간 끝의 1분 조각이 매 분 1분짜리 매수가 되지 않게 블록마다 한 번 사요.
  • 응답 coverage: {now_nodes, horizon_covered_pct, next_gap_at} · spent_krw · bought_node_hours · buy_orders · last_error(크레딧 부족 · 상한가 이하 매물 없음 등 — 다음 분에 다시 시도).
  • 자동 매수 주문은 managed_by 가 배포 id 예요(주문 목록에 「자동」 표시).

공급자

메서드경로설명
GET / POST/v1/clusters목록 / 등록(응답 installs[] 에 노드별 설치 명령 install.shell(curl 한 줄 · 24시간 유효)·스크립트 주소 install.script_url · bootstrap 토큰 1회)
GET / PATCH / DELETE/v1/clusters/{id}PATCH {name?, auto_sell?, available_to_at?}
POST/v1/clusters/{id}/nodes{nodes:[…]} 또는 줄바꿈 문자열 → 응답 installs[]
POST/v1/clusters/{id}/nodes/{nodeId}/install설치 명령 다시 받기(bootstrap 재발급 · 등록된 노드는 재설치용 새 호스트)
DELETE/v1/clusters/{id}/nodes/{nodeId}노드 제거(구매자 인스턴스가 돌고 있으면 409)
GET/v1/clusters/{id}/assignments노드별 배정(익명 꼬리표)

크레딧·조직

메서드경로설명
GET/v1/credits/ledger원장
GET / POST/v1/credits/topups충전 요청 {amount_krw}
GET / POST/v1/credits/payouts출금 요청 {amount_krw, bank_info} (소유자)
GET / POST/v1/tokens · DELETE /v1/tokens/{id}API 토큰
GET/v1/members · PATCH/DELETE /v1/members/{userId}멤버·역할
POST/v1/invites{email, role} → 초대 링크

역할

역할할 수 있는 것
owner전부 + 출금 + 역할 변경
admin매도·클러스터·토큰·초대·충전 요청
member매수·조회
viewer조회만(GET)

API 토큰은 만들 때 고른 역할(admin·member·viewer)로 동작해요(기본 admin).

워크스페이스

조직 안에서 풀·인스턴스 템플릿·이미지를 묶는 단위예요(SF Compute workspaces). 인스턴스·주문·배포는 풀의 워크스페이스를 따라요. 조직마다 default 가 하나 있고 특별하지 않아요(이름 변경·삭제 가능 · 마지막 하나는 남음).

메서드경로설명
GET / POST/v1/workspaces목록 / 생성 {name, description?}
GET / PATCH / DELETE/v1/workspaces/{id|name}단건 / {name?, description?} / 삭제(풀·템플릿·이미지가 남아 있으면 409 workspace_not_empty)
  • 풀 생성 본문 workspace · PATCH /v1/pools/{id} {workspace} 로 이동 · 목록 API 에 ?workspace=<id|name> 필터(풀·인스턴스·주문·배포·템플릿·이미지).
  • 자원 경로(resource_path) rerent:pool:<조직 slug>:<워크스페이스>:<이름> 을 id 자리에 써도 돼요(풀·템플릿·이미지·배포).

태그

key=value 쌍으로 자원을 묶어요. 풀·주문·인스턴스·배포·클러스터·템플릿·이미지에 붙고, 생성 본문 tags: {key: value}(또는 ["key=value"]) · PATCH 로 전부 교체(tags: null 이면 모두 삭제).

  • 규칙: 키 ≤128자(공백·쉼표·= 금지 · _ 로 시작 금지) · 값 ≤256자(쉼표·= 금지) · 자원당 50개 · 대소문자 구분.
  • 목록 필터: ?tag=env%3Dprod(반복 가능 · 모두 만족) · ?tag_key=team(키만).
  • 배포의 태그는 그 배포가 만드는 인스턴스·주문에도 붙어요.

한도

메서드경로설명
GET/v1/limits내 한도(limits)와 사용량(usage) · 화면용 items[]
  • compute_max_total_node_hours: 전 풀 합계로 보유할 수 있는 노드·시간. 스케줄 아래 면적이라 멀리 사도 넓게 사도 같이 소모돼요. compute_node_hours_free_window_seconds(기본 1시간) 안은 세지 않아요.
  • 넘기는 매수 주문은 403 limit_exceeded 로 거절되고 주문이 만들어지지 않아요(견적은 통과 · 매도는 한도로 거절되지 않아요).
  • 개수 한도: 풀·워크스페이스·열린 주문·주문 하나의 노드 수·살아 있는 인스턴스·템플릿·이미지·SSH 키·API 토큰·클러스터·노드·배포·웹훅. 상향은 support@rerent.ai.

인스턴스 템플릿

재사용 가능한 인스턴스 설정(불변 — 바꾸려면 새 이름으로). 배포가 인스턴스를 만들 때 쓰고, POST /v1/instances {template} 로 사람도 써요.

메서드경로설명
GET / POST/v1/instance-templates?workspace=&tag=목록 / 생성 {name, image, mode?, command?, startup_script?, env?, ports?, priority?, ssh_allow_cidrs?, tags?, workspace?, description?}
GET / PATCH / DELETE/v1/instance-templates/{id|name}단건 / {tags?, description?} 만 / 삭제(배포·살아 있는 인스턴스가 쓰면 409 template_in_use)
  • image 에 이미지 카탈로그 이름(trainer)을 쓰면 참조·자격증명이 붙어요.
  • ssh_allow_cidrs: SSH 접속을 허용할 IPv4 대역(203.0.113.0/24). 비우면 어디서나(키 있는 사람만). 컨테이너 sshd 의 Match Address 규칙으로 적용돼요 — 노출 포트(ports)에는 적용되지 않아요.
  • 인스턴스 생성 시 template + 다른 필드를 함께 보내면 요청 값이 템플릿을 덮어써요. priority_level: yield|normal|preferred|critical 도 받아요(= priority 1~4).

이미지 카탈로그

조직이 이름 붙여 둔 도커 이미지 참조(+비공개 레지스트리 자격증명). Rerent 는 컨테이너라 레지스트리 참조가 곧 이미지예요(SF Compute 의 디스크 이미지 업로드는 없음).

메서드경로설명
GET / POST/v1/images?workspace=&tag=&status=&include_public=1목록(+공개 프리셋) / 등록 {name, image_ref, description?, registry_auth?: {registry?, username, password}, tags?, workspace?}
GET / PATCH / DELETE/v1/images/{id|name}단건 / {description?, tags?, registry_auth?|null} / 삭제(살아 있는 인스턴스·템플릿이 쓰면 409)
POST/v1/images/{id}/revoke · /reinstate회수(새 인스턴스에 못 씀 · 돌던 것은 그대로) / 복구
  • 자격증명은 응답에 절대 나오지 않고, 컨테이너 기동 때 노드 에이전트가 격리된 임시 도커 설정으로 로그인해 pull 하는 데만 써요.
  • 회수된 이미지를 가리키는 템플릿·인스턴스 생성은 400 image_revoked.

인스턴스 추가 API

메서드경로설명
PATCH/v1/instances/{id}{name?, priority?|priority_level?, tags?} — 그 밖의 설정은 불변(교체로)
PATCH/v1/instances{ids: […], priority?|priority_level?, tags?} 여러 개 한 번에 — 전부 성공 아니면 전부 실패
GET/v1/instances/{id}/ssh접속 정보(host·port·command·allowed_cidrs·등록 키 지문) — 실행 중 ssh 모드에서만
GET/v1/instances?deployment=&workspace=&tag=배포·워크스페이스·태그 필터
GET/v1/skus/availability?range_start_at&range_end_at&requirements=(공개) 전 SKU 가용 스케줄 한 번에
GET/v1/users · /v1/users/{id|email}조직 사용자(/v1/members 와 같은 데이터)

인스턴스 응답에 tags · template {id,name} · image_catalog {id,name,revoked} · deployment {id,name,kind} · managed_by · priority_level · ssh_allow_cidrs 가 추가됐어요.

배포(deployments) — generic · spot

배포는 풀 위에서 인스턴스 무리를 목표 수만큼 유지하는 자동화예요(SF Compute deployments / spot deployments).

kind하는 일
spot(기본)목표 노드 수를 확보 기간(앞으로 horizon_hours시간) 안에서 항상 확보하도록 상한가 이하 매물을 매 분 매수(부분 허용 · 호가에 남기지 않음) + template 이 있으면 산 노드에 인스턴스를 목표 수만큼 유지
generic매수 없음. 이미 산 시간 위에서 template 으로 인스턴스 수만 목표에 맞춤(템플릿 필수)
메서드경로설명
GET / POST/v1/deployments?pool=&kind=&workspace=&tag=목록 / 생성 {pool, sku, kind?, name?, target_node_count(=target_instance_count), max_price(spot), horizon_hours?=6, block_hours?=1 (또는 min_runtime_minutes), template?, instance_name_template?, tags?, enabled?}
GET / PATCH / DELETE/v1/deployments/{id|name}단건 / 같은 필드 / 삭제(generic 은 관리 인스턴스 전부 종료 · spot 은 돌고 있는 것은 두고 더 사지 않음)
POST/v1/deployments/{id}/run(spot) 지금 한 번 매수 시도
  • 풀×SKU 조합마다 배포는 하나만(409 duplicate_pool_sku).
  • 인스턴스 이름 템플릿 {{adjective}}-{{noun}}-{{nanoid(6)}}(기본) · {{nanoid(n)}} 필수 · 결과는 32자 이내.
  • 목표를 줄이면 우선순위 낮은 것·최근 것부터 종료. 사람이 인스턴스를 종료하면 다음 조정(1분 안)에서 채워요. 일시 정지(enabled:false)는 대기 인스턴스만 내리고 돌고 있는 것은 그대로.
  • 응답 instances {awaiting, running, target} · status {state: info|warning|error, message} · min_runtime_minutes · instance_template · tags.
  • 주문 목록 ?deployment=<id|name> 으로 배포가 낸 주문만, ?managed=0 으로 자동 주문 제외.

이벤트 · 웹훅

조직에서 일어난 일이 이벤트로 기록되고(GET), 웹훅으로 내 서버에 POST 돼요. SF Compute 에는 없는 기능이에요.

메서드경로설명
GET/v1/events?type=&object_id=&limit=&starting_after=evt_…활동 기록(최근순 · 커서) + types[] 카탈로그
GET / POST/v1/webhooks목록 / 생성 {url(https), event_types?: [] (비우면 전부), description?, enabled?} → secret 은 지금 한 번만
GET / PATCH / DELETE/v1/webhooks/{id}단건 / {url?, event_types?, description?, enabled?} / 삭제
POST/v1/webhooks/{id}/testwebhook.test 이벤트를 바로 보내고 결과 {ok, status, error}
GET/v1/webhooks/{id}/deliveries배달 기록(상태·시도·오류·다음 재시도)

이벤트 종류: order.filled order.partially_filled order.standing order.cancelled order.fill(내 호가가 체결됨 · 메이커) instance.created instance.running instance.failed instance.terminated deployment.bought deployment.error deployment.scaled node.online node.offline node.assigned topup.confirmed payout.paid webhook.test.

배달: POST <url> · 본문 {id, type, label, object_type, object_id, data, created_at, org_id, webhook_id, api_version} · 헤더 Rerent-Event · Rerent-Event-Id · Rerent-Signature: t=<epoch>,v1=<hex> (v1 = HMAC-SHA256(secret, "<t>.<본문>") · 5분 안의 t 만 받아요). 2xx 가 아니면 1·5·30분·2시간 뒤 다시 보내고 5회 뒤 포기(failures 증가).

// 수신 서버(Node) 검증 예
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(secret, rawBody, header) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || ''); if (!m) return false
  if (Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false
  const want = createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest('hex')
  return timingSafeEqual(Buffer.from(want, 'hex'), Buffer.from(m[2], 'hex'))
}

토큰 역할

POST /v1/tokens {name, role?: admin|member|viewer, expires_days?} — admin(기본 · 전부) · member(매수·인스턴스·조회 · 매도·토큰·클러스터 불가) · viewer(GET 만 · 변경 요청은 403 read_only). 멤버 역할에도 viewer 가 추가됐고(PATCH /v1/members/{id} {role} · 초대 role), GET /v1/account 에 read_only · default_workspace_id 가 있어요.