Jobs API 사용하기
API 키로 잡을 제출하고 상태를 읽고 로그와 결과물을 받는 HTTP 호출을 요청과 응답 예시로 설명합니다
러너 API는 https://xenoci.com/api/runner/v1 아래에 있고 모든 요청은 Authorization: Bearer xci_live_… 헤더 하나로 인증합니다
기본 규칙
| 항목 | 값 |
|---|---|
| 기본 URL | https://xenoci.com/api/runner/v1 |
| 인증 | Authorization: Bearer <API 키> (키는 콘솔 API 키 메뉴에서 발급) |
| 본문 | JSON, 최대 64KB (업로드만 application/octet-stream) |
| 응답 헤더 | X-Request-Id (문의할 때 함께 보내는 요청 번호) |
| 멱등 | 쓰기 요청에 Idempotency-Key 헤더(1~255자)를 보내면 24시간 안의 같은 요청은 같은 잡을 돌려주고, 본문이 다르면 409 idempotency_conflict |
| 오류 봉투 | { "error": { "code", "message", "retryable", "retry_after_s", "fault", "request_id", "docs_url", "details" } } |
read 권한 키는 GET만 할 수 있습니다
POST를 보내면 403 insufficient_permission입니다
키는 자기 워크스페이스에서만 동작하므로 workspace_id는 생략하거나 키의 워크스페이스와 같아야 합니다
1. 잡 제출
curl -s https://xenoci.com/api/runner/v1/jobs \
-H "Authorization: Bearer $XENOCI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"script": "xcodebuild -version\nsw_vers",
"xcode": "27.0",
"timeout_minutes": 20,
"source_kind": "api"
}'요청 본문 필드
| 필드 | 형식 | 설명 |
|---|---|---|
script | 문자열, 1~65,536자, 필수 | VM에서 bash -eo pipefail로 실행할 셸 스크립트 |
source | 객체 | { "upload_id": "upl_…" } 또는 { "git_url": "https://…", "ref": "main" } 없으면 빈 작업 폴더에서 시작 |
xcode | 문자열 | 27.0 또는 26.6 없으면 워크스페이스 기본값, 그것도 없으면 이미지 기본값 27.0 |
timeout_minutes | 정수 1~360 | 기본 60 스크립트 시작 후 이 시간이 지나면 timed_out |
env | 객체 | 환경 변수 최대 100개, 이름은 [A-Za-z_][A-Za-z0-9_]{0,127}, 값은 32,768자 이하 |
artifacts | 문자열 배열, 최대 50개 | 끝난 뒤 모을 경로 글롭 (build/*.ipa) |
repository | 문자열, 200자 이하 | owner/name 사용량 CSV 표시용 |
source_kind | cli, mcp, github_action, api | 잡 출처 표시 기본 api |
recipe_id | 문자열 | 레시피로 스크립트를 채울 때 script와 함께 보낼 수 없음 |
workspace_id | 문자열 | 콘솔 세션만 필수 키는 생략 |
응답은 201과 잡 객체입니다
막 접수된 잡은 queued이고 수 초 안에 빈 슬롯을 받습니다
{
"id": "job_7Kp2QmX9bT4vR1sLwZ3n",
"organization_id": "org_b1Y4kP",
"workspace_id": "wrk_Hq8sN2",
"api_key_id": "key_c3Tt9x",
"status": "queued",
"queued_reason": "capacity",
"end_reason": null,
"error": null,
"exit_code": null,
"created_at": "2026-10-12T03:10:00.000Z",
"started_at": null,
"ended_at": null,
"run_seconds": null,
"billed_minutes": null,
"rate_micro_usd_per_min": 39600,
"rate_usd_per_min": "0.039600",
"amount_micro_usd": 0,
"amount_usd": "0.000000",
"credit_low": null,
"log_url": "/api/runner/v1/jobs/job_7Kp2QmX9bT4vR1sLwZ3n/logs",
"repository": null,
"source_kind": "api"
}금액은 항상 두 필드로 옵니다
*_micro_usd는 정수 µUSD(1 USD = 1,000,000)로 계산용이고 *_usd는 같은 값의 소수 6자리 문자열로 표시용입니다
2. 잡 읽기
curl -s https://xenoci.com/api/runner/v1/jobs/job_7Kp2QmX9bT4vR1sLwZ3n \
-H "Authorization: Bearer $XENOCI_API_KEY"끝난 잡은 status가 종료 상태로 바뀌고 run_seconds, billed_minutes, amount_usd가 채워집니다
{
"id": "job_7Kp2QmX9bT4vR1sLwZ3n",
"status": "succeeded",
"end_reason": "succeeded",
"exit_code": 0,
"started_at": "2026-10-12T03:10:41.000Z",
"ended_at": "2026-10-12T03:12:46.000Z",
"run_seconds": 125,
"billed_minutes": 3,
"rate_usd_per_min": "0.039600",
"amount_usd": "0.118800"
}3. 잡 목록
curl -s "https://xenoci.com/api/runner/v1/jobs?status=failed&limit=20" \
-H "Authorization: Bearer $XENOCI_API_KEY"| 쿼리 | 설명 |
|---|---|
status | 상태 하나로 거름 |
limit | 1~100, 기본 20 |
cursor | 이전 응답의 next_cursor (잡 ID) |
응답은 { "data": [잡…], "next_cursor": "job_…" 또는 null }이고 최신순입니다
4. 로그 읽기
curl -s "https://xenoci.com/api/runner/v1/jobs/job_7Kp2QmX9bT4vR1sLwZ3n/logs?offset=0" \
-H "Authorization: Bearer $XENOCI_API_KEY" -D -본문은 text/plain이고 응답 헤더 X-Log-Next-Offset이 다음 호출의 offset, X-Log-Complete가 잡 종료 여부입니다
자세한 폴링 방법은 로그 스트리밍에 있습니다
5. 취소
curl -s -X POST https://xenoci.com/api/runner/v1/jobs/job_7Kp2QmX9bT4vR1sLwZ3n/cancel \
-H "Authorization: Bearer $XENOCI_API_KEY"대기 중이면 바로 cancelled, 실행 중이면 스크립트를 멈춘 뒤 cancelled가 되고 이미 끝난 잡은 409 conflict입니다
6. 결과물
curl -s https://xenoci.com/api/runner/v1/jobs/job_7Kp2QmX9bT4vR1sLwZ3n/artifacts \
-H "Authorization: Bearer $XENOCI_API_KEY"목록의 각 항목을 GET /v1/jobs/{job_id}/artifacts/{artifact_id}로 부르면 15분짜리 다운로드 URL로 302 리다이렉트합니다
결과물을 보세요
오류 코드
| HTTP | 코드 | 뜻 |
|---|---|---|
| 400 | invalid_request | 본문 검증 실패 details.fields에 필드 이름 |
| 401 | api_key_required, invalid_api_key, api_key_revoked_leaked | 키 없음, 폐기됨 또는 공개 유출로 자동 폐기됨 |
| 402 | insufficient_credit | 사용 가능 잔액이 1분 요금 미만 details.topup_url에 충전 링크 |
| 402 | workspace_spend_limit, tier_monthly_limit | 워크스페이스 또는 티어 월 한도 도달 details.resets_at에 해제 시각 |
| 403 | insufficient_permission, workspace_archived | 읽기 키로 쓰기 요청, 다른 워크스페이스, 보관된 워크스페이스 |
| 404 | not_found | 없는 잡이거나 이 키가 볼 수 없는 잡 |
| 409 | conflict, idempotency_conflict | 상태가 맞지 않음, 같은 키에 다른 본문 |
| 429 | tier_concurrency_limit, rate_limited | 동시 실행 한도와 대기열 모두 가득 Retry-After 헤더 |
| 503 | capacity_unavailable | 러너 용량 없음 retry_after_s 뒤 재시도 |
같은 흐름을 CLI로 하려면 다음 한 줄이면 됩니다
npx -y -p github:xeno-ci/xenocast xenocast build --script ./ci.sh --xcode 27.0 --timeout 20다음 단계
- 잡 상태와 종료 이유: 응답의
status,end_reason,error읽는 법 - 소스 올리기: 업로드와 git 소스
- 실패와 재시도: 거절과 실패를 구분해 다시 보내기