API

빌드 스크립트·CI·게임 서버에서, 또는 코딩 에이전트에게 시켜서 DotForge 를 부릅니다. 브라우저에서 쓰는 것과 같은 엔진이고, 다른 것은 자격증명뿐입니다.

AI 에게 맡기기

Claude Code·Codex 같은 코딩 에이전트에게 시킬 거라면, 규칙 파일을 저장소에 넣어 두세요. 이 페이지의 규칙 중 에이전트가 첫 요청을 성공시키는 데 필요한 것만 골라 담아 우리가 배포합니다 — 손으로 옮겨 적은 사본은 규칙이 바뀔 때 뒤처지고, 그 실패는 우리 로그에 남지 않습니다.

  • Claude Code
  • Codex
  • Cursor
  • Copilot
  • 그 밖에 무엇이든

AGENTS.md Codex·Cursor·Copilot 을 비롯해 대부분이 저장소 루트에서 이 이름을 찾는다.

AGENTS.md
curl -O https://dotforge.cc/AGENTS.md

CLAUDE.md Claude Code 가 찾는 이름. 내용은 위와 같다.

CLAUDE.md
curl -O https://dotforge.cc/CLAUDE.md

Claude Code 스킬 도트·스프라이트 이야기가 나올 때 알아서 열린다. 규칙을 미리 안 읽어도 된다.

Claude Code 스킬
curl --create-dirs -o .claude/skills/dotforge/SKILL.md https://dotforge.cc/skill/SKILL.md

이 페이지도 로그인 없이 열립니다 — 주소만 줘도 에이전트가 읽습니다. 값이 필요할 때 읽을 곳은 GET /catalog 이고 그것도 인증 없이 열립니다. 사이트 지도는 /llms.txt 에 있습니다.

파일을 넣을 수 없는 자리(웹 채팅 등)에는 같은 규칙을 문단으로 붙여넣으세요.

에이전트에게 붙여넣을 프롬프트
DotForge API 로 도트 스프라이트를 만들어 줘.
아래 규칙을 지켜라. 더 필요하면 https://dotforge.cc/docs 를 읽어라 (로그인 없이 열린다).

## 인증

헤더 하나다.

```
Authorization: Bearer $DOTFORGE_KEY
```

키는 어느 쪽에도 넣지 마세요. 환경변수 DOTFORGE_KEY 로 두고 그 이름만 알려 주면 됩니다 — 대화 기록과 소스에 원문이 남지 않습니다. 키는 설정에서 발급합니다.

시작하기

모든 주소는 이 밑에 붙습니다. 키는 설정에서 발급하고, 발급 화면을 닫으면 다시 볼 수 없습니다.

https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/v1

인증은 헤더 하나입니다. console 의 JWT 는 여기서 통하지 않습니다 — 문이 다릅니다.

Authorization: Bearer dotforge_sk_...

응답은 언제나 { success: true, data } 또는 { success: false, error } 입니다. 아래 예시는 data 까지 포함한 전문입니다.

굽고 받아 가는 흐름

그림 한 장을 굽는 데 수십 초가 걸립니다. 그동안 연결을 붙잡고 있으면 함수 실행 한도에 먼저 걸리기 때문에, POST 는 잡을 접수하고 바로 돌아옵니다. 결과는 폴링으로 받습니다.

  1. 1. 굽는다 — job_id 를 받는다
    curl -X POST "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/v1/assets" \
        -H "Authorization: Bearer $DOTFORGE_KEY" \
        -H "Content-Type: application/json" \
        -d '{"prompt":"버섯 갑옷을 입은 작은 전사","kind":"character"}'
  2. 2. 끝날 때까지 본다
    curl "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/v1/jobs" -H "Authorization: Bearer $DOTFORGE_KEY"

    기본은 진행 중인 것만 옵니다. status done 이 되면 그 에셋을 다시 읽어 새 이미지를 찾습니다.

  3. 3. 받아 간다
    curl "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/v1/assets/$ASSET_ID" -H "Authorization: Bearer $DOTFORGE_KEY"
      curl "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/v1/images/$IMAGE_ID/download" -H "Authorization: Bearer $DOTFORGE_KEY"

    짧은 수명의 서명 URL 이 옵니다. 애니메이션 시트면 frames 도 같이 오므로 엔진에 넣을 때 격자를 다시 추론하지 않아도 됩니다.

엔드포인트

사용 내역과 키 발급은 없습니다. 앞엣것은 결제 기록이라 사람이 보는 화면이고, 뒤엣것은 키로 키를 만들 수 있으면 유출된 키 하나가 스스로 자기 자리를 늘리기 때문입니다. 그 밖에는 브라우저가 하는 일을 전부 할 수 있습니다.

계정

