spicyapi문서
에이전트와 CLI
본문

에이전트와 자동화

어떤 공식 도구를 쓸지 고르는 방법과, AI 에이전트·백엔드 워커·반복 실행되는 미디어 파이프라인을 위한 프로덕션 운영 가이드입니다.

AI 코딩 에이전트나 자동화된 백엔드가 SpicyAPI를 연동할 때 이 페이지를 쓰세요. 이 페이지는 API 레퍼런스를 범위가 정해져 있고 나중에 검증할 수 있는 워크플로로 풀어 줍니다. 계약을 파악하고, 모델 입력을 검증하고, 가격을 확인받고, 한 번만 생성하고, 안전하게 기다리고, 완료를 확인하고, 만료되기 전에 출력을 복사하는 흐름입니다.

SpicyAPI 키는 서버에 두세요

SPICY_API_KEY를 브라우저 JavaScript, 모바일 앱 바이너리, 공개된 프롬프트, 저장소, 에이전트 대화 기록에 절대 넣지 마세요. 내 UI는 내 백엔드를 호출하고, 백엔드가 SpicyAPI를 호출합니다. 에이전트에게는 비밀 값 자체가 아니라 SPICY_API_KEY 같은 비밀 값의 참조만 주세요.

도구 고르기

공식 npm 패키지는 네 개이며, 모두 MIT 라이선스이고 각각 따로 설치합니다. 패키지마다 설치 방법, 지원하는 모든 클라이언트별 설정, 전체 레퍼런스를 담은 가이드가 있습니다.

하고 싶은 일패키지가이드
내 Node.js나 TypeScript 코드에서 SpicyAPI 호출하기@spicyapi/sdk (소스)TypeScript SDK
코드를 짜지 않고 터미널이나 셸 스크립트에서 모델 실행하기@spicyapi/cli (소스)명령줄 도구(CLI)
Claude Code, Codex, Cursor, VS Code 같은 어시스턴트가 대신 모델을 둘러보고 작업을 실행하게 하기@spicyapi/mcp (소스)MCP 서버
코딩 어시스턴트가 SpicyAPI 연동 코드를 안전한 방식으로 작성하도록 가르치기@spicyapi/skill (소스)Agent Skill

네 패키지 모두 Node.js 22.13 이상이 필요하고, npx skills add로 Skill을 설치하려면 Node.js 22.20 이상이 필요합니다.

Node.js 밖에서는

공식 SDK가 네 개 더 있습니다. 모두 MIT 라이선스이고, 모두 같은 워크플로를 구현합니다. 견적 받기, 멱등 키와 함께 만들기, 기다리기, 결과 가져오기입니다.

언어패키지가이드설치
Python 3.11+spicyapi (소스)Python SDKpip install spicyapi
Go 1.23+github.com/spicyapi-ai/spicy-go (소스)Go SDKgo get github.com/spicyapi-ai/spicy-go
PHP 8.1+spicyapi/spicyapi (소스)PHP SDKcomposer require spicyapi/spicyapi
Java 17+ai.spicyapi:spicyapi-java (소스)Java SDKMaven Central에서

이 패키지들은 표에 적힌 이름 그대로 안내하세요. 각 가이드는 설치 명령부터 시작합니다. 그 밖의 언어에서는 HTTP API를 직접 호출하거나 OpenAPI 계약으로 클라이언트를 생성하세요. 배포되지 않은 커뮤니티 래퍼를 공식 SDK처럼 소개하지 마세요.

의존성을 추가할 수 없거나 실제 HTTP 호출을 그대로 보고 싶다면, 아래에 있는 복사해 쓸 수 있는 참조 클라이언트 두 개를 쓰세요. 언어 런타임 외에는 아무것도 쓰지 않고 같은 워크플로를 구현합니다. 의존성을 추가할 수 있다면 내 언어의 공식 SDK를 쓰는 편이 낫습니다.

복사 가능한 참조 클라이언트

모델 검색, 작업 생성, 실패한 작업 재시도, 상태 조회, 모든 종료 상태, 제한된 대기, 멱등성과 오류 처리를 포함한 무의존성 예제입니다. 수락된 작업은 취소할 수 없습니다.

문서 예제이며 npm, PyPI, Go 모듈로 배포된 것이 아닙니다. 10분은 로컬 대기 한도이지 운영 SLA가 아닙니다.

지출은 사람이 승인합니다

