IDEA-MVP API
아이디어 한 줄을 보내면 트렌드 수집 → 기술 분석 → 다관점 정제 → MVP 제안서까지 만들어 돌려줍니다. 결과는 JSON · HTML · PDF 로 받을 수 있습니다.
curl -s https://idea.socialproject.net/developers.md
로 받으면 요약 없이 그대로 읽힙니다. 폴링 연동 순서와 Temporal 예시가 들어 있습니다.개요
| Base URL | https://idea.socialproject.net |
|---|---|
| 인증 | OAuth 2.0 client_credentials — client_id·client_secret → access token → Authorization: Bearer … |
| 형식 | 요청·응답 JSON (UTF-8). 시간은 ISO 8601 (UTC). |
POST /oauth/tokenPOST /api/v1/jobsGET /api/v1/jobs/{id} — 보통 5~15분GET /api/v1/mvps/{id}/export1. 클라이언트 발급
서비스에 로그인한 뒤 오른쪽 위 ⚙ 설정 › API 클라이언트에서 이름을 적고 발급을 누릅니다.
client_secret은 발급할 때 한 번만 보여줍니다. 서버에는 해시만 남아 다시 볼 수 없습니다 — 잃어버리면 secret 재발급.- scope —
read(결과 조회) ·write(분석·생성 실행, LLM 을 씀). - 비활성화·삭제·secret 재발급을 하면 이미 받은 토큰도 즉시 막힙니다.
- API 로 만든 MVP 는 발급한 사람의 계정에 저장되어 화면의 “저장된 MVP”에도 보입니다.
2. 토큰 받기
JSON · application/x-www-form-urlencoded · HTTP Basic(client_id:client_secret) 어느 것이든 받습니다.
| 필드 | 필수 | 설명 |
|---|---|---|
grant_type | 예 | client_credentials |
client_id | 예 | idc_… |
client_secret | 예 | ids_… |
scope | 공백 구분. 생략하면 클라이언트에 허용된 전부. |
curl -X POST https://idea.socialproject.net/oauth/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"client_credentials","client_id":"idc_...","client_secret":"ids_..."}'
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read write"
}
토큰은 expires_in초(기본 1시간) 동안 씁니다. 매 요청마다 새로 받지 말고, 만료 1분 전쯤 다시 받으세요.
401 token_expired 가 오면 새로 받아 한 번만 재시도하면 됩니다.
3. 분석 시작
| 필드 | 필수 | 설명 |
|---|---|---|
prompt | 예 | 만들고 싶은 서비스 · 관심 기술 · 풀고 싶은 문제. 자유 문장, 4,000자 이하. 구체적일수록 결과가 주제를 벗어나지 않습니다. |
title | MVP 제목 (없으면 결과 제안서 제목으로 채움) | |
domain | 도메인 힌트 (예: 헬스케어) |
curl -X POST https://idea.socialproject.net/api/v1/jobs \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"prompt":"동네 소상공인이 재고와 단골 고객을 함께 관리하는 AI 도우미"}'
202 Accepted — 분석은 뒤에서 돕니다. 돌려받은 id 가 작업 ID 이자 결과(MVP) ID 입니다.
{
"id": "a1b2c3d4",
"status": "queued",
"query": "inventory management small business AI assistant",
"progress": null,
"error": null,
"created_at": "2026-09-23T02:10:00+00:00",
"links": {
"job": ".../api/v1/jobs/a1b2c3d4",
"result": ".../api/v1/mvps/a1b2c3d4",
"html": ".../api/v1/mvps/a1b2c3d4/export?format=html",
"pdf": ".../api/v1/mvps/a1b2c3d4/export?format=pdf"
}
}
4. 진행 확인
보통 5~15분 걸립니다. 10~20초 간격으로 확인하세요 (더 자주 봐도 빨라지지 않습니다).
| status | 뜻 |
|---|---|
queued | 대기 중 |
running | 진행 중 — progress.percent · progress.phase_label · progress.message · progress.estimated_remaining |
done | 완료 — 결과를 받으세요 |
failed | 실패 — error 에 이유. 같은 prompt 로 다시 시작할 수 있습니다. |
{ "id": "a1b2c3d4", "status": "running",
"progress": { "percent": 45, "phase": "tech_discovery", "phase_label": "혁신 패턴 발견",
"message": "…", "estimated_remaining": "약 6분 12초 남음" }, … }
5. 결과 받기
끝나기 전에 부르면 409 not_ready. ?include=raw 를 붙이면 내부 파이프라인 원본(수백 KB)도 함께 옵니다.
{
"id": "a1b2c3d4",
"title": "단골노트 — 소상공인 재고·고객 AI 도우미",
"status": "done",
"query": "…",
"proposal": { // 최종 제안서 — 아래 필드는 결과마다 조금씩 다를 수 있음
"title": "…", "problem": "…", "solution": "…",
"target_market": "…", "revenue_model": "…", "competitive_advantage": "…",
"mvp_features": ["…"], "tech_stack": ["…"], "biz_architecture": { … }
},
"score": { "innovation": 8, "feasibility": 7, "overall_score": 7.6 }, // 다관점 정제를 거친 경우
"ideas": [ { "title": "…", "problem": "…", … } ], // 도출된 아이디어 전체
"has_mockup": false,
"has_patent": false,
"links": { … }
}
6. JSON · HTML · PDF 로 받기
| doc | json | html | |
|---|---|---|---|
proposal (기본) 최종 제안서 | ✓ | ✓ 인쇄용 문서 | ✓ A4 |
patent 특허 명세서 초안 | ✓ {html} | ✓ | ✓ A4 |
mockup 화면 목업 | ✓ {html} | ✓ 인터랙티브 | — (HTML 로) |
# PDF 파일로 저장
curl -L -o proposal.pdf -H "Authorization: Bearer $TOKEN" \
"https://idea.socialproject.net/api/v1/mvps/a1b2c3d4/export?format=pdf"
# HTML
curl -H "Authorization: Bearer $TOKEN" \
"https://idea.socialproject.net/api/v1/mvps/a1b2c3d4/export?format=html&doc=proposal" > proposal.html
PDF 는 Content-Type: application/pdf, 파일 이름은 mvp-{id}-{doc}.pdf 입니다.
patent·mockup 은 먼저 7번으로 만들어야 합니다 (없으면 404).
7. 화면 목업 · 특허 초안 만들기
응답이 올 때까지 기다립니다 (HTTP 타임아웃을 2분 이상으로). 이미 만든 게 있으면 LLM 을 다시 부르지 않고
그대로 돌려줍니다("generated": false). 새로 만들려면 {"regenerate": true}.
{ "id": "a1b2c3d4", "doc": "patent", "generated": true, "html": "<!doctype html>…",
"links": { "html": ".../export?format=html&doc=patent" } }
8. 목록 · 내 정보
화면에서 만든 것까지 포함해 이 계정의 MVP 목록 (최신순). {items, total, has_more}
토큰이 누구 것인지, 오늘 쓴 양과 한도.
{ "client_id": "idc_…", "name": "사내 기획 봇", "owner": "me@example.com", "scopes": ["read","write"],
"limits": { "daily_llm_jobs": 30, "used_today": 3, "max_concurrent_jobs": 2 } }
오류와 한도
오류는 모두 같은 모양입니다 — {"error": {"code": "…", "message": "…"}}.
/oauth/token 만 OAuth 표준 모양 {"error": "invalid_client", "error_description": "…"}.
| HTTP | code | 할 일 |
|---|---|---|
| 400 | invalid_request · unsupported_grant_type · invalid_scope | 요청을 고치세요. 재시도해도 같습니다. |
| 401 | invalid_client | client_id / secret 확인, 비활성화 여부 확인 |
| 401 | token_expired · invalid_token | 토큰을 새로 받아 한 번 재시도 |
| 403 | insufficient_scope | 클라이언트에 write scope 추가 |
| 404 | not_found | ID 확인 (다른 계정의 MVP 는 보이지 않습니다) |
| 409 | not_ready | 분석이 끝나지 않았습니다 — 4번으로 기다리세요 |
| 429 | too_many_jobs | 동시 실행 한도 — 진행 중인 작업이 끝난 뒤 |
| 429 | rate_limited | 하루 한도 소진 — UTC 자정(한국 09:00)에 초기화 |
| 502 · 503 | generation_failed · server_error | 잠시 뒤 재시도 (LLM 공급자 문제일 수 있음) |
한도
분석 한 건은 LLM 을 수십 번 부릅니다. 비용이 새지 않도록 클라이언트마다 다음을 막습니다.
| 항목 | 기본값 |
|---|---|
| 동시 실행 분석 | 2건 |
| 하루 LLM 작업 (분석 + 목업 + 특허 생성 합산) | 30건 |
| 이미 만든 목업·특허 다시 받기, 결과·목록 조회, 내보내기 | 한도에 안 셈 |
전체 예제
import os, time, requests
BASE = "https://idea.socialproject.net"
CID, SECRET = os.environ["IDEA_CLIENT_ID"], os.environ["IDEA_CLIENT_SECRET"]
def token():
r = requests.post(f"{BASE}/oauth/token", json={
"grant_type": "client_credentials", "client_id": CID, "client_secret": SECRET})
r.raise_for_status()
return r.json()["access_token"]
H = {"Authorization": f"Bearer {token()}"}
job = requests.post(f"{BASE}/api/v1/jobs", headers=H,
json={"prompt": "동네 소상공인이 재고와 단골 고객을 함께 관리하는 AI 도우미"}).json()
print("작업", job["id"])
while True:
j = requests.get(f"{BASE}/api/v1/jobs/{job['id']}", headers=H).json()
if j["status"] in ("done", "failed"):
break
p = j.get("progress") or {}
print(j["status"], p.get("percent"), p.get("phase_label"))
time.sleep(15)
if j["status"] == "failed":
raise SystemExit(j["error"])
mvp = requests.get(f"{BASE}/api/v1/mvps/{job['id']}", headers=H).json()
print(mvp["proposal"]["title"])
pdf = requests.get(f"{BASE}/api/v1/mvps/{job['id']}/export", headers=H, params={"format": "pdf"})
open("proposal.pdf", "wb").write(pdf.content)
const BASE = "https://idea.socialproject.net";
const { IDEA_CLIENT_ID: cid, IDEA_CLIENT_SECRET: secret } = process.env;
const tok = await fetch(`${BASE}/oauth/token`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ grant_type: "client_credentials", client_id: cid, client_secret: secret }),
}).then(r => r.json());
const H = { Authorization: `Bearer ${tok.access_token}`, "Content-Type": "application/json" };
const job = await fetch(`${BASE}/api/v1/jobs`, {
method: "POST", headers: H,
body: JSON.stringify({ prompt: "동네 소상공인이 재고와 단골 고객을 함께 관리하는 AI 도우미" }),
}).then(r => r.json());
let j;
do {
await new Promise(r => setTimeout(r, 15000));
j = await fetch(`${BASE}/api/v1/jobs/${job.id}`, { headers: H }).then(r => r.json());
console.log(j.status, j.progress?.percent ?? "");
} while (!["done", "failed"].includes(j.status));
const html = await fetch(`${BASE}/api/v1/mvps/${job.id}/export?format=html`, { headers: H }).then(r => r.text());
console.log(html.length, "bytes");