누구인지, 얼마가 남았는지, 굽는 기계가 살아 있는지.

GET/me

계정과 남은 Pixo

응답

{
  "success": true,
  "data": {
    "id": "0f8b2d14-9c3a-4e21-b7d5-6a1e0c9f4b32",
    "email": "you@studio.com",
    "role": "user",
    "pixo_balance": 320,
    "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"
  }
}

프로젝트

에셋을 담는 그릇이자 스타일 계약이 붙는 자리입니다. 같은 프로젝트에서 만든 것은 같은 아트 스타일·색 수·테두리·피사체 크기를 물려받습니다.

GET/projects

프로젝트 목록

기본 프로젝트가 맨 앞입니다. 안 고르면 에셋이 거기로 들어가는 자리라, 목록에서도 그 자리에 있어야 헷갈리지 않습니다. 가입할 때 하나가 자동으로 만들어지므로 목록이 비는 일은 없습니다.

응답

{
  "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/projects

프로젝트를 만든다

빌드 스크립트가 “이번 게임의 에셋” 을 한 그릇에 모으고 그 그릇에 아트 스타일을 걸어 두는 자리입니다. 만든 뒤 POST /assetsproject_id 를 실으면 그 프로젝트로 들어가고, 계약도 같이 걸립니다.

본문

타입설명
name필수string80자까지
colorenumrose · orange · amber · lime · emerald · teal · sky · indigo · violet · gray. 안 주면 gray. 목록에서 점으로만 쓰이고 그림에는 영향이 없습니다
base_promptstring이 프로젝트의 모든 프롬프트 앞에 붙는 공통 첫 문장. 500자까지
style_contractobject아래 표
어디로 가나
artStyle문자열 300자프롬프트
notes문자열 300자프롬프트
paletteSize값 사전후처리
outline값 사전후처리
subjectScale70 · 80 · 90 · 100후처리
  • 모르는 키는 버립니다. jsonb 라 무엇이든 넣을 수 있는데, 정체 모를 키는 프롬프트로 갈지 후처리로 갈지 판단할 근거가 없습니다 — 그대로 두면 유저가 쓴 적 없는 지시가 모델에게 갑니다
  • subjectScale요청이 덮어쓸 수 없습니다. 이 값이 하는 일이 “이 프로젝트의 에셋은 서로 같은 크기다” 라서, 한 장만 다르게 고를 수 있으면 보장이 아니라 기본값이 됩니다. 안 주면 만들 때 100 이 붙습니다
  • paletteSize·outline 은 요청이 덮어쓸 수 있습니다. 지금 고른 것이 더 구체적이니까요

요청

curl -X POST "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/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} 만 보내면 artStyleoutline 도 사라집니다.
특히 subjectScale 을 빠뜨리지 마세요. 이 값만은 요청으로 되돌릴 수 없어서, 여기서 지워지면 그 프로젝트는 피사체 크기를 안 맞추는 상태로 남고 다시 PATCH 하기 전까지 그대로입니다. 계약을 고쳐도 이미 만들어진 그림은 그대로입니다; 다음에 굽는 것부터 새 계약을 따릅니다.

본문

타입설명
namestring빈 문자열은 거절됩니다
colorenum없는 색은 거절됩니다
base_promptstring | null빈 문자열을 주면 지웁니다
style_contractobject키는 POST /projects 와 같습니다. {} 를 주면 계약이 비고, 그 프로젝트의 에셋은 기본값으로 굽습니다

요청

curl -X PATCH "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/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

라이브러리

카드 한 장이 에셋 하나입니다. 이미지를 전부 싣지 않고 커버 한 장과 개수만 옵니다 — 그림이 필요하면 GET /assets/:id 입니다.

쿼리

타입설명
limitnumber기본 50, 최대 100
favorite"true"즐겨찾기만
project_iduuid안 주면 전체 프로젝트를 합쳐서 봅니다

요청

curl "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/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
  }
}
에러NOT_FOUND
POST/assets4 Pixo

에셋을 만들고 첫 그림을 굽는다

두 단계로 나누지 않습니다. “에셋 만들기” 가 따로 있으면 그림 한 장을 보기 전에 이름부터 지어야 하는데, 아직 뭐가 나올지 모르는 시점입니다. 에셋 행과 잡이 같이 생깁니다 — 잔액이 모자라 잡이 서지 못하면 만들던 에셋도 지웁니다.

본문

