API v1

API 문서

Statix API로 페이지 목록 조회, 생성, 삭제를 자동화할 수 있습니다. AI 에이전트, CI 파이프라인, 배포 스크립트에서 바로 호출하세요.

AI 에이전트에게 맡기기

문서를 쓰는 일과 올리는 일을 에이전트가 한 번에 처리할 수 있습니다. 아래 지시문을 시스템 프롬프트나 도구 설명에 그대로 붙여넣으세요.

너는 Statix API로 정적 페이지를 배포한다.

베이스 URL: https://flzwoyvuujcgfnmurjdj.supabase.co/functions/v1/api
인증: Authorization: Bearer $STATIX_API_KEY
      (환경 변수에서만 읽고, 키 값을 답변이나 페이지 내용에 절대 쓰지 마라)

엔드포인트:
  GET    /pages       목록                   pages:read
  POST   /pages       생성                   pages:write
  GET    /pages/{id}  조회 (content 포함)     pages:read
  PATCH  /pages/{id}  수정 (새 버전 append)   pages:write
  DELETE /pages/{id}  삭제                   pages:write

할 일:
1. 요청받은 문서(개인정보처리방침, 이용약관, 랜딩 페이지 등)를
   완결된 HTML 한 덩어리로 작성한다.
   외부 CSS·JS를 참조하지 말고 <style> 태그로 인라인 처리한다.
2. POST /pages 에 { title, slug, content, published: true } 를 보낸다.
   slug 는 소문자 영문·숫자·하이픈·점만 쓴다.
3. 응답 data.url 을 사용자에게 그대로 알려준다. 그 주소가 곧 살아 있는 URL이다.
4. 이미 있는 페이지를 고칠 때는 GET /pages/{id} 로 현재 content 를 읽고,
   PATCH /pages/{id} 로 고친다. 지우고 다시 만들지 마라 —
   PATCH 는 URL·조회수를 유지하지만 재생성은 id 가 바뀐다.
5. 실패하면 error.code 로 분기한다.
   - conflict      : slug 가 이미 있다. 다른 slug 로 한 번만 재시도한다.
   - limit_exceeded: 플랜 한도다. 재시도하지 말고 사용자에게 알린다.
   - 401 / 403     : 키나 스코프 문제다. 재시도하지 말고 사용자에게 알린다.

만들기 전에 GET /pages 로 같은 slug 가 있는지 확인하고, 같은 페이지를
중복해서 만들지 마라. 지우는 것은 사용자가 명시적으로 요청할 때만 한다.

키는 에이전트가 도는 서버의 환경 변수에 두세요. 대화 기록이나 생성한 HTML 안에 키가 들어가면 그대로 유출됩니다.

베이스 URL

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

모든 요청과 응답은 UTF-8 JSON입니다. 성공 응답은 data 키에, 실패 응답은 error 키에 담깁니다.

인증

모든 엔드포인트는 API Key가 필요합니다. 대시보드 > API Key에서 발급한 키를 Authorization 헤더에 담아 보내세요.

Authorization: Bearer stx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

헤더 이름을 바꾸기 어려운 환경이라면 x-api-key: stx_... 도 지원합니다.

키는 서버에서만 사용하세요.

API Key는 계정 전체 권한을 갖습니다. 브라우저 코드나 공개 저장소에 넣지 마세요. 유출이 의심되면 대시보드에서 즉시 만료 처리하고 새로 발급하면 됩니다.

스코프

키를 발급할 때 권한을 고릅니다. 필요한 최소 권한만 주는 편이 안전합니다.

pages:read
내 페이지 목록을 조회합니다.
GET /pages
pages:write
페이지를 생성하고 삭제합니다.
POST /pages, DELETE /pages/{id}

엔드포인트

GET/pages
pages:read
내 페이지 목록을 최신 생성 순으로 반환합니다.

쿼리 파라미터

이름타입필수설명
limitnumber선택한 번에 가져올 개수. 기본 20, 최대 100.
offsetnumber선택건너뛸 개수. 기본 0.
publishedtrue | false선택게시 상태로 필터링합니다. 생략하면 전체.

요청

curl "https://flzwoyvuujcgfnmurjdj.supabase.co/functions/v1/api/pages?limit=20" \
  -H "Authorization: Bearer $STATIX_API_KEY"

응답 200

{
  "data": [
    {
      "id": "8f14e45f-ceea-467a-9f8b-3f2c1d4e5a6b",
      "title": "개인정보처리방침",
      "slug": "privacy-policy",
      "published": true,
      "view_count": 128,
      "url": "https://mysubdomain.statix.kr/privacy-policy",
      "created_at": "2026-08-01T02:11:43.201Z",
      "updated_at": "2026-08-12T09:30:02.118Z"
    }
  ],
  "pagination": {
    "limit": 20,
    "offset": 0,
    "total": 1,
    "has_more": false
  }
}
POST/pages
pages:write
새 페이지를 만들고 첫 버전을 저장합니다.

요청 바디

이름타입필수설명
titlestring필수페이지 제목.
contentstring필수페이지 HTML. 최대 1MB.
slugstring선택URL 경로. 소문자 영문·숫자·하이픈(-)·점(.)만 사용합니다. 생략하면 제목에서 자동 생성됩니다.
publishedboolean선택게시 여부. 기본 false(비공개).

요청

curl -X POST "https://flzwoyvuujcgfnmurjdj.supabase.co/functions/v1/api/pages" \
  -H "Authorization: Bearer $STATIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "이용약관",
    "slug": "terms",
    "content": "<h1>이용약관</h1><p>...</p>",
    "published": true
  }'

응답 201

