DEVELOPERS

Postman · Insomnia · AI 에이전트 도구에는 https://darman-pub.azurewebsites.net/openapi.json 주소를 그대로 넣으면 됩니다.

다르만 출판사 API 가이드

외부 시스템과 AI 에이전트가 소식지 서비스를 그대로 다룰 수 있도록 만든 연동 안내서입니다.
글을 올리고, 재작성 지시를 받아 고쳐 올리고, 컨펌해서 게시·발송하는 전 과정을 API로 처리할 수 있습니다.

  • 기계가 읽는 명세(OpenAPI 3.1): https://darman-pub.azurewebsites.net/openapi.json
  • 이 문서 내려받기: https://darman-pub.azurewebsites.net/api-guide.md
  • 기준 주소: https://darman-pub.azurewebsites.net

1. 빠른 시작

1) 키 발급

관리자 화면 → 설정 → 외부 연동 API 키에서 이름과 권한을 고르고 발급합니다.
키는 발급 순간 한 번만 표시되니 바로 안전한 곳에 보관하세요. 형식은 dmk_ 로 시작합니다.

2) 첫 호출

curl https://darman-pub.azurewebsites.net/api/v1/categories \
  -H "Authorization: Bearer $DARMAN_API_KEY"
{
  "ok": true,
  "items": [
    { "id": 1, "slug": "buddhist-tales", "name": "불교 야화", "next_series_no": 1001 },
    { "id": 2, "slug": "healing", "name": "힐링 메시지", "next_series_no": 1 }
  ]
}

3) 글 올리기

curl -X POST https://darman-pub.azurewebsites.net/api/v1/posts \
  -H "Authorization: Bearer $DARMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"category":"healing","title":"오늘의 한 문장","body_md":"## 한 줄 요약\n오늘은 이만큼만.","tags":["위로"]}'

응답에 검토 링크와 웹훅 주소가 함께 옵니다. 사람이 컨펌하면 게시됩니다.

2. 인증