타입설명
prompt필수string1000자까지
kindenum안 주면 character. 값 사전
aspectenum에셋이 소유합니다 — 여기서 한 번 고르고, 이후 요청에는 싣지 않습니다. 그 종류가 못 쓰는 값이면 첫 번째 값으로 떨어집니다
cell_sizenumber종류와 비율이 같이 정합니다. 못 고르는 값은 기본값으로 떨어집니다
namestring안 주면 프롬프트 앞 80자
project_iduuid안 주면 기본 프로젝트
use_project_styleboolean프로젝트 스타일 계약을 따를지. false 를 명시할 때만 끕니다. 이 값은 에셋에 남아 리롤에도 따라갑니다
palette_sizenumber색 수. 안 주면 프로젝트 계약, 계약에도 없으면 기본값. 고를 수 있는 값은 값 사전
outlinestring테두리 세기. 종류가 테두리를 안 쓰면 무엇을 줘도 off

요청

curl -X POST "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/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"]
  }
}
에러NOT_FOUND
PATCH/assets/:id

이름 · 커버 · 즐겨찾기 · 프로젝트

하나 이상 주세요 — 빈 요청은 VALIDATION_ERROR 입니다. 프로젝트를 옮기면 이후 리롤·애니메이션이 새 프로젝트의 계약을 따릅니다.이미 만들어진 그림은 그대로입니다 — 옮겼다고 다시 굽지 않습니다.

본문

타입설명
namestring빈 문자열은 거절됩니다
is_favoriteboolean
cover_image_iduuid이 에셋의 이미지여야 합니다
project_iduuid다른 프로젝트로 옮깁니다
use_project_styleboolean만든 뒤에도 켜고 끌 수 있습니다

요청

curl -X PATCH "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/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 }
}
에러NOT_FOUND
POST/assets/:id/imagesop 마다 다름

같은 에셋에 그림 한 장 더

여섯 갈래가 이 하나로 들어갑니다.에셋은 메타데이터고(“버섯 전사”) 그림은 그 안에 쌓입니다 — 리롤도 반전도 시트도 전부 같은 에셋의 이미지 한 줄입니다. 응답은 여섯 갈래가 모두 같습니다.

본문

타입설명
op필수enum아래 표
parent_image_iduuidgenerate 를 뺀 전부에 필요합니다. 이 에셋의 이미지여야 하고, 아직 굽는 중인 그림은 부모가 될 수 없습니다
labelstring어느 op 든 선택. 안 주면 op 마다 정해진 이름이 붙습니다
palette_sizenumber색 수. 안 주면 프로젝트 계약, 계약에도 없으면 기본값. 고를 수 있는 값은 값 사전
outlinestring테두리 세기. 종류가 테두리를 안 쓰면 무엇을 줘도 off
op이 op 만 받는 값Pixo
generateprompt (안 주면 에셋의 프롬프트 그대로 = 리롤)4
reviseview · shot · prompt 중 하나 이상4
mirror없음0
recolorsource_color · target_color (#RRGGBB)0
resizecell_size0
animateposes[] · action(자세 하나일 때만)자세당 8

mirror·recolor·resize 가 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 의 부모가 될 수 없습니다. 워커가 시트를 한 장짜리 그림으로 보고 다시 그리므로 격자가 통째로 레퍼런스가 됩니다
  • resize 는 축소 전 원본이 남아 있는 30일 안에서만 됩니다(images[].resizable). 같은 크기로 다시 뽑는 것도 거절합니다
  • recolorsource_color 그 이미지의 팔레트에서 계열을 찾습니다. 다른 그림의 색을 넣으면 못 찾고 거절합니다 — 이미지마다 따로 양자화돼서 같은 파랑이라도 값이 몇 단위 다릅니다
  • generateview 를 실으면 거절합니다. 시선은 revise 로 옮겨 갔습니다
  • animateposes[] 는 그 종류가 허용하는 것만 남기고, 하나도 안 남으면 거절합니다. action 은 자세를 하나만 보낼 때만 쓸 수 있습니다 — 여러 자세에 문장 하나면 어느 자세를 말하는지 알 수 없습니다

요청

# 리롤
curl -X POST "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/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
  }
}
DELETE/images/:id

그림 한 장

커버로 걸려 있었으면 커버가 null 로 돌아가고, 목록 조회가 가장 오래된 그림으로 대신 채웁니다.

응답

{
  "success": true,
  "data": { "deleted": true }
}
에러NOT_FOUND
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)가 그 폴링을 감당하도록 잡혀 있습니다. statuspending · processing · done · failed · cancelled 입니다.

쿼리

타입설명
active"false"끝난 것까지 최근 50건. 기본은 진행 중인 것만

요청

curl "https://sfrdtlqdcuwrlpvfkdxy.supabase.co/functions/v1/api/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"
      }
    ]
  }
}

값 사전

아래 표는 전부 GET /catalog 가 돌려준 값을 그대로 그린 것입니다. 코드에 상수로 박지 말고 그 엔드포인트를 읽으세요 — 종류가 늘면 여기도 같이 늡니다.