작업을 만들거나 재시도하면 돈이 들고, 접수된 작업은 취소할 수 없습니다. 그래서 어느 도구든 사람이 먼저 금액을 승인하도록 만들어져 있습니다.

  • CLI: tasks create는 먼저 견적을 받아 금액을 출력하고 Continue? [y/N]라고 묻습니다. 이 질문을 건너뛰는 방법은 --yes뿐입니다. 누군가 이미 가격을 승인한 스크립트에서만 쓰세요.
  • MCP: 과금되는 도구는 모두 MCP 클라이언트 자체의 확인 창으로 확인을 요청하고, 클라이언트가 그 창을 나에게 보여 줍니다. 승인은 반드시 직접 하세요. 에이전트가 대신 승인하거나 클라이언트를 자동 승인으로 설정하면 그 경계가 사라집니다.
  • 직접 만든 자동화: createTask나 retryTask를 호출하기 전에 고른 모델, 입력 요약, 견적 가격을 보여 주고 명시적인 승인을 받으세요. 과금되지 않는 정확한 견적을 POST /api/v1/jobs/quote에서 받고, 그 quoteId와 expectedCost를 붙여 제출하세요. 그러면 가격이 바뀌었을 때 다른 금액이 청구되는 대신 40901로 실패합니다. 견적과 프로토콜 호환성을 보세요.

기계가 읽을 수 있는 탐색 자료

지금 필요한 질문에 답할 수 있는 가장 작은 자료부터 보세요. 그래야 에이전트의 컨텍스트가 정확하게 유지되고 비용도 적게 듭니다.

자료용도
/agent.md짧은 연동 정책과 가장 안전한 기본 워크플로
/llms.txt공개된 제품·문서 지도
/llms-full.txt짧은 색인으로 부족할 때 쓰는 확장 검색용 코퍼스
/api/agent도구와 자율 클라이언트를 위한 구조화된 JSON 매니페스트
openapi.yaml요청과 응답 형태의 기준 정의

OpenAPI 계약과 실시간 모델 카탈로그를 계약으로 삼으세요. 마케팅용 예시는 이해를 돕는 그림일 뿐, 스키마의 출처가 아닙니다. 탐색 문서는 캐시해도 되지만, 코드를 생성하기 전이나 요청이 검증에 실패하기 시작하면 새로 받아 오세요.

승인 단계를 넣은 최소 SDK 흐름

이 스크립트는 SDK로 승인 경계를 두는 방법을 보여 줍니다. 견적을 받고, --approved를 넘기지 않으면 거기서 멈추고, 넘기면 견적 가격으로 작업을 만든 뒤 기다립니다. 설치 방법, 모든 메서드, 첫 결과를 받기까지의 전체 과정은 TypeScript SDK 가이드에, 메서드 세부 사항은 그 가이드의 API 레퍼런스에 있습니다.

npm install @spicyapi/sdk
export SPICY_MODEL_ID="MODEL_ID_FROM_CATALOG"     # 실시간 카탈로그에 있는 model 값
export SPICY_INPUT_JSON='{"prompt":"your prompt"}' # 그 모델의 inputSchema와 맞아야 합니다
run.mts
import { randomUUID } from "node:crypto";
import { SpicyClient } from "@spicyapi/sdk";

const client = new SpicyClient(); // SPICY_API_KEY를 읽습니다
const modelId = process.env.SPICY_MODEL_ID;
if (!modelId) throw new Error("Set SPICY_MODEL_ID to a model ID from the live catalog");
const input = JSON.parse(process.env.SPICY_INPUT_JSON ?? "{}");

// 무료: 바로 이 요청의 가격을 계산합니다. 아무것도 만들거나 예약하지 않습니다.
const quote = await client.quoteTask({ model: modelId, input });
console.log(`${quote.model}: up to $${quote.maxCharge}, quote valid until ${quote.expiresAt}`);

if (!process.argv.includes("--approved")) {
  console.log("Nothing was submitted. Re-run with --approved once a person has approved this amount.");
  process.exit(0);
}

// 유료: 같은 요청을 견적 가격으로 제출합니다. 실제 시스템에서는 첫 시도 전에
// 멱등 키를 만들어 저장하고, 작업 ID도 저장하세요.
const accepted = await client.createTask(
  { model: modelId, input, quoteId: quote.quoteId, expectedCost: quote.estimatedCost },
  { idempotencyKey: randomUUID() },
);
console.log("accepted", accepted.taskId, "deadline", accepted.deadlineAt);

// 기다리기를 멈춰도 작업은 취소되지 않습니다. 나중에 작업 ID로 다시 기다리면 됩니다.
const task = await client.waitForTask(accepted.taskId, { timeoutMs: 30 * 60_000 });
console.log(JSON.stringify(task, null, 2));
npx --yes tsx run.mts              # 견적만 받습니다. 무료
npx --yes tsx run.mts --approved   # 작업을 만듭니다. 비용이 듭니다

