고객센터

블로그 팩토리 · 게시판 연동 · v1

글쓰기 API 문서

게시판에 올라온 글을 글감으로 보내면, 블로그 팩토리 작업자가 검색 노출용 정보성 블로그 글을 쓰고 완성된 글을 HTML 로 돌려주는 API 입니다. 관리자가 발급한 글쓰기 키로만 호출할 수 있습니다. 글은 등록 즉시 만들어지지 않고, 작업자가 가져가 쓴 뒤 상태가 ready 가 되면 받아 갑니다.

기준 주소
https://blogfactory.kr
인증 헤더
x-api-key: <키>
호출 제한
키당 분당 120회 · 페이지 최대 100건
권한
writing:read (조회·HTML) · writing:write (등록·가져가기·제출·게시 보고)

0. 한눈에 보는 흐름

  1. 게시판 서버가 POST /api/v1/writing/jobs 로 글감(제목·본문·작성자·사이트)을 등록합니다 → queued.
  2. 작업자가 가져가 글을 쓰고 제출합니다 → ready(검수가 필요한 작업은 review → 관리자 승인 → ready).
  3. callback_url 로 알림을 받거나 GET /jobs/{id} 를 폴링해 ready 를 확인합니다.
  4. GET /jobs/{id}/html 로 완성 글 HTML 을 받아 자기 블로그에 올립니다. 이미지는 자리(alt)만 내려오므로 게시판 쪽에서 채웁니다.
  5. POST /jobs/{id}/published 로 게시 주소를 보고합니다 → published.

완성 글의 구조는 고정입니다: h1 제목 · 검색 설명문 · 리드 문단 · 소제목(h2) 3~8개(각 이미지 자리 + 문단) · FAQ(FAQPage JSON-LD) · CTA · 관련 글. 문단 강조는 <b class="pt">(포인트)와 <b class="warn">(주의) 두 클래스만 씁니다.

1. 인증

모든 요청에 발급받은 키를 헤더로 보냅니다. x-api-key 가 기본이고 Authorization: Bearer <키> 도 같은 뜻으로 받습니다. 글쓰기 전용 키는 bfwrt_ 로 시작하는 54자 문자열이며 서버에는 해시만 저장되므로, 발급 화면에서 한 번만 표시됩니다. 잃어버리면 관리자에게 폐기·재발급을 요청하세요.

  • writing:read — 작업 목록·상세·HTML 조회. 항상 포함됩니다.
  • writing:write — 글감 등록, 작업 가져가기, 결과 제출, 게시·실패 보고. 게시판 서버와 작업자 모두 필요합니다.
  • 글쓰기 키로 VOC API 를 부르면 403 입니다. 키는 서버 간 통신에만 쓰고 브라우저·앱·공개 저장소·로그에 두지 마세요.
curl -H "x-api-key: $WRITING_KEY" https://blogfactory.kr/api/v1/writing/me
# {"key":{"id":"…","name":"메가헤르츠 게시판","scopes":["writing:read","writing:write"]},
#  "server_time":"2026-09-17T10:20:00.000Z","statuses":[…],"endpoints":[…]}

2. 작업 상태

status표시뜻다음
queued대기등록됨. 작업자가 가져가기 전writing · cancelled(관리자)
writing작성 중작업자가 글을 쓰는 중ready / review · failed / queued
review검수 대기review_required 작업. 관리자 승인 전까지 HTML 이 열리지 않음ready(승인) · queued(반려)
ready완료완성. GET /html 가능 — 올린 뒤 published 로 보고published · queued(다시 쓰기)
published게시됨호출자가 게시 주소를 보고함—
failed실패작업자가 쓰지 못함(error 에 사유)queued(관리자 재대기)
cancelled취소관리자 취소queued(관리자 재대기)

응답의 status_label 은 한글 표시입니다. 상태 전이가 안 되는 요청은 409 와 현재 status 를 돌려줍니다.

3. 엔드포인트 — 게시판(호출자)

모든 응답은 JSON(UTF-8)이며 시각은 ISO 8601 UTC 입니다. HTML 엔드포인트만 text/html 입니다.