모든 /api/v1/* 요청에 키를 넣습니다. 세션이나 CSRF 토큰은 필요 없습니다.

Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxx

X-API-Key: dmk_... 헤더도 같은 뜻으로 받습니다. 키가 없거나 폐기됐으면 401, 권한이 모자라면 403 이 돌아옵니다.

권한(scope)

scope할 수 있는 일
content:read주제·글 목록·상세 조회, 검토 큐 읽기
content:write글 올리기·수정 (재작성 결과 재업로드 포함)
review:write컨펌·재작성 지시·즉시 게시·검토 요청 메일 발송
admin:read통계·발송 내역·설정 조회
admin:write설정 변경·글 삭제·재발송·발송 작업 실행

집필 에이전트에는 보통 content:read, content:write 만 주고, 컨펌은 사람이 하도록 두는 편이 안전합니다.

3. 글의 상태

상태뜻공개메일
pending컨펌(검토 승인) 대기안 됨안 나감
rewrite재작성 요청됨 (사유가 review_note 에)안 됨안 나감
scheduled컨펌됐고 예약 시각 대기안 됨그 시각에
published게시됨공개발송됨
draft초안안 됨안 나감
archived보관안 됨안 나감

컨펌 게이트(설정 review_required, 기본 켜짐)가 켜져 있으면 새로 올라온 글은 무조건 pending 으로 들어옵니다.

컨펌하면 벌어지는 일

  • 예약 시각(publish_at)이 미래면 → scheduled 로 두고 그 시각에 게시·발송합니다.
  • 예약 시각이 없거나 지났으면 → 즉시 게시하고 구독자 발송 큐를 적재합니다.
  • 전에 게시된 적 있는 글이면 → 원래 게시 시각을 유지하고 메일을 다시 보내지 않습니다.

4. AI 에이전트 연동

집필 에이전트가 글을 올리고, 사람이 재작성을 지시하면 고쳐서 다시 올리는 순환입니다.

1단계 — 글 올리기

POST /api/v1/posts · content:write

{
  "category": "healing",
  "slug": "healing-today-one-line",
  "title": "오늘의 한 문장",
  "summary": "비우면 본문에서 자동 생성됩니다",
  "body_md": "## 한 줄 요약\n오늘은 이만큼만 하면 됩니다.",
  "tags": ["위로", "아침"]
}

응답:

{
  "ok": true,
  "created": true,
  "queued": 0,
  "review_required": true,
  "post": {
    "id": 4123,
    "slug": "healing-today-one-line",
    "status": "pending",
    "review_url": "https://darman-pub.azurewebsites.net/review/AbCd...",
    "confirm_webhook": "https://darman-pub.azurewebsites.net/api/review/AbCd.../confirm",
    "rewrite_webhook": "https://darman-pub.azurewebsites.net/api/review/AbCd.../rewrite"
  }
}

slug 를 직접 정해 두면 나중에 같은 글을 덮어쓰기 쉽습니다. 비우면 제목에서 자동 생성됩니다(한글 제목은 주제-날짜 형태).

2단계 — 재작성 지시 받기

GET /api/v1/review/queue?status=rewrite · content:read

curl "https://darman-pub.azurewebsites.net/api/v1/review/queue?status=rewrite&body=1" \
  -H "Authorization: Bearer $DARMAN_API_KEY"
{
  "ok": true,
  "status": "rewrite",
  "total": 2,
  "items": [
    {
      "id": 4123,
      "slug": "healing-today-one-line",
      "title": "오늘의 한 문장",
      "review_note": "도입부를 더 짧게, 예시를 하나 추가",
      "body_md": "## 한 줄 요약\n..."
    }
  ]
}

review_note 가 사람이 남긴 재작성 사유입니다. body=1 을 붙이면 본문까지 같이 받습니다.

3단계 — 고쳐서 다시 올리기

같은 slug 로 POST /api/v1/posts 를 호출하면 덮어쓰고 상태가 다시 pending 이 됩니다.

{
  "slug": "healing-today-one-line",
  "category": "healing",
  "title": "오늘의 한 문장",
  "body_md": "## 한 줄 요약\n짧게 고쳤습니다.\n\n예시를 하나 더했습니다."
}

응답의 created 가 false 면 기존 글을 덮어쓴 것입니다.

4단계 — 컨펌

사람이 검토 링크나 메일에서 눌러도 되고, 권한이 있으면 API로도 가능합니다.

curl -X POST https://darman-pub.azurewebsites.net/api/v1/posts/4123/confirm \
  -H "Authorization: Bearer $DARMAN_API_KEY"
{ "ok": true, "result": "published", "queued": 12, "post": { "status": "published" } }

result 는 published · scheduled · restored · already_published 중 하나이고, queued 는 적재된 메일 발송 건수입니다.
메일을 보내지 않고 게시만 하려면 {"notify": false} 를 함께 보냅니다.

에이전트 의사코드

매일:
  주제마다:
    글 = 생성한다()
    POST /api/v1/posts { slug, category, title, body_md }

주기적으로:
  큐 = GET /api/v1/review/queue?status=rewrite&body=1
  큐의 각 글에 대해:
    새본문 = 다시쓴다(글.body_md, 글.review_note)
    POST /api/v1/posts { slug: 글.slug, category: 글.category, title: 글.title, body_md: 새본문 }

5. 이미지 넣기

글에 사진을 쓰려면 먼저 이미지를 올리고, 돌려받은 url 을 본문이나 대표 이미지에 씁니다.

올리기 — 두 가지 방법

바이트를 그대로 (가장 간단합니다)

curl -X POST "https://darman-pub.azurewebsites.net/api/v1/images?alt=%EA%B0%80%EC%9D%84%20%EB%93%A4%ED%8C%90" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @photo.jpg

JSON 으로 base64

POST https://darman-pub.azurewebsites.net/api/v1/images
Authorization: Bearer <키>
Content-Type: application/json

{ "data_base64": "/9j/4AAQSkZJRg...", "alt": "가을 들판" }

data:image/jpeg;base64, 접두사가 붙어 있어도 알아서 떼어냅니다.

응답

{
  "ok": true,
  "image": {
    "id": "9f2c…",
    "url": "/uploads/9f2c….jpg",
    "absolute_url": "https://darman-pub.azurewebsites.net/uploads/9f2c….jpg",
    "mime": "image/jpeg", "bytes": 214880, "width": 1600, "height": 1067,
    "alt": "가을 들판", "deduped": false
  }
}

deduped: true 는 똑같은 사진이 이미 있어서 그것을 그대로 돌려줬다는 뜻입니다. 같은 파일을 여러 번 올려도 저장 공간이 늘지 않습니다.

글에 쓰기

본문 안에 넣을 때는 마크다운 이미지 문법에 url 을 그대로 씁니다.

{
  "category": "healing-art",
  "title": "가을 들판을 그린 사람들",
  "cover_image": "/uploads/9f2c….jpg",
  "cover_alt": "가을 들판",
  "body_md": "## 첫 장면\n\n![가을 들판](/uploads/9f2c….jpg)\n\n그림은 이렇게 시작합니다…"
}
  • cover_image — 대표 이미지. 목록 카드, 메일, 카카오톡·슬랙 공유 미리보기(og:image), RSS 에 쓰입니다. 빈 문자열 "" 을 보내면 해제됩니다.
  • 본문 이미지 — 업로드한 /uploads/… 주소이거나 https:// 로 시작하는 외부 주소만 그림으로 그려집니다. 그 밖의 주소는 무시합니다.

제한과 거절 사유

항목값
형식JPEG · PNG · GIF · WebP
한 장 용량6MB
가로·세로각 12,000px, 합쳐서 4천만 화소
받지 않는 것SVG (스크립트를 담을 수 있어 제외), 그 밖의 모든 형식

형식은 파일 앞부분 바이트로 판별합니다. 확장자나 Content-Type 을 바꿔 보내도 통하지 않습니다.

코드뜻
413용량·해상도 초과
415허용하지 않는 형식 (SVG 포함)
409삭제하려는 이미지를 쓰는 글이 있음 (?force=1 로 강행)

목록·삭제

GET    https://darman-pub.azurewebsites.net/api/v1/images?page=1      content:read
GET    https://darman-pub.azurewebsites.net/api/v1/images/{id}        content:read
DELETE https://darman-pub.azurewebsites.net/api/v1/images/{id}        admin:write

6. 사이트 고치기 — 문구 · 메뉴 · 페이지 · 배너

화면에 나가는 문구, 메뉴, 고정 페이지, 배너(광고)를 배포 없이 바꿀 수 있습니다. 관리자 화면에서 하는 일을 그대로 API 로도 할 수 있습니다.

화면 문구

GET   https://darman-pub.azurewebsites.net/api/v1/site      admin:read
PATCH https://darman-pub.azurewebsites.net/api/v1/site      admin:write

GET 은 현재 값과 함께 각 항목의 기본값·최대 길이·형식을 알려줍니다.

{
  "ok": true,
  "values": { "hero_title": "매일 한 편,\n마음과 일을 위한 짧은 읽을거리", "footer_legal": "…" },
  "fields": { "hero_title": { "default": "…", "maxLength": 160, "kind": "lines" } }
}
kind뜻
plain글자 그대로
rich굵게 와 링크 를 쓸 수 있음
lines줄바꿈이 화면에서도 줄바꿈으로
email이메일 형식 검사
adsenseca-pub- 로 시작하는 게시자 ID

PATCH 는 보낸 키만 바꿉니다. 빈 문자열을 보내면 기본 문구로 되돌아갑니다.

PATCH https://darman-pub.azurewebsites.net/api/v1/site
{ "hero_title": "매일 한 편,\n오늘의 그림과 이야기", "footer_legal": "정보 제공 목적이며 투자 권유가 아닙니다." }

어떤 값을 보내도 HTML 로 해석되지 않습니다. 태그를 넣으면 글자 그대로 보입니다.

메뉴

GET    https://darman-pub.azurewebsites.net/api/v1/menu        admin:read
POST   https://darman-pub.azurewebsites.net/api/v1/menu        admin:write
PATCH  https://darman-pub.azurewebsites.net/api/v1/menu/{id}   admin:write
DELETE https://darman-pub.azurewebsites.net/api/v1/menu/{id}   admin:write

area 는 header(상단), footer_browse·footer_subscribe·footer_about(푸터 세 칸) 중 하나입니다. 주소는 / 로 시작하는 사이트 내 경로이거나 http(s)·mailto 만 받습니다.

고정 페이지

GET    https://darman-pub.azurewebsites.net/api/v1/pages        content:read
POST   https://darman-pub.azurewebsites.net/api/v1/pages        admin:write
PATCH  https://darman-pub.azurewebsites.net/api/v1/pages/{id}   admin:write
DELETE https://darman-pub.azurewebsites.net/api/v1/pages/{id}   admin:write

slug 를 about · privacy · terms 로 만들면 기본 화면을 대신합니다. 지우면 기본 화면이 다시 나갑니다. 그 밖의 주소는 /page/{slug} 로 열리고, in_menu: true 면 푸터에 링크가 붙습니다. 본문은 마크다운입니다.

배너 · 광고

GET    https://darman-pub.azurewebsites.net/api/v1/banners        admin:read   (노출·클릭 수 포함)
POST   https://darman-pub.azurewebsites.net/api/v1/banners        admin:write
PATCH  https://darman-pub.azurewebsites.net/api/v1/banners/{id}   admin:write
DELETE https://darman-pub.azurewebsites.net/api/v1/banners/{id}   admin:write

노출 위치(slot)

값자리
topbar헤더 바로 아래 (공지 띠)
home_hero홈 · 히어로 아래
home_mid홈 · 주제별 글과 최신 글 사이
list_top목록·주제 화면 상단
post_top글 · 본문 시작 전
post_bottom글 · 본문 끝
footer_top푸터 바로 위

종류(kind) — text(제목·설명·버튼 카드), image(업로드한 이미지), notice(한 줄 공지, topbar 전용), adsense

POST https://darman-pub.azurewebsites.net/api/v1/banners
{
  "slot": "home_mid", "kind": "text", "name": "가을 이벤트",
  "title": "**가을 미술 기획전** 초대",
  "body": "미술 이야기 구독자에게 드리는 무료 관람권.",
  "cta_label": "신청하기", "url": "https://example.com/event",
  "category": "healing-art", "audience": "member",
  "starts_at": "2026-10-01T00:00:00+09:00", "ends_at": "2026-10-31T23:59:59+09:00",
  "weight": 10
}
  • audience — all · guest(비회원만) · member(회원만)
  • category — 그 주제의 글·목록 화면에서만 노출
  • starts_at·ends_at — 비우면 무기한. 같은 자리에 여럿이면 weight 가 큰 쪽이 먼저
  • 링크는 /b/{id} 로 감싸 클릭이 집계되고, 바깥 주소에는 rel="sponsored nofollow noopener" 가 붙습니다
  • 이미지는 POST /api/v1/images 로 올린 /uploads/… 만 씁니다

애드센스

/api/v1/site 의 adsense_client 에 게시자 ID(ca-pub-…)를 넣으면 그때부터:

  • 광고 스크립트가 페이지에 실리고
  • 보안 정책(CSP)에 구글 광고 도메인이 열리며
  • https://darman-pub.azurewebsites.net/ads.txt 가 자동으로 만들어집니다

비워두면 이 중 아무것도 일어나지 않습니다. 광고를 켜도 인라인 스크립트는 쓰지 않으므로 script-src 에 unsafe-inline 이 열리지는 않습니다.

광고 단위는 kind: "adsense" 배너로 만들고, 애드센스에서 발급한 광고 단위의 data-ad-slot 값을 ad_slot 에 넣습니다.

7. 언어별 사이트

한 서버에서 언어마다 독립된 사이트가 섭니다. 주소는 언어별 경로로 나뉩니다.

언어주소시간대
한국어 (기본)https://darman-pub.azurewebsites.net/Asia/Seoul
일본어https://darman-pub.azurewebsites.net/ja/Asia/Tokyo
영어https://darman-pub.azurewebsites.net/en/America/New_York
독일어https://darman-pub.azurewebsites.net/de/Europe/Berlin

실제로 열려 있는 언어는 GET /api/v1/categories 응답의 locales 로 확인할 수 있습니다.

글의 언어는 주제가 정합니다

글을 올릴 때 언어를 따로 지정하지 않습니다. 주제(category)가 어느 언어에 속하는지가 곧 글의 언어입니다.

POST https://darman-pub.azurewebsites.net/api/v1/posts
{ "category": "zen", "title": "古い寺の朝", "body_md": "…" }

zen 이 일본어 주제라면 이 글은 일본어 사이트에 올라가고, 주소는 https://darman-pub.azurewebsites.net/ja/p/… 가 됩니다. 응답의 locale 과 url 로 확인하세요.

목록을 언어로 거르기

GET https://darman-pub.azurewebsites.net/api/v1/categories?locale=ja
GET https://darman-pub.azurewebsites.net/api/v1/posts?locale=ja&status=pending
GET https://darman-pub.azurewebsites.net/api/v1/review/queue?locale=ja

locale 을 빼면 모든 언어가 섞여 나옵니다. 에이전트가 한 언어만 맡는다면 항상 붙이는 편이 안전합니다.

문구·메뉴·페이지·배너

사이트 구성도 언어별입니다.

GET   https://darman-pub.azurewebsites.net/api/v1/site?locale=ja
PATCH https://darman-pub.azurewebsites.net/api/v1/site?locale=ja      { "hero_title": "毎日一篇、…" }
GET   https://darman-pub.azurewebsites.net/api/v1/menu?locale=ja
POST  https://darman-pub.azurewebsites.net/api/v1/pages               { "locale": "ja", "slug": "about", … }
POST  https://darman-pub.azurewebsites.net/api/v1/banners             { "locale": "ja", "slot": "home_mid", … }

배너의 locale 에 "*" 를 주면 모든 언어에 노출됩니다(기본값).

메일

구독자에게 나가는 메일은 그 회원의 언어로 만들어집니다. 제목·본문·버튼·수신거부 안내와 본문 링크(/ja/p/…)까지 그 언어를 따릅니다. 발송 시각도 회원이 사는 시간대를 기준으로 판단합니다.

8. 사람이 검토하는 방법

검토 링크

글마다 발급되는 주소로, 로그인 없이 열립니다. 메일·슬랙·사내 도구에 그대로 붙이면 됩니다.

https://darman-pub.azurewebsites.net/review/{token}

내용을 읽고 컨펌하고 게시 또는 재작성 요청을 누르면 끝입니다. 토큰이 곧 열쇠이므로 외부에 공유하지 마세요.

검토 요청 메일

대기 중인 글을 묶어 관리자에게 보냅니다. 메일 안의 버튼을 누르면 해당 동작이 선택된 검토 화면이 열립니다.

curl -X POST https://darman-pub.azurewebsites.net/api/v1/review/notify \
  -H "Authorization: Bearer $DARMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit":10}'

특정 주소로만 보내려면 {"to":"editor@example.com"} 을 함께 보냅니다.
설정에서 검토 대기 글이 생기면 관리자에게 메일을 켜 두면 글이 올라올 때 자동으로 나갑니다.

메일의 버튼은 클릭 즉시 게시하지 않고 확인 화면을 한 번 거칩니다. 메일 보안 스캐너가 링크를 자동으로 열어 의도치 않게 게시되는 사고를 막기 위한 설계입니다.

9. 검토 웹훅 (인증 불필요)

토큰 자체가 인증 수단이라 외부 시스템에서 바로 호출할 수 있습니다.

메서드주소하는 일
POST/api/review/{token}/confirm컨펌 → 게시
POST/api/review/{token}/rewrite재작성 지시 ({"note":"사유"})
GET/api/review/{token}상태 조회
curl -X POST https://darman-pub.azurewebsites.net/api/review/AbCd.../rewrite \
  -H "Content-Type: application/json" \
  -d '{"note":"표의 숫자 출처를 밝혀 주세요","by":"편집자 김"}'

by 를 넣으면 누가 처리했는지 감사기록에 남습니다.

10. 발송 동작

구독자는 주제마다 즉시 수신 또는 하루 한 번 모아 보기(다이제스트) 를 고를 수 있습니다.

  • 컨펌으로 게시되는 순간, 그 주제를 즉시 수신으로 둔 구독자에게 발송 큐가 적재됩니다.
  • 다이제스트 구독자는 각자 정한 시각에 그날 글을 묶어 받습니다.
  • 예약(publish_at)된 글은 그 시각에 게시되며 같은 규칙으로 발송됩니다.
  • 발송 작업을 지금 돌리려면 POST /api/v1/newsletter/tick (admin:write).

11. 엔드포인트 요약

메서드경로권한설명
GET/api/v1/categoriescontent:read주제 목록 (다음 연재 번호 포함)
GET/api/v1/postscontent:read글 목록 (status·category·q·page·limit)
POST/api/v1/postscontent:write글 올리기 (같은 slug 면 덮어쓰기)
GET/api/v1/posts/{id}content:read글 상세 (본문 포함)
PATCH/api/v1/posts/{id}content:write글 수정
DELETE/api/v1/posts/{id}admin:write글 삭제
POST/api/v1/posts/previewcontent:read마크다운 → HTML 미리보기
POST/api/v1/imagescontent:write이미지 올리기 (바이트 또는 base64)
GET/api/v1/imagescontent:read올린 이미지 목록
GET/api/v1/images/{id}content:read이미지 한 장
DELETE/api/v1/images/{id}admin:write이미지 삭제 (?force=1)
GET/api/v1/siteadmin:read화면 문구 조회
PATCH/api/v1/siteadmin:write화면 문구 수정
GET/api/v1/menuadmin:read메뉴 목록
POST/api/v1/menuadmin:write메뉴 추가
PATCH/api/v1/menu/{id}admin:write메뉴 수정
DELETE/api/v1/menu/{id}admin:write메뉴 삭제
GET/api/v1/pagescontent:read고정 페이지 목록
POST/api/v1/pagesadmin:write고정 페이지 만들기
PATCH/api/v1/pages/{id}admin:write고정 페이지 수정
DELETE/api/v1/pages/{id}admin:write고정 페이지 삭제
GET/api/v1/bannersadmin:read배너 목록 (노출·클릭)
POST/api/v1/bannersadmin:write배너 만들기
PATCH/api/v1/banners/{id}admin:write배너 수정
DELETE/api/v1/banners/{id}admin:write배너 삭제
GET/api/v1/review/queuecontent:read검토 대기·재작성 목록
POST/api/v1/posts/{id}/confirmreview:write컨펌 → 게시
POST/api/v1/posts/{id}/rewritereview:write재작성 지시
POST/api/v1/posts/{id}/publishreview:write즉시 게시
POST/api/v1/posts/{id}/resendadmin:write재발송 큐 적재
POST/api/v1/review/notifyreview:write검토 요청 메일 발송
GET/api/v1/statsadmin:read운영 통계
GET/api/v1/deliveriesadmin:read발송 내역 (수신자 마스킹)
POST/api/v1/newsletter/tickadmin:write발송 작업 실행
GET/api/v1/settingsadmin:read설정 조회
PATCH/api/v1/settingsadmin:write설정 변경

12. 입력 제한과 오류

제한

항목제한
제목120자
요약300자 (비우면 본문에서 자동 생성)
본문60,000자
태그10개, 각 20자
슬러그영문 소문자·숫자·하이픈
요청 본문256KB (이미지 업로드는 6MB, base64 JSON 은 9MB)
이미지한 장 6MB · JPEG/PNG/GIF/WebP · 12,000px · 4천만 화소
호출 한도/api/v1/* 분당 600회, 검토 웹훅 분당 60회

오류 형식

{ "error": "이 작업에는 review:write 권한이 필요합니다. (보유: content:read, content:write)" }
코드뜻
400입력이 올바르지 않음 (주제·상태·형식)
401키 없음·잘못됨·폐기됨
403권한(scope) 부족
404대상 없음 (글·토큰)
429호출 한도 초과 — Retry-After 초 뒤 재시도
500서버 오류

13. 자주 묻는 것

같은 글을 여러 번 올리면 중복되나요?
slug 가 같으면 덮어씁니다. 다르면 새 글이 됩니다. 재작성 결과는 반드시 같은 slug 로 올리세요.

컨펌 없이 바로 게시하고 싶습니다.
설정에서 review_required 를 0 으로 두면 예전처럼 올리는 즉시 게시됩니다. PATCH /api/v1/settings 로도 바꿀 수 있습니다.

이미 게시된 글을 고치면 메일이 다시 나가나요?
아니요. 이미 게시된 글의 수정은 재발송하지 않습니다. 다시 보내려면 POST /api/v1/posts/{id}/resend 를 쓰세요.

연재 번호는 어떻게 매기나요?
series_no 에 숫자를 직접 넣거나 "auto" 로 보내면 해당 주제의 다음 번호가 붙습니다. 현재 다음 번호는 GET /api/v1/categories 의 next_series_no 로 확인합니다.

키를 잃어버렸습니다.
다시 볼 수 없습니다. 관리자 화면에서 기존 키를 폐기하고 새로 발급하세요.

예약 발행은 어떻게 하나요?
글을 올릴 때 publish_at 에 미래 시각(ISO 8601)을 넣고 컨펌하면 scheduled 가 되어 그 시각에 게시·발송됩니다.

엔드포인트 전체 명세

아래는 /openapi.json 에서 자동으로 생성됩니다. 항목을 펼치면 파라미터를 볼 수 있습니다.

posts 글 작성·조회·수정

GET/api/v1/posts글 목록
파라미터위치설명
statusquery상태 필터 (draft | pending | rewrite | scheduled | published | archived)
categoryquery주제 슬러그
qquery제목·태그 검색
pagequery페이지
limitquery페이지 크기(최대 100)
localequery언어 (ko·ja·en·de). 생략하면 모든 언어

응답:200, 401, 403, 429

POST/api/v1/posts글 올리기(같은 slug 면 덮어쓰기)

컨펌 게이트가 켜져 있으면 `pending`(검토 대기)으로 저장되고, 응답에 검토 링크·웹훅 주소가 포함됩니다. 재작성 결과를 올릴 때도 같은 `slug` 로 호출하세요.

요청 본문 — application/json

응답:200, 201, 400, 401, 403, 429

GET/api/v1/posts/{id}글 상세(본문 포함)
파라미터위치설명
id *path글 id

응답:200, 401, 403, 404, 429

PATCH/api/v1/posts/{id}글 수정
파라미터위치설명
id *path글 id

요청 본문 — application/json

응답:200, 401, 403, 404, 429

DELETE/api/v1/posts/{id}글 삭제
파라미터위치설명
id *path글 id

응답:200, 401, 403, 429

POST/api/v1/posts/preview마크다운 → HTML 미리보기

요청 본문 — application/json

응답:200, 401, 403, 429

review 컨펌(승인)·재작성 지시

GET/api/v1/review/queue검토 대기·재작성 목록

`status=rewrite` 로 조회하면 재작성 지시를 받은 글과 사유(`review_note`)를 가져올 수 있습니다.

파라미터위치설명
statusquery기본 pending (pending | rewrite)
bodyquery1 이면 본문 포함 (1)
pagequery페이지
limitquery페이지 크기

응답:200, 401, 403, 429

POST/api/v1/posts/{id}/confirm컨펌(승인) → 게시

예약 시각이 미래면 `scheduled` 로 두어 그 시각에 발송하고, 아니면 즉시 게시하며 구독자 발송 큐를 적재합니다. 과거에 게시된 적 있는 글은 원래 게시 시각을 유지하고 재발송하지 않습니다.

파라미터위치설명
id *path글 id

요청 본문 — application/json

응답:200, 401, 403, 404, 429

POST/api/v1/posts/{id}/rewrite재작성 지시

공개를 내리고 사유를 남깁니다. 에이전트는 `GET /api/v1/review/queue?status=rewrite` 로 사유를 읽어 다시 작성합니다.

파라미터위치설명
id *path글 id

요청 본문 — application/json

응답:200, 401, 403, 404, 429

POST/api/v1/posts/{id}/publish즉시 게시
파라미터위치설명
id *path글 id

응답:200, 401, 403, 429

POST/api/v1/review/notify검토 요청 메일 발송(관리자)

검토 대기 글을 묶어 관리자에게 메일로 보냅니다. 메일 안의 링크에서 바로 컨펌·재작성할 수 있습니다.

요청 본문 — application/json

응답:200, 401, 403, 429

categories 주제

GET/api/v1/categories주제 목록
파라미터위치설명
localequery언어 (ko·ja·en·de). 생략하면 모든 언어

응답:200, 401, 403, 429

images 글에 쓸 이미지 업로드

GET/api/v1/images올린 이미지 목록
파라미터위치설명
pagequery페이지

응답:200, 401, 403, 429

POST/api/v1/images이미지 올리기

두 가지 방법 중 편한 쪽을 쓰세요. 1. **바이트 그대로** — `Content-Type: image/png`(또는 image/jpeg·image/gif·image/webp) 로 파일 내용을 본문에 담아 보냅니다. 설명은 `?alt=` 쿼리로 붙입니다. 2. **JSON** — `Content-Type: application/json` 으로 `{ "data_base64": "...", "alt": "설명" }` 을 보냅니다. `data:image/png;base64,` 접두사가 붙어 있어도 됩니다. 형식은 파일 앞부분 바이트로 판별하므로 확장자·Content-Type 을 속일 수 없습니다. SVG 는 받지 않습니다. 한 장 6MB, 가로·세로 12000px, 4천만 화소까지입니다. 같은 내용을 다시 올리면 새로 저장하지 않고 기존 이미지를 그대로 돌려줍니다(`deduped: true`). 응답의 `url` 을 본문 마크다운 `![설명](url)` 에 넣거나, 글의 `cover_image` 로 지정하세요.

파라미터위치설명
altquery대체 텍스트(바이트로 보낼 때)

요청 본문 — application/json

응답:200, 201, 401, 403, 413, 415, 429

GET/api/v1/images/{hex}이미지 한 장
파라미터위치설명
hex *path이미지 id (32자리 hex)

응답:200, 401, 403, 404, 429

DELETE/api/v1/images/{hex}이미지 삭제

이 이미지를 쓰는 글이 있으면 409 로 막습니다. 그래도 지우려면 `?force=1` 을 붙이세요.

파라미터위치설명
hex *path이미지 id (32자리 hex)
forcequery쓰는 글이 있어도 삭제 (1)

응답:200, 401, 403, 404, 409, 429

site 화면 문구 · 메뉴 · 고정 페이지 · 배너(광고)

GET/api/v1/site화면 문구 조회

각 문구의 현재 값과 기본값·최대 길이·형식을 함께 돌려줍니다. `kind` 가 `rich` 면 `**굵게**` 와 `[링크](/주소)` 를, `lines` 면 줄바꿈을 쓸 수 있습니다.

파라미터위치설명
localequery언어 (ko·ja·en·de). 생략하면 모든 언어

응답:200, 401, 403, 429

PATCH/api/v1/site화면 문구 수정

보낸 키만 바뀝니다. 빈 문자열로 보내면 기본 문구로 되돌아갑니다. 어떤 값도 HTML 로 해석되지 않습니다.

파라미터위치설명
localequery언어 (ko·ja·en·de). 생략하면 모든 언어

요청 본문 — application/json

응답:200, 400, 401, 403, 429

GET/api/v1/menu메뉴 목록
파라미터위치설명
localequery언어 (ko·ja·en·de). 생략하면 모든 언어

응답:200, 401, 403, 429

POST/api/v1/menu메뉴 추가

요청 본문 — application/json

응답:201, 400, 401, 403, 429

PATCH/api/v1/menu/{id}메뉴 수정
파라미터위치설명
id *path메뉴 id

요청 본문 — application/json

응답:200, 401, 403, 404, 429

DELETE/api/v1/menu/{id}메뉴 삭제
파라미터위치설명
id *path메뉴 id

응답:200, 401, 403, 404, 429

GET/api/v1/pages고정 페이지 목록
파라미터위치설명
localequery언어 (ko·ja·en·de). 생략하면 모든 언어

응답:200, 401, 403, 429

POST/api/v1/pages고정 페이지 만들기

`about` · `privacy` · `terms` 로 만들면 기본 화면을 대신합니다. 그 밖의 주소는 `/page/{slug}` 로 열립니다.

요청 본문 — application/json

응답:201, 401, 403, 409, 429

PATCH/api/v1/pages/{id}고정 페이지 수정
파라미터위치설명
id *path페이지 id

요청 본문 — application/json

응답:200, 401, 403, 404, 429

DELETE/api/v1/pages/{id}고정 페이지 삭제

기본 화면이 있는 주소라면 지운 뒤 기본 화면이 다시 나갑니다.

파라미터위치설명
id *path페이지 id

응답:200, 401, 403, 404, 429

GET/api/v1/banners배너 목록(노출·클릭 포함)
파라미터위치설명
localequery언어 (ko·ja·en·de). 생략하면 모든 언어

응답:200, 401, 403, 429

POST/api/v1/banners배너 만들기

요청 본문 — application/json

응답:201, 400, 401, 403, 429

PATCH/api/v1/banners/{id}배너 수정
파라미터위치설명
id *path배너 id

요청 본문 — application/json

응답:200, 401, 403, 404, 429

DELETE/api/v1/banners/{id}배너 삭제
파라미터위치설명
id *path배너 id

응답:200, 401, 403, 404, 429

ops 운영 정보·발송·설정

POST/api/v1/posts/{id}/resend이미 게시된 글 재발송 큐 적재
파라미터위치설명
id *path글 id

응답:200, 401, 403, 429

GET/api/v1/stats운영 통계

응답:200, 401, 403, 429

GET/api/v1/deliveries발송 내역(수신자 마스킹)
파라미터위치설명
statusquery상태
pagequery페이지
limitquery페이지 크기

응답:200, 401, 403, 429

POST/api/v1/newsletter/tick발송 작업 실행(예약 게시·다이제스트·큐 처리)

응답:200, 401, 403, 429

GET/api/v1/settings설정 조회

응답:200, 401, 403, 429

PATCH/api/v1/settings설정 변경

요청 본문 — application/json

응답:200, 401, 403, 429

webhook 토큰 기반 검토 웹훅 (API 키 불필요)

POST/api/review/{token}/confirm검토 토큰으로 컨펌

인증 불필요 — 주소에 포함된 토큰이 인증 수단입니다.

파라미터위치설명
token *path글별 검토 토큰

요청 본문 — application/json

응답:200, 404

POST/api/review/{token}/rewrite검토 토큰으로 재작성 지시

인증 불필요 — 주소에 포함된 토큰이 인증 수단입니다.

파라미터위치설명
token *path글별 검토 토큰

요청 본문 — application/json

응답:200, 404

GET/api/review/{token}검토 대상 상태 조회

인증 불필요 — 주소에 포함된 토큰이 인증 수단입니다.

파라미터위치설명
token *path글별 검토 토큰

응답:200, 404