{
  "data": {
    "id": "3b2a1c9d-7e6f-4a5b-8c9d-0e1f2a3b4c5d",
    "title": "이용약관",
    "slug": "terms",
    "published": true,
    "view_count": 0,
    "url": "https://mysubdomain.statix.kr/terms",
    "current_version_id": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
    "version_number": 1,
    "created_at": "2026-08-23T04:22:10.339Z",
    "updated_at": "2026-08-23T04:22:10.339Z"
  }
}

플랜 한도를 넘으면 429 limit_exceeded가 반환됩니다. Free는 20개, Pro는 1,000개까지 만들 수 있습니다.

GET/pages/{id}
pages:read
페이지 하나를 현재 HTML까지 함께 조회합니다.

요청

curl "https://flzwoyvuujcgfnmurjdj.supabase.co/functions/v1/api/pages/3b2a1c9d-7e6f-4a5b-8c9d-0e1f2a3b4c5d" \
  -H "Authorization: Bearer $STATIX_API_KEY"

응답 200

{
  "data": {
    "id": "3b2a1c9d-7e6f-4a5b-8c9d-0e1f2a3b4c5d",
    "title": "이용약관",
    "slug": "terms",
    "published": true,
    "view_count": 42,
    "url": "https://mysubdomain.statix.kr/terms",
    "content": "<h1>이용약관</h1><p>...</p>",
    "current_version_id": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
    "version_number": 3,
    "created_at": "2026-08-01T02:11:43.201Z",
    "updated_at": "2026-08-12T09:30:02.118Z"
  }
}

목록(GET /pages)에는 content가 들어 있지 않습니다. 내용을 고치기 전에 이 엔드포인트로 현재 HTML을 먼저 읽으세요.

PATCH/pages/{id}
pages:write
제목·슬러그·게시 상태·내용을 고칩니다. 넘긴 항목만 바뀝니다.

요청 바디

이름타입필수설명
titlestring선택페이지 제목.
slugstring선택URL 경로. 바꾸면 기존 주소는 더 이상 열리지 않습니다.
contentstring선택페이지 HTML. 넘기면 새 버전이 만들어지고 현재 버전이 그쪽을 가리킵니다.
publishedboolean선택게시 여부.

요청

curl -X PATCH "https://flzwoyvuujcgfnmurjdj.supabase.co/functions/v1/api/pages/3b2a1c9d-7e6f-4a5b-8c9d-0e1f2a3b4c5d" \
  -H "Authorization: Bearer $STATIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "<h1>이용약관</h1><p>개정판</p>" }'

지우고 다시 만들지 마세요.

수정은 버전을 덧붙이는 방식이라 페이지 ID와 URL, 조회수가 그대로 유지됩니다. 삭제 후 재생성하면 ID가 바뀌고 이력이 사라집니다. 이미 스토어 심사에 제출한 주소라면 특히 위험합니다.

DELETE/pages/{id}
pages:write
페이지와 모든 버전 기록을 삭제합니다. 되돌릴 수 없습니다.

경로 파라미터

이름타입필수설명
iduuid필수삭제할 페이지 ID. 목록 조회 응답의 id 값입니다.

요청

curl -X DELETE "https://flzwoyvuujcgfnmurjdj.supabase.co/functions/v1/api/pages/3b2a1c9d-7e6f-4a5b-8c9d-0e1f2a3b4c5d" \
  -H "Authorization: Bearer $STATIX_API_KEY"

응답 200

{
  "data": {
    "id": "3b2a1c9d-7e6f-4a5b-8c9d-0e1f2a3b4c5d",
    "deleted": true
  }
}

에러

실패 응답은 항상 아래 형태입니다. code로 분기하세요. HTTP 상태 코드만 보고 판단하지 않아도 됩니다.

{
  "error": {
    "code": "insufficient_scope",
    "message": "이 요청에는 pages:write 스코프가 필요합니다.",
    "requiredScope": "pages:write",
    "grantedScopes": ["pages:read"]
  }
}
codeHTTP의미
unauthorized401Authorization 헤더가 없습니다.
invalid_api_key401키가 존재하지 않거나, 만료 처리됐거나, 만료일이 지났습니다.
insufficient_scope403이 요청에 필요한 스코프가 키에 없습니다.
validation_error400요청 값이 올바르지 않습니다.
not_found404대상을 찾을 수 없습니다.
conflict409이미 사용 중인 slug입니다.
limit_exceeded429플랜별 페이지 생성 한도에 도달했습니다.
method_not_allowed405해당 경로가 지원하지 않는 메서드입니다.
internal_error500서버 오류입니다.

JavaScript 예제

Node.js 18 이상이면 별도 패키지 없이 fetch로 호출할 수 있습니다.

const BASE_URL = "https://flzwoyvuujcgfnmurjdj.supabase.co/functions/v1/api";
const API_KEY = process.env.STATIX_API_KEY;

async function statix(path, init = {}) {
  const response = await fetch(BASE_URL + path, {
    ...init,
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  });

  const payload = await response.json();

  if (!response.ok) {
    throw new Error(`[${payload.error.code}] ${payload.error.message}`);
  }

  return payload;
}

// 목록 조회
const { data: pages } = await statix("/pages?limit=50");

// 생성
const { data: created } = await statix("/pages", {
  method: "POST",
  body: JSON.stringify({
    title: "새 랜딩 페이지",
    content: "<h1>안녕하세요</h1>",
    published: true,
  }),
});

console.log("생성됨:", created.url);

// 삭제
await statix(`/pages/${created.id}`, { method: "DELETE" });
시작할 준비가 됐나요?
대시보드에서 키를 발급하면 바로 위 예제를 실행할 수 있습니다.