POST/api/v1/writing/jobs글감 등록 · 권한 writing:write
본문 필드설명기본
site *대상 사이트 식별자. 소문자·숫자·하이픈 1~40자(예 megahertz)—
title *게시글 제목(200자)—
body *게시글 본문(20,000자, 평문 또는 마크다운)—
author_name게시글 작성자 표시 이름(60자)""
category게시판 분류(40자)""
keywords노출 목표 키워드 배열(10개, 각 40자)[]
memo작업자에게 남기는 지시 — 톤·분량·꼭 넣을 내용(2,000자)""
source_ref / source_url호출자 쪽 게시글 id(120자) / 원 게시글 주소(http·https)"" / null
source_typeapi 또는 boardapi
review_requiredtrue 면 완성 뒤 review 에 머물고 관리자 승인 뒤 readyfalse
callback_url완성(ready) 시 POST 할 https 주소null
curl -X POST -H "x-api-key: $WRITING_KEY" -H "Content-Type: application/json" \
  https://blogfactory.kr/api/v1/writing/jobs \
  -d '{
    "site": "megahertz",
    "source_type": "board", "source_ref": "42", "source_url": "https://megahertz.co.kr/board/42",
    "title": "병원 블로그, 어떻게 시작해야 하나요",
    "body": "개원 3개월 차인데 블로그를 시작하려고 합니다. 무엇부터 정해야 할지 …",
    "author_name": "홍길동", "category": "병원",
    "keywords": ["병원마케팅", "병원 블로그"],
    "memo": "의료광고법 주의 문장 꼭 포함",
    "callback_url": "https://megahertz.co.kr/hooks/writing"
  }'
# 201 {"job":{"id":"8f4c2a1e-…","status":"queued","status_label":"대기","site":"megahertz", …}}
GET/api/v1/writing/jobs/{id}작업 상세 · 결과(article 구조 + html) 포함
{
  "job": {
    "id": "8f4c2a1e-5b7d-4c3e-9a1f-2d6b8e0c4a71",
    "status": "ready", "status_label": "완료", "site": "megahertz",
    "source": { "type": "board", "ref": "42", "url": "https://megahertz.co.kr/board/42" },
    "author_name": "홍길동", "title": "병원 블로그, 어떻게 시작해야 하나요",
    "category": "병원", "keywords": ["병원마케팅", "병원 블로그"], "review_required": false,
    "claimed_by": "작업자 맥", "claimed_at": "2026-09-17T10:05:00.000Z", "attempts": 1,
    "has_result": true, "result_title": "병원마케팅, 검색 상위노출부터 문의까지", "result_body_chars": 1980,
    "published_url": null, "published_at": null, "error": null,
    "created_at": "2026-09-17T10:00:00.000Z", "updated_at": "2026-09-17T10:12:00.000Z",
    "body": "개원 3개월 차인데 …", "memo": "의료광고법 주의 문장 꼭 포함",
    "callback_url": "https://megahertz.co.kr/hooks/writing",
    "result": {
      "article": {
        "title": "병원마케팅, 검색 상위노출부터 문의까지",
        "description": "병원마케팅의 검색 상위노출 원리와 …",
        "lead": "병원마케팅은 광고비를 늘리는 일이 아니라 …",
        "sections": [
          { "heading": "병원마케팅, 무엇부터 정해야 할까",
            "paragraphs": ["가장 먼저 정할 것은 …", "**검색어는 병원 용어가 아니라 환자의 언어로** 정합니다."],
            "image": { "alt": "병원마케팅, 무엇부터 정해야 할까", "src": null, "caption": null } }
        ],
        "faq": [{ "question": "병원마케팅 효과는 얼마 만에 나타나나요?", "answer": "콘텐츠 축적형이라 …" }],
        "thumbnail": { "alt": "병원마케팅, 검색 상위노출부터 문의까지", "src": null, "caption": null },
        "tags": ["병원마케팅", "상위노출"], "author": "메가헤르츠 마케팅연구소",
        "cta": { "heading": "우리 가게는 지금 검색 몇 위일까요?", "body": "…", "label": "무료 진단 신청하기", "href": "/#apply" },
        "related": []
      },
      "html": "<h1>병원마케팅, 검색 상위노출부터 문의까지</h1>\n<div class=\"meta\">…</div>…",
      "html_source": "server", "worker": "작업자 맥", "notes": "", "body_chars": 1980,
      "submitted_at": "2026-09-17T10:12:00.000Z"
    }
  }
}
  • result 는 제출 뒤부터 보이지만(검수 대기 중 미리보기 가능) 게시는 status 가 ready 일 때만 하세요.
  • result.html 은 아래 HTML 엔드포인트의 본문 조각과 같습니다. html_source 가 worker 면 작업자가 직접 만든 HTML 입니다.
