계정
누구인지, 얼마가 남았는지, 굽는 기계가 살아 있는지.
응답
{
"success": true,
"data": {
"id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
"email": "[email protected]",
"role": "user",
"plan": "starter",
"pixo_balance": 320,
"pixo_expires_at": "2026-11-28T02:10:00.000Z",
"pixo_subscription": 120,
"pixo_subscription_resets_at": "2026-10-04T09:00:00.000Z",
"pixo_purchased": 200,
"created_at": "2026-07-02T11:20:04.118Z"
}
}
GET/catalog인증 불필요
고를 수 있는 값 전부
코드에 상수로 박지 마세요. 종류가 늘면 이 응답이 같이 늘고, 화면에도 이 페이지에도 여기서 온 값만 그립니다. 아래 값 사전이 지금 이 엔드포인트의 응답을 그린 것입니다.
응답
{
"success": true,
"data": {
"kinds": [
{
"id": "character",
"aspects": ["square", "tall"],
"poses": ["idle", "walk", "attack"],
"animatable": true,
"cell_sizes": [64, 96, 128, 192, 256, 384, 512],
"default_cell_size": 128,
"outlines": ["off", "light", "normal", "strong"],
"scale_axis": "height",
"views": ["front", "back", "left", "right"],
"shots": ["full", "bust", "face"],
"generate_cost": 4
}
],
"poses": [{ "id": "walk", "frames": 4, "fps": 8 }],
"aspects": [
{
"id": "tall",
"cell_width": 3,
"cell_height": 4,
"frames": 1,
"cell_sizes": null,
"default_cell_size": null
}
],
"cell_sizes": [64, 96, 128, 192, 256, 384, 512],
"palette_sizes": [8, 16, 24, 32, 48],
"outlines": ["off", "light", "normal", "strong"],
"subject_scales": [70, 80, 90, 100],
"defaults": {
"kind": "character",
"aspect": "square",
"cell_size": 128,
"palette_size": 24,
"outline": "normal"
}
}
}
GET/system/status
러너가 살아 있나 · 큐에 몇 개 있나
잡이 안 움직일 때 우리 쪽인지 내 쪽인지 가르는 값입니다. runner_seen_at 이 한참 전이면 굽는 기계가 멈춘 것이고, 그 판정은 클라이언트가 합니다 — 그래서 서버 시각(now)을 같이 줍니다. 기기 시계가 몇 분씩 어긋난 경우가 실제로 있습니다.
응답
{
"success": true,
"data": {
"runner_seen_at": "2026-08-13T11:29:54.207Z",
"queued": 2,
"processing": 1,
"now": "2026-08-13T11:29:58.664Z"
}
}
프로젝트
에셋을 담는 그릇이자 스타일 계약이 붙는 자리입니다. 같은 프로젝트에서 만든 것은 같은 아트 스타일·색 수·테두리·피사체 크기를 물려받습니다.
기본 프로젝트가 맨 앞입니다. 안 고르면 에셋이 거기로 들어가는 자리라, 목록에서도 그 자리에 있어야 헷갈리지 않습니다. 가입할 때 하나가 자동으로 만들어지므로 목록이 비는 일은 없습니다.
응답
{
"success": true,
"data": {
"projects": [
{
"id": "b52d7f36-8e01-4c94-a6b3-19f8d0e47c25",
"name": "기본 프로젝트",
"color": "gray",
"style_contract": { "subjectScale": 100 },
"base_prompt": null,
"is_default": true,
"asset_count": 12,
"created_at": "2026-07-02T11:20:04.118Z",
"updated_at": "2026-07-02T11:20:04.118Z"
}
]
}
}
빌드 스크립트가 “이번 게임의 에셋” 을 한 그릇에 모으고 그 그릇에 아트 스타일을 걸어 두는 자리입니다. 만든 뒤 POST /assets 에 project_id 를 실으면 그 프로젝트로 들어가고, 계약도 같이 걸립니다.
본문
| 타입 | 설명 |
|---|
name필수 | string | 80자까지 |
color | enum | rose · orange · amber · lime · emerald · teal · sky · indigo · violet · gray. 안 주면 gray. 목록에서 점으로만 쓰이고 그림에는 영향이 없습니다 |
base_prompt | string | 이 프로젝트의 모든 프롬프트 앞에 붙는 공통 첫 문장. 500자까지 |
style_contract | object | 아래 표 |
| 키 | 값 | 어디로 가나 |
|---|
artStyle | 문자열 300자 | 프롬프트 |
notes | 문자열 300자 | 프롬프트 |
paletteSize | 값 사전 | 후처리 |
outline | 값 사전 | 후처리 |
subjectScale | 70 · 80 · 90 · 100 | 후처리 |
- ·모르는 키는 버립니다. jsonb 라 무엇이든 넣을 수 있는데, 정체 모를 키는 프롬프트로 갈지 후처리로 갈지 판단할 근거가 없습니다 — 그대로 두면 유저가 쓴 적 없는 지시가 모델에게 갑니다
- ·
subjectScale 은 요청이 덮어쓸 수 없습니다. 이 값이 하는 일이 “이 프로젝트의 에셋은 서로 같은 크기다” 라서, 한 장만 다르게 고를 수 있으면 보장이 아니라 기본값이 됩니다. 안 주면 만들 때 100 이 붙습니다 - ·
paletteSize·outline 은 요청이 덮어쓸 수 있습니다. 지금 고른 것이 더 구체적이니까요
요청
curl -X POST "https://api.dotforge.cc/v1/projects" \
-H "Authorization: Bearer $DOTFORGE_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "던전 크롤러",
"color": "violet",
"base_prompt": "어두운 지하 던전의 색조",
"style_contract": {
"artStyle": "16비트 SNES 도트",
"paletteSize": 24,
"outline": "normal",
"subjectScale": 90
}
}'
응답
{
"success": true,
"data": {
"id": "b52d7f36-8e01-4c94-a6b3-19f8d0e47c25",
"name": "던전 크롤러",
"color": "violet",
"style_contract": {
"artStyle": "16비트 SNES 도트",
"paletteSize": 24,
"outline": "normal",
"subjectScale": 90
},
"base_prompt": "어두운 지하 던전의 색조",
"is_default": false,
"created_at": "2026-08-13T11:40:22.017Z",
"updated_at": "2026-08-13T11:40:22.017Z"
}
}
PATCH/projects/:id
이름 · 색 · 공통 문장 · 스타일 계약
style_contract 는 병합이 아니라 통째로 갈아끼웁니다. 한 키만 바꾸려면 나머지도 같이 보내세요 — 안 그러면 빠뜨린 키가 지워집니다. {"paletteSize":32} 만 보내면 artStyle 도 outline 도 사라집니다.
특히 subjectScale 을 빠뜨리지 마세요. 이 값만은 요청으로 되돌릴 수 없어서, 여기서 지워지면 그 프로젝트는 피사체 크기를 안 맞추는 상태로 남고 다시 PATCH 하기 전까지 그대로입니다. 계약을 고쳐도 이미 만들어진 그림은 그대로입니다; 다음에 굽는 것부터 새 계약을 따릅니다.
본문
| 타입 | 설명 |
|---|
name | string | 빈 문자열은 거절됩니다 |
color | enum | 없는 색은 거절됩니다 |
base_prompt | string | null | 빈 문자열을 주면 지웁니다 |
style_contract | object | 키는 POST /projects 와 같습니다. {} 를 주면 계약이 비고, 그 프로젝트의 에셋은 기본값으로 굽습니다 |
요청
curl -X PATCH "https://api.dotforge.cc/v1/projects/b52d7f36-8e01-4c94-a6b3-19f8d0e47c25" \
-H "Authorization: Bearer $DOTFORGE_KEY" \
-H "Content-Type: application/json" \
-d '{"style_contract":{"artStyle":"16비트 SNES 도트","paletteSize":32,"subjectScale":90}}'
응답
{
"success": true,
"data": {
"id": "b52d7f36-8e01-4c94-a6b3-19f8d0e47c25",
"name": "던전 크롤러",
"color": "violet",
"style_contract": {
"artStyle": "16비트 SNES 도트",
"paletteSize": 32,
"subjectScale": 90
},
"base_prompt": "어두운 지하 던전의 색조",
"is_default": false,
"updated_at": "2026-08-13T11:42:09.554Z"
}
}
DELETE/projects/:id
프로젝트만 지운다
안의 에셋은 사라지지 않습니다. 지우기 직전에 기본 프로젝트로 옮겨집니다 — 그릇을 치우는 것이지 그림을 지우는 것이 아닙니다. 그림을 지우려면 DELETE /assets/:id 를 하나씩 부르세요.
기본 프로젝트(is_default: true)는 지울 수 없습니다 — 이관 대상이 사라지면 남은 에셋이 갈 곳이 없어집니다.
응답
{
"success": true,
"data": { "deleted": true }
}
에셋
에셋은 메타데이터고(“버섯 전사”) 그림은 그 안에 쌓입니다. 리롤도 반전도 시트도 전부 같은 에셋의 이미지 한 줄입니다.
카드 한 장이 에셋 하나입니다. 이미지를 전부 싣지 않고 커버 한 장과 개수만 옵니다 — 그림이 필요하면 GET /assets/:id 입니다.
쿼리
| 타입 | 설명 |
|---|
limit | number | 기본 50, 최대 100 |
favorite | "true" | 즐겨찾기만 |
project_id | uuid | 안 주면 전체 프로젝트를 합쳐서 봅니다 |
요청
curl "https://api.dotforge.cc/v1/assets?limit=20&favorite=true" \
-H "Authorization: Bearer $DOTFORGE_KEY"
응답
{
"success": true,
"data": {
"assets": [
{
"id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
"project_id": "…",
"name": "버섯 전사",
"prompt": "버섯 갑옷을 입은 작은 전사",
"kind": "character",
"cell_size": 128,
"aspect": "tall",
"cover_image_id": "7c4e9a02-15bd-4a86-8f31-2d90e5b7c184",
"is_favorite": true,
"uses_project_style": true,
"created_at": "2026-08-12T09:03:11.402Z",
"updated_at": "2026-08-12T09:04:52.881Z",
"image_count": 4,
"active_job_count": 1,
"cover_url": "https://… (5분)"
}
],
"count": 1
}
}
POST/assets4 Pixo
에셋을 만들고 첫 그림을 굽는다
두 단계로 나누지 않습니다. “에셋 만들기” 가 따로 있으면 그림 한 장을 보기 전에 이름부터 지어야 하는데, 아직 뭐가 나올지 모르는 시점입니다. 에셋 행과 잡이 같이 생깁니다 — 잔액이 모자라 잡이 서지 못하면 만들던 에셋도 지웁니다.
본문
| 타입 | 설명 |
|---|
prompt필수 | string | 1000자까지 |
kind | enum | 안 주면 character. 값 사전 |
aspect | enum | 에셋이 소유합니다 — 여기서 한 번 고르고, 이후 요청에는 싣지 않습니다. 그 종류가 못 쓰는 값이면 첫 번째 값으로 떨어집니다 |
cell_size | number | 종류와 비율이 같이 정합니다. 못 고르는 값은 기본값으로 떨어집니다 |
name | string | 안 주면 프롬프트 앞 80자 |
project_id | uuid | 안 주면 기본 프로젝트 |
use_project_style | boolean | 프로젝트 스타일 계약을 따를지. false 를 명시할 때만 끕니다. 이 값은 에셋에 남아 리롤에도 따라갑니다 |
palette_size | number | 색 수. 안 주면 프로젝트 계약, 계약에도 없으면 기본값. 고를 수 있는 값은 값 사전 |
outline | string | 테두리 세기. 종류가 테두리를 안 쓰면 무엇을 줘도 off |
요청
curl -X POST "https://api.dotforge.cc/v1/assets" \
-H "Authorization: Bearer $DOTFORGE_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "버섯 갑옷을 입은 작은 전사",
"kind": "character",
"aspect": "tall",
"cell_size": 128
}'
응답
{
"success": true,
"data": {
"asset": {
"id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
"name": "버섯 갑옷을 입은 작은 전사",
"kind": "character",
"cell_size": 128,
"aspect": "tall",
"cover_image_id": null,
"is_favorite": false,
"uses_project_style": true,
"created_at": "2026-08-12T09:03:11.402Z",
"updated_at": "2026-08-12T09:03:11.402Z"
},
"job_id": "3ab61f58-70c2-4d19-9e45-8b0fa2c61d77",
"status": "pending",
"pixo_cost": 4
}
}
GET/assets/:id
에셋 + 그림 전부 + 진행 중인 잡
images[] 는 오래된 순입니다 — 첫 칸이 유저가 처음 본 그림 입니다. preview_url 은 5분짜리라 저장해 두면 안 되고, 받아 갈 때는 /images/:id/download 를 부릅니다.
응답
{
"success": true,
"data": {
"id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
"name": "버섯 전사",
"prompt": "버섯 갑옷을 입은 작은 전사",
"kind": "character",
"cell_size": 128,
"aspect": "tall",
"cover_image_id": "7c4e9a02-15bd-4a86-8f31-2d90e5b7c184",
"is_favorite": false,
"uses_project_style": true,
"images": [
{
"id": "7c4e9a02-15bd-4a86-8f31-2d90e5b7c184",
"asset_id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
"op": "generate",
"parent_image_id": null,
"label": null,
"palette": [{ "hex": "#3b2a4d", "count": 812 }],
"width": 96,
"height": 128,
"frames": 1,
"meta": {},
"pixo_cost": 4,
"preview_url": "https://… (5분)",
"resizable": true,
"source_expires_at": "2026-09-11T09:04:52.881Z",
"expires_at": "2026-11-10T09:04:52.881Z",
"created_at": "2026-08-12T09:04:52.881Z"
}
],
"active_jobs": [
{
"id": "3ab61f58-70c2-4d19-9e45-8b0fa2c61d77",
"op": "animate",
"status": "processing",
"progress": 40,
"progress_note": "walk 행 굽는 중",
"error_message": null,
"created_at": "2026-08-12T09:10:02.114Z"
}
],
"animatable": true,
"poses": ["idle", "walk", "attack"]
}
}
PATCH/assets/:id
이름 · 커버 · 즐겨찾기 · 프로젝트
하나 이상 주세요 — 빈 요청은 VALIDATION_ERROR 입니다. 프로젝트를 옮기면 이후 리롤·애니메이션이 새 프로젝트의 계약을 따릅니다.이미 만들어진 그림은 그대로입니다 — 옮겼다고 다시 굽지 않습니다.
본문
| 타입 | 설명 |
|---|
name | string | 빈 문자열은 거절됩니다 |
is_favorite | boolean | |
cover_image_id | uuid | 이 에셋의 이미지여야 합니다 |
project_id | uuid | 다른 프로젝트로 옮깁니다 |
use_project_style | boolean | 만든 뒤에도 켜고 끌 수 있습니다 |
요청
curl -X PATCH "https://api.dotforge.cc/v1/assets/0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32" \
-H "Authorization: Bearer $DOTFORGE_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"버섯 전사","is_favorite":true}'
응답
{
"success": true,
"data": {
"id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
"name": "버섯 전사",
"is_favorite": true,
"updated_at": "2026-08-13T11:31:02.900Z"
}
}
DELETE/assets/:id
안의 그림과 파일까지. 되돌릴 수 없다
응답
{
"success": true,
"data": { "deleted": true }
}
여섯 갈래가 이 하나로 들어갑니다.에셋은 메타데이터고(“버섯 전사”) 그림은 그 안에 쌓입니다 — 리롤도 반전도 시트도 전부 같은 에셋의 이미지 한 줄입니다. 응답은 여섯 갈래가 모두 같습니다.
본문
| 타입 | 설명 |
|---|
op필수 | enum | 아래 표 |
parent_image_id | uuid | generate 를 뺀 전부에 필요합니다. 이 에셋의 이미지여야 하고, 아직 굽는 중인 그림은 부모가 될 수 없습니다 |
label | string | 어느 op 든 선택. 안 주면 op 마다 정해진 이름이 붙습니다 |
palette_size | number | 색 수. 안 주면 프로젝트 계약, 계약에도 없으면 기본값. 고를 수 있는 값은 값 사전 |
outline | string | 테두리 세기. 종류가 테두리를 안 쓰면 무엇을 줘도 off |
| op | 이 op 만 받는 값 | Pixo |
|---|
generate | prompt (안 주면 에셋의 프롬프트 그대로 = 리롤) | 4 |
revise | view · shot · prompt 중 하나 이상 | 4 |
mirror | 없음 | 0 |
recolor | source_color · target_color (#RRGGBB) | 0 |
resize | cell_size | 0 |
rotate | angle (1~359, 정수) | 0 |
animate | poses[] · action(자세 하나일 때만) | 자세당 8 |
mirror·recolor·resize·rotate 가 0 인 것은 할인이 아니라 원가가 0 이기 때문입니다 — 모델을 부르지 않고 픽셀만 건드립니다.
카메라를 옮기는 일은 전부 revise 입니다 — 부모를 레퍼런스로 붙여서 각도만 바꾸기 때문입니다. 시선은 front(정면) · front_quarter(우앞대각) · side(우측면) · back_quarter(우뒷대각) · back(후면) · back_quarter_left(좌뒷대각) · side_left(좌측면) · front_quarter_left(좌앞대각), 컷은 full(전신) · bust(상반신) · face(얼굴). 레퍼런스 없이 각도만 바꾸면 각도는 맞아도 매번 다른 사람이 나옵니다.
거절당하는 조합
- ·애니메이션 시트(
frames > 1)는 revise·animate·resize·rotate 의 부모가 될 수 없습니다. 워커가 시트를 한 장짜리 그림으로 보고 다시 그리므로 격자가 통째로 레퍼런스가 됩니다 - ·
resize·rotate 는 축소 전 원본이 남아 있는 30일 안에서만 됩니다(images[].resizable). 최종본을 돌리면 격자가 통째로 기울어져 되살릴 수 없어서, 원본이 없으면 폴백 없이 거절합니다. 같은 크기로 다시 뽑는 것도 거절합니다 - ·
recolor 의 source_color 는 그 이미지의 팔레트에서 계열을 찾습니다. 다른 그림의 색을 넣으면 못 찾고 거절합니다 — 이미지마다 따로 양자화돼서 같은 파랑이라도 값이 몇 단위 다릅니다 - ·
generate 에 view 를 실으면 거절합니다. 시선은 revise 로 옮겨 갔습니다 - ·
animate 의 poses[] 는 그 종류가 허용하는 것만 남기고, 하나도 안 남으면 거절합니다. action 은 자세를 하나만 보낼 때만 쓸 수 있습니다 — 여러 자세에 문장 하나면 어느 자세를 말하는지 알 수 없습니다
요청
# 리롤
curl -X POST "https://api.dotforge.cc/v1/assets/0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32/images" \
-H "Authorization: Bearer $DOTFORGE_KEY" \
-H "Content-Type: application/json" \
-d '{"op":"generate"}'
# 뒷모습 — 부모를 ref 로 붙인다
-d '{"op":"revise","parent_image_id":"7c4e9a02-15bd-4a86-8f31-2d90e5b7c184","view":"back"}'
# 좌우반전 (0 Pixo)
-d '{"op":"mirror","parent_image_id":"7c4e9a02-15bd-4a86-8f31-2d90e5b7c184"}'
# 색 갈아입히기 (0 Pixo)
-d '{"op":"recolor","parent_image_id":"7c4e9a02-15bd-4a86-8f31-2d90e5b7c184",
"source_color":"#3b2a4d","target_color":"#1f4d3b"}'
# 걷기 시트
-d '{"op":"animate","parent_image_id":"7c4e9a02-15bd-4a86-8f31-2d90e5b7c184","poses":["walk"]}'
응답
{
"success": true,
"data": {
"job_id": "3ab61f58-70c2-4d19-9e45-8b0fa2c61d77",
"status": "pending",
"pixo_cost": 4
}
}
커버로 걸려 있었으면 커버가 null 로 돌아가고, 목록 조회가 가장 오래된 그림으로 대신 채웁니다.
응답
{
"success": true,
"data": { "deleted": true }
}
GET/images/:id/download
서명 URL
15분짜리입니다. 아직 굽는 중인 그림은 NOT_FOUND 입니다 — 잡이 done 이 된 뒤에 부르세요.
응답
{
"success": true,
"data": {
"url": "https://…",
"expires_in": 900,
"frames": 4,
"width": 384,
"height": 128
}
}
GET/jobs
진행 중(기본) 또는 끝난 것까지
이 문에는 실시간 신호가 없으므로 폴링이 정답입니다. 분당 한도(120)가 그 폴링을 감당하도록 잡혀 있습니다. status 는 pending · processing · done · failed · cancelled 입니다.
쿼리
| 타입 | 설명 |
|---|
active | "false" | 끝난 것까지 최근 50건. 기본은 진행 중인 것만 |
요청
curl "https://api.dotforge.cc/v1/jobs?active=false" \
-H "Authorization: Bearer $DOTFORGE_KEY"
응답
{
"success": true,
"data": {
"jobs": [
{
"id": "3ab61f58-70c2-4d19-9e45-8b0fa2c61d77",
"asset_id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
"op": "generate",
"status": "done",
"progress": 100,
"progress_note": null,
"error_code": null,
"error_message": null,
"pixo_cost": 4,
"created_at": "2026-08-12T09:03:11.402Z",
"completed_at": "2026-08-12T09:04:52.881Z"
}
]
}
}