종류

kind 가 고를 수 있는 것을 정합니다. 목록이 비어 있다는 것이 곧 “그 종류에는 없다” 는 뜻입니다 — 자세가 비면 애니메이션이, 시선이 비면 카메라 축이 없습니다.

kind비율크기 (기본)자세시선·컷
character캐릭터tall · square · wide · card24 · 32 · 40 · 48 · 64 · 80 · 96 · 128 · 160 · 192 · 256 · 384 · 512 (128)8개8 · 3
item아이템square24 · 32 · 40 · 48 · 64 · 80 · 96 · 128 · 160 · 192 · 256 · 384 · 512 (128)
effect이펙트tall · square · wide · card24 · 32 · 40 · 48 · 64 · 80 · 96 · 128 · 160 · 192 · 256 · 384 · 512 (128)7개
background배경wide · square · tall · phone · phone_safe128 · 256 · 384 · 512 · 768 (384)
tile타일square16 · 24 · 32 · 40 · 48 · 64 · 80 · 96 · 128 · 160 · 192 · 256 · 384 · 512 (128)
etc기타tall · square · wide · card24 · 32 · 40 · 48 · 64 · 80 · 96 · 128 · 160 · 192 · 256 · 384 · 512 (128)

비율

aspect에셋이 소유합니다. 만들 때 한 번 고르고, 이후 리롤·애니메이션 요청에는 싣지 않습니다 — 같은 에셋의 그림들이 비율이 다르면 리컬러도 시트도 서로 안 맞습니다.

aspect크기
tall세로3 : 4종류를 따름
square정사각1 : 1종류를 따름
wide가로4 : 3종류를 따름
card카드2 : 3종류를 따름
phone모바일 고정3 : 7280 · 420 · 560 · 840 (560)
phone_safe모바일 가변11 : 21210 · 420 · 840 (840)

자세

animateposes[] 에 넣는 값입니다. 종류가 허용하는 것만 통과하고, 프레임 수가 곧 시트 한 행의 칸 수입니다.

pose프레임fps
idle대기44
walk걷기48
attack공격48
jump점프48
wave손 흔들기46
die쓰러짐46
run달리기412
hurt피격410
cast시전410
charge충전410
travel전개412
projectile날아가기412
impact착탄812
aura오라46
linger잔존46

후처리

어느 op 에나 붙일 수 있습니다. 안 주면 프로젝트의 스타일 계약을 따르고, 계약에도 없으면 기본값입니다. 프롬프트로 가지 않습니다 — 그림 지시가 아니라 축소 뒤에 도는 값입니다.

고를 수 있는 값기본
palette_size8 · 16 · 24 · 32 · 4824
outlineoff · light · normal · strongnormal

에러

code 로 분기하세요 — message 는 사람이 읽는 한국어라 바뀔 수 있습니다.

{
    "success": false,
    "error": {
      "code": "TOKEN_INSUFFICIENT",
      "message": "Pixo가 모자랍니다."
    }
  }
codeHTTP언제
AUTH_REQUIRED401헤더가 없다
AUTH_INVALID_TOKEN401없는 키 · 폐기된 키 · 키 형식이 아님
VALIDATION_ERROR400값이 카탈로그에 없거나 빠졌다
NOT_FOUND404없거나 남의 것 (구분하지 않는다)
TOKEN_INSUFFICIENT402Pixo 가 모자라 잡이 서지 않았다
PROJECT_LIMIT_EXCEEDED403프로젝트가 이미 50개다 (기본 프로젝트 포함)
PROJECT_DEFAULT_UNDELETABLE409기본 프로젝트를 지우려 했다
RATE_LIMIT_EXCEEDED429분당 한도를 넘겼다
INTERNAL_ERROR500우리 쪽 문제

한도

키 하나당 분당 120회입니다. 창은 60초 고정이고, 넘기면 429 가 나며 그 요청은 세지 않습니다. 폴링이 이 한도 안에서 돌도록 잡아 둔 값이라, 배치로 열두 장을 굽고 각각 확인해도 걸리지 않습니다.

유료 op 는 Pixo 잔액이 막지만 조회와 mirror·recolor 는 공짜라, 그 경로를 세우는 것은 이 한도뿐입니다. 키는 계정당 5개까지 살아 있을 수 있습니다.

프롬프트1000자
이름 · 라벨80자 (넘으면 잘린다)
프로젝트50개 (기본 프로젝트 포함)
base_prompt500자
artStyle · notes각 300자
GET /assets limit기본 50 · 최대 100
GET /jobs최근 50건
다운로드 URL15분
미리보기 URL5분
원본 보관30일 (지나면 resize·고화질 revise 불가)
이미지 보관90일