GET/api/v1/writing/jobs/{id}/html완성 글 HTML · text/html · ready·published 에서만 200
파라미터설명
(없음)본문 조각 — <h1>·메타 줄·리드·<h2> 섹션·이미지 자리·FAQ <dl>·CTA·관련 글. 호출자 페이지 틀 안에 넣습니다. 작업자가 직접 HTML 을 준 경우 그것(헤더 x-writing-html-source: worker)
format=document독립 HTML 한 장 — <head> 메타(title·description·og)·canonical·FAQPage/Article JSON-LD·최소 CSS 포함. 정적 페이지로 그대로 올릴 때
site_name · canonical_url · published_at · logo_urlformat=document 의 메타와 JSON-LD 를 채웁니다
# 본문 조각(호출자 페이지 틀 안에 넣기)
curl -H "x-api-key: $WRITING_KEY" "https://blogfactory.kr/api/v1/writing/jobs/<id>/html" > article.html

# 독립 HTML 한 장(정적 페이지로 그대로 올리기)
curl -G -H "x-api-key: $WRITING_KEY" "https://blogfactory.kr/api/v1/writing/jobs/<id>/html" \
  --data-urlencode "format=document" \
  --data-urlencode "site_name=메가헤르츠" \
  --data-urlencode "canonical_url=https://megahertz.co.kr/blog/hospital-marketing.html" > page.html

본문 조각의 모양(이미지는 자리와 alt 만 옵니다 — <!-- image-slot:… --> 를 <img> 나 <figure> 로 바꿔 넣으세요. CTA 링크와 마지막 줄의 1×1 비콘은 성과 측정용이니 그대로 두세요):

<h1>병원마케팅, 검색 상위노출부터 문의까지</h1>
<div class="meta">메가헤르츠 마케팅연구소 · 2026년 09월 17일</div>
<!-- image-slot:thumb alt="병원마케팅, 검색 상위노출부터 문의까지" -->
<p class="lead">병원마케팅은 광고비를 늘리는 일이 아니라 …</p>
<h2>병원마케팅, 무엇부터 정해야 할까</h2>
<!-- image-slot:s0 alt="병원마케팅, 무엇부터 정해야 할까" -->
<p>가장 먼저 정할 것은 목표 환자와 핵심 진료입니다.</p>
<p><b class="pt">검색어는 병원 용어가 아니라 환자의 언어로</b> 정합니다.</p>
…
<div class="faq"><h2>자주 묻는 질문</h2><dl><dt>Q. 병원마케팅 효과는 얼마 만에 나타나나요?</dt><dd>…</dd></dl></div>
<div class="ctabox"><b>우리 가게는 지금 검색 몇 위일까요?</b><p>…</p>
  <a href="https://blogfactory.kr/go/8f4c2a1e-…" target="_blank" rel="noopener" data-cta="tracked">카카오톡으로 무료 진단 신청하기</a></div>
<img src="https://blogfactory.kr/go/8f4c2a1e-…/view.gif" alt="" width="1" height="1" aria-hidden="true" data-beacon="view">
GET/api/v1/writing/jobs작업 목록 · 요약만(본문·결과 없음)
파라미터설명기본
status상태. 쉼표로 여러 개(예 ready,published)전체
site사이트 식별자전체
sincecreated_at >= since. ISO 8601없음
orderasc · desc (created_at 기준)asc
page / page_size1부터 시작, 페이지 크기 1~1001 / 50

응답 { jobs, page, page_size, total, total_pages, next_page }. 콜백을 놓쳤을 때는 site=<사이트>&status=ready 로 주기적으로 찾으면 됩니다.

POST/api/v1/writing/jobs/{id}/published게시 보고 · 권한 writing:write · ready → published
curl -X POST -H "x-api-key: $WRITING_KEY" -H "Content-Type: application/json" \
  "https://blogfactory.kr/api/v1/writing/jobs/<id>/published" \
  -d '{"url":"https://megahertz.co.kr/blog/hospital-marketing.html"}'
# 200 {"job":{"status":"published", "published_url":"https://…", …}}

4. 엔드포인트 — 작업자