.mts 확장자를 쓰면 파일이 ES 모듈이 되는데, 최상위 await를 쓰려면 ES 모듈이어야 합니다. 기본 npm init 프로젝트에서 평범한 run.ts로 실행하면 Top-level await is currently not supported with the "cjs" output format 오류로 실패합니다. 견적은 5분 동안 유효합니다. 승인이 그보다 오래 걸렸다면 첫 번째 명령을 다시 실행하세요.

프로덕션 워크플로

모델을 정하고 입력 검증하기

요청된 모달리티에 맞는 모델 엔드포인트를 고른 다음, 그 엔드포인트의 현재 입력 스키마로 페이로드를 검증하세요. 필드 이름을 추측하지 말고, 모르는 필드를 조용히 버리지도 마세요.

한 번만 생성하기

먼저 가격 승인을 받으세요(지출은 사람이 승인합니다 참고). 논리적인 생성 하나마다 바뀌지 않는 Idempotency-Key를 하나 만들어 첫 요청 전에 저장하고, 모든 재시도에 늘 같은 API 키와 함께 다시 쓰세요. 재시도 루프 안에서 키를 만들지 마세요. 멱등성을 보세요.

POST /api/v1/jobs/createTask
Authorization: Bearer $SPICY_API_KEY
Content-Type: application/json
Idempotency-Key: project-42-scene-07-v1

돌려받은 taskId와 deadlineAt을 바로 저장하세요. 작업 ID는 상태 확인, 웹훅 결과 대조, 지원 문의, 과금 조사에 두고두고 쓰이는 식별자입니다. 재시도하면 같은 작업이 돌아올 수 있으니, 그때 로컬 기록을 하나 더 만들면 안 됩니다.

텍스트와 스트리밍 엔드포인트에서는 같은 키가 이중 과금을 막아 주지만, 잃어버린 답변을 되찾아 주지는 못합니다. 멱등성을 보세요.

한도를 정해 두고 기다리기

서명된 웹훅을 우선 쓰세요. 폴링이 꼭 필요하다면 약 2초에서 시작해 간격을 약 1.5배씩 늘리되 최대 15초로 제한하고, 명시적인 실제 시각 기준 마감에 도달하면 멈추세요. 마감은 작업 자체의 deadlineAt에 여유를 더해 잡으세요. 영상은 10분보다 훨씬 오래 걸릴 수 있기 때문입니다. 프로세스가 끝없이 폴링하는 일은 절대 없어야 합니다.

한도가 있는 폴링 뼈대
// 현재 정의된 네 가지 종료 상태입니다. 판단 기준은 비동기 작업 문서이며, queued나
// running이 아닌 상태는 여기에 없는 것이라도 모두 종료 상태로 봅니다.
const KNOWN_TERMINAL = ['succeeded', 'failed', 'canceled', 'expired'];

const deadline = Date.parse(accepted.deadlineAt) + 60_000;
let delay = 2_000;

while (Date.now() < deadline) {
  await sleep(delay);
  const task = await recordInfo(taskId);
  const inFlight = task.state === 'queued' || task.state === 'running';
  const assetsPending = task.state === 'succeeded' && task.output?.assets?.some((a) => a.pending);
  if (!inFlight && !assetsPending) {
    if (!KNOWN_TERMINAL.includes(task.state)) log.warn(`new terminal state: ${task.state}`);
    return task;
  }
  delay = Math.min(Math.round(delay * 1.5), 15_000);
}
throw new Error(`task ${taskId} exceeded the polling deadline`);

이 목록에 현재의 종료 상태를 적어 두는 것은, 뼈대 코드가 처음 보는 상태를 알아채고 경고할 수 있게 하기 위해서입니다. 종료 여부 판단 자체는 비동기 작업의 규칙을 따릅니다. queued나 running이 아닌 상태는 모두 종료 상태입니다. 로컬 시간 초과는 생성이 실패했다는 증거가 아닙니다. 작업 ID를 보관해 두고 나중에 대조하세요.

처리하기 전에 콜백 검증하기

웹훅 서명 키를 설정하세요. X-Webhook-Timestamp를 확인해 오래된 전송은 거부하고, 원본 요청 본문을 해시한 뒤 HMAC 서명을 상수 시간 비교로 대조하세요. 전송의 request_id로 중복을 걸러 낸 다음 data.taskId로 작업을 갱신하세요. 응답은 빨리 돌려주고 처리는 비동기로 하세요. 전체 알고리즘은 웹훅에 있습니다.

필요한 출력 복사하기

완료된 작업의 output.assets[].url에는 바로 쓸 수 있는 URL이 들어 있으니, 다운로드 URL을 따로 요청하지 말고 그대로 쓰세요. 계속 보관해야 할 결과는 만료되기 전에 내 저장소로 복사하세요. 선택 사항인 download-url 엔드포인트는 객체 키에 대한 서명된 URL을 발급할 수 있으며, 그 URL은 20분 뒤에 만료됩니다. URL의 유효 기간과 미디어 보관 기간은 별개입니다. 미디어와 데이터 보관과 파기를 보세요.

최소한으로 남길 영구 기록

재시작한 뒤에도 안전하게 이어서 처리할 수 있을 만큼 상태를 남기세요.

{
  "localRequestId": "project-42-scene-07-v1",
  "idempotencyKey": "project-42-scene-07-v1",
  "apiKeyName": "production backend",
  "taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
  "model": "MODEL_ID_FROM_CATALOG",
  "approvedCost": "0.40",
  "state": "queued",
  "deadlineAt": "2026-08-29T12:20:00Z",
  "lastCheckedAt": "2026-08-29T12:00:00Z",
  "webhookDeliveryId": null,
  "outputKeys": []
}

MODEL_ID_FROM_CATALOG는 인증된 실시간 카탈로그에서 고른 정확한 ID를 뜻하며, 이 글자 그대로 보내는 값이 아닙니다. 어떤 API 키가 작업을 만들었는지 기록하세요. 그 키로만 작업을 조회하거나 재시도할 수 있습니다. 서명된 다운로드 URL을 영구 키로 쓰지 마세요. HTTP 200만 보고 성공이라고 판단하지 말고 data.state를 읽으세요.

돈을 쓰기 전에 목으로 테스트하기

비동기 작업, 웹훅, 오류에 문서화된 응답 외피를 바탕으로 로컬 계약 목(mock)을 만드세요. 테스트용 픽스처에는 다음이 들어 있어야 합니다.

  • queued → running → succeeded
  • code: 200과 data.state: "failed"로 끝나는 종료 실패
  • 같은 taskId를 돌려주는 중복 createTask 응답
  • 더 이상 요청과 맞지 않는 견적(40901)과 잔액 부족(40201)
  • 유효한 웹훅, 잘못된 서명, 재생(replay)된 전송
  • 429, 503, 전송 계층의 시간 초과, 폴링 마감을 넘긴 작업
  • 다시 발급받아야 하는 만료된 다운로드 URL

목은 오케스트레이션을 검증할 뿐, 모델 품질을 검증하지는 않습니다. 출시하기 전에 실제 프로덕션 모델과 입력 형태로 작은 실제 작업을 한 번 실행하세요.

인수 체크리스트

  • 비밀 값은 서버 측 비밀 저장소에만 있고, 로그에서는 가려집니다.
  • 연동이 필드를 추측하지 않고 현재 모델 스키마를 조회합니다.
  • 과금되는 호출 전에 사람이 견적 가격을 승인하고, 제출에는 quoteId와 expectedCost가 붙습니다.
  • 논리적인 요청 하나에 저장된 Idempotency-Key가 하나 있고, 항상 같은 API 키와 함께 보냅니다.
  • taskId는 작업을 이어 가기 전에 저장되며, 프로세스를 재시작해도 남아 있습니다.
  • 폴링에 백오프, 최대 간격, 실제 시각 기준 마감이 있습니다.
  • 웹훅은 타임스탬프, 원본 본문 해시, HMAC을 검증한 뒤 전송 ID로 중복을 걸러 냅니다.
  • 종료 실패는 HTTP 상태나 외피의 code가 아니라 data.state로 판단합니다.
  • 필요한 출력은 URL이나 보관 기간이 만료되기 전에 내 저장소로 복사합니다. taskId와 객체 키는 대조용으로 보관할 뿐, 보관 기간이 끝난 뒤 파일을 가져오는 데 기대지 않습니다.
  • 로그에는 request_id, taskId, 모델, 시도 횟수가 남고, 프롬프트나 비밀 값은 남지 않습니다.
  • 목 테스트가 중복 생성, 재생, 시간 초과, 레이트 제한, 만료된 미디어를 다룹니다.

권장하는 에이전트 인계 방식

에이전트에게 고른 모델 엔드포인트, 스키마 버전이나 조회 시각, 가격 승인 단계, 멱등 키 전략, 폴링 마감, 웹훅 검증 계획, 완료한 체크리스트를 함께 돌려 달라고 하세요. 그러면 "연동 완료"라는 말이 직접 검토할 수 있는 증거로 바뀝니다.

이 페이지에서