글을 쓰는 쪽(블로그 팩토리 작업자 세션)이 씁니다. 게시판 연동만 하면 이 절은 참고만 하세요.

POST/api/v1/writing/jobs/{id}/claim{ worker? } · queued → writing · 동시 호출 시 한쪽만 200
PUT/api/v1/writing/jobs/{id}/result{ article, html?, worker?, notes? } · writing|queued → ready(검수 필요 시 review)
POST/api/v1/writing/jobs/{id}/fail{ error, requeue? } · writing → failed(requeue 면 5회까지 queued)

article 구조

필드규칙
title5~120자. h1
description20~320자. 검색 결과 설명문(meta description·og)
lead20~1,200자. 첫 문단(결론부터)
sections[]3~8개. { heading(≤80), paragraphs[](1~8개, 각 ≤1,500), image?{ alt(필수 ≤200), src?, caption? } }
faq[]0~8개. { question(≤200), answer(≤1,000) }. "Q." 접두는 붙이지 않습니다
thumbnail?{ alt, src?, caption? }
tags? · author?≤10개(JSON-LD keywords) · 메타 줄과 Article author(60자)
cta? · related?[]{ heading, body, label, href } · ≤8개 { title, href }. href 는 http(s) 또는 / 경로

문단은 평문이며 강조 표기 두 가지만 씁니다: **핵심 문장** → <b class="pt">, !!주의 문장!! → <b class="warn">. 태그를 넣어도 문자로 이스케이프됩니다. 서버가 이 구조로 HTML 과 JSON-LD 를 만들고, html 을 함께 주면 호출자에게는 그것을 내려줍니다.

# 1) 대기 작업 → 가져가기
curl -H "x-api-key: $WORKER_KEY" "https://blogfactory.kr/api/v1/writing/jobs?status=queued&order=asc"
curl -X POST -H "x-api-key: $WORKER_KEY" -H "Content-Type: application/json" \
  "https://blogfactory.kr/api/v1/writing/jobs/<id>/claim" -d '{"worker":"작업자 맥"}'

# 2) 글 제출(서버가 HTML 렌더링; html 을 함께 주면 그것을 호출자에게 내려줌)
curl -X PUT -H "x-api-key: $WORKER_KEY" -H "Content-Type: application/json" \
  "https://blogfactory.kr/api/v1/writing/jobs/<id>/result" \
  -d '{"article": { "title": "…", "description": "…", "lead": "…", "sections": [ … ], "faq": [ … ] }, "worker": "작업자 맥"}'
# 200 {"job":{"status":"ready", …}, "callback_sent": true}

# 3) 못 쓴 경우
curl -X POST -H "x-api-key: $WORKER_KEY" -H "Content-Type: application/json" \
  "https://blogfactory.kr/api/v1/writing/jobs/<id>/fail" -d '{"error":"본문이 비어 있음","requeue":false}'

5. 콜백

등록 시 callback_url(https)을 주면 글이 완성될 때(ready, 검수 작업은 승인 시) 아래 JSON 을 POST 합니다. HTML 은 크기 때문에 넣지 않으니 html_url 을 다시 부르세요. 5초 안에 응답이 없거나 실패해도 작업 상태는 그대로이며 관리자 이력에 남습니다.

POST <callback_url>   (헤더 x-writing-event: ready, 5초 제한)
{
  "event": "ready",
  "job": { "id": "8f4c2a1e-…", "status": "ready", "site": "megahertz", "title": "…", "result_title": "…", "result_body_chars": 1980, … },
  "detail_url": "https://blogfactory.kr/api/v1/writing/jobs/8f4c2a1e-…",
  "html_url": "https://blogfactory.kr/api/v1/writing/jobs/8f4c2a1e-…/html",
  "sent_at": "2026-09-17T10:12:01.000Z"
}

5-1. 성과 측정 — 게시글별 조회·CTA 클릭

HTML 을 내려줄 때 서버가 두 가지를 심습니다. 글이 어느 사이트에 올라가도 블로그 팩토리 쪽에서 게시글별 성과가 잡힙니다.

  • CTA 경유 링크 https://blogfactory.kr/go/{id} — 독자가 누르면 클릭 1건을 기록하고 글의 CTA 목적지(예: 카카오톡 오픈채팅)로 바로 넘깁니다. 목적지는 서버에만 있어 주소를 바꿔 다른 곳으로 보낼 수 없습니다.
  • 조회 비콘 https://blogfactory.kr/go/{id}/view.gif — 글 끝의 1×1 이미지 요청 한 번이 조회 1건입니다. 자바스크립트가 필요 없습니다.
  • 봇과 같은 방문자의 분당 30회 초과는 세지 않습니다. IP·브라우저 원문은 저장하지 않고 하루 단위 방문자 해시와 리퍼러 호스트만 남깁니다.
  • 작업 응답의 view_count · click_count · click_rate(%) 로 연동 쪽에서도 볼 수 있습니다. 관리자 화면은 고유 방문자와 최근 14일 일별 표까지 보여 줍니다.
  • CTA 문구는 바꿔도 되지만 href 와 비콘은 그대로 두어야 성과가 잡힙니다.

5-2. 블로그 팩토리 칼럼(site=blogfactory) 자동 게시

  • site 가 blogfactory 인 작업은 완료되는 순간(검수 작업은 승인 때) 서버가 published 로 바꿔 blogfactory.kr/blog 의 /blog/{slug}.html 에 공개합니다. 응답에 slug·column_path 가 붙습니다. 다른 사이트 작업은 종전처럼 호출자가 HTML 을 받아 올립니다.
  • 제출 조건 — article.slug(영문 소문자·숫자·하이픈)와 article.summary(핵심 요약 2~5줄)가 없으면 400 입니다. 요약은 본문의 ‘핵심 요약’ 상자와 JSON-LD abstract·speakable 로 나가 검색·생성형 AI 답변에 쓰입니다.
  • 검색 노출 — 본문 페이지는 canonical·robots·OpenGraph(article)·Article/FAQPage/BreadcrumbList JSON-LD 를 내보내고, /sitemap.xml·/robots.txt·/rss.xml 이 있습니다.
  • 칼럼 주제(category)는 블로그 글쓰기 · 온라인 수익화 · 브랜딩 셋입니다.
  • 개인정보 가리기 — 등록 시 제목·본문의 이메일, 휴대폰·유선 번호, 주민·카드·계좌번호, 개인 SNS 주소, 카톡 아이디를 [이메일] 같은 표지로 바꿔 저장하고, source_type: board 면 작성자 이름을 첫 글자만 남깁니다(응답 masked_personal_info). 이름·상호처럼 형태로 못 잡는 것은 작업자가 글에서 일반화합니다.

6. 오류

코드뜻조치
400파라미터·본문 오류. 본문 error 에 이유가 있습니다요청을 수정합니다
401키 없음 · 형식 오류 · 폐기 · 만료관리자에게 키 확인 또는 재발급을 요청합니다
403권한 범위 밖(읽기 키로 등록·제출, 글쓰기 키로 VOC 호출)관리자에게 writing:write 부여를 요청합니다
404없는 작업 id—
409상태 전이 불가 — 다른 작업자가 먼저 가져감, 검수 대기 중 HTML 요청, 이미 게시됨 등. 본문 status 에 현재 상태상세를 다시 읽고 상태에 맞게 진행합니다
429분당 120회 초과잠시 뒤 재시도(지수 백오프)
500서버 오류잠시 뒤 재시도. 반복되면 시각과 요청 URL 을 관리자에게 전달합니다

오류 응답은 항상 {"error": "사람이 읽을 수 있는 이유"} 형태이고, 상태 충돌(409)에는 status 가 함께 옵니다.

7. 운영 정보

  • 키 발급·폐기·만료·권한 설정과 작업 검수(승인·반려·취소·재대기)는 블로그 팩토리 관리자가 합니다. 키마다 발급자와 마지막 사용 시각이, 작업마다 이벤트 이력이 기록됩니다.
  • 글은 작업자가 켜져 있을 때 처리되므로 등록부터 완성까지 시간이 걸립니다. 게시판 쪽은 콜백 또는 폴링으로 기다리는 구조로 만들어 주세요.
  • 사실 확인이 필요한 주장·법률 문구는 작업자가 완곡하게 씁니다. 사이트 고유의 CTA·관련 글 목록은 memo 로 알려 주면 반영됩니다.
  • 호출 제한은 키 단위 분당 120회입니다. 한글 파라미터는 반드시 URL 인코딩해 보냅니다.
  • 키 발급이나 연동 문의는 고객센터로 보내 주세요.

문서 버전 2026-09-17 · API 버전 v1