원스카우트
API · blog-post

AI가 쓴 글, 내 PC가 발행합니다

ChatGPT·Claude 같은 내 AI가 만든 글과 사진을 API로 보내면, 켜져 있는 내 PC의 원스카우트가 가져가 네이버 블로그에 예약글로 올립니다. 글은 우리가 쓰지 않습니다 — 내 AI가 쓰고, 우리는 발행 길만 제공합니다. 네이버 접속은 항상 내 PC에서만 일어납니다.

1. 내 AI발행폴더 포맷 v1로 글 작성
→
2. API 접수POST 후 대기열에 보관
→
3. 내 PC 앱가져와서 초안·예약 등록
→
4. 네이버 예약글발행 전에 직접 확인·수정
PC 앱이 켜져 있고 로그인돼 있어야 발행됩니다. 꺼져 있으면 글은 대기열에서 최대 7일 기다리다 만료됩니다. 발행 시각을 정하지 않으면 지금부터 기본 6시간 뒤(앱 설정에서 변경)로 예약되어, 그 사이 네이버 예약글에서 내용을 고치거나 삭제할 수 있습니다.
앱은 평소 최대 15분 간격으로 확인합니다. API 키를 새로 만든 직후에는 앱이 다음 확인까지 최대 15분 기다릴 수 있습니다. 키가 있으면 1분 간격으로 바뀌고, 앱에서 블로그 설정을 저장하거나 앱을 다시 시작하면 바로 확인합니다.

시작하기

  1. 원스카우트 블로그 이용권을 구독합니다. API 키 발급은 이용권이 있는 계정만 할 수 있습니다.
  2. 웹에서 로그인한 뒤 API 키 발급(현재 위치: 도구 > 인스타 댓글 DM > 설정 탭)에서 권한 blog-post:write(+ 조회용 blog-post:read)를 선택해 키를 만듭니다. 키(osk_…)는 만들 때 한 번만 보입니다.
  3. PC에 원스카우트를 설치하고 같은 계정으로 로그인, 네이버 계정을 연결해 둡니다.

인증과 권한

모든 요청에 Authorization: Bearer osk_… 헤더를 붙입니다. 계정당 분당 60회까지 요청할 수 있고, 넘으면 429와 Retry-After 헤더가 옵니다.

권한할 수 있는 일
blog-post:write글 접수(POST), 업로드 완료 알림, 대기 중인 글 취소(DELETE)
blog-post:read대기열·상태 조회(GET)

키가 없거나 틀리면 401, 권한이 모자라면 403 insufficient_scope, 이용권이 만료되면 403 paid_required입니다.

발행폴더 포맷 v1

글은 평문 텍스트 파일 한 개와 사진 파일들로 보냅니다. 앱의 “문서 폴더 발행”과 같은 규칙·같은 검사기를 쓰므로, 서버가 접수 시점에 먼저 검사해 문제가 있으면 바로 422와 이유 목록(report.items)을 돌려줍니다.

  • 헤더 4줄: 제목: 메뉴: 대표이미지: 사진:, 그 아래 ========== 본문 ========== 줄.
  • 본문 안 사진 자리는 한 줄에 [사진 01], [사진 02] … 처럼 단독으로 씁니다. 번호는 01부터 연속, 한 번씩만.
  • 사진 파일명은 반드시 NN_ 접두(예: 사진/01_atm.jpg)로 시작합니다. 3월 여행.jpg처럼 번호가 아닌 이름은 사진 번호로 읽지 않습니다. (번호 바로 뒤에 한글이 붙는 이름은 01내부.jpg처럼 0을 채운 두 자리일 때만 번호로 인식합니다.)
  • 맨 끝에 #태그를 한 줄로(최대 30개). 소제목은 1. 제목 한 줄.
  • 지원하지 않는 서식(표·HTML·글자색 등)은 단순 문단으로 바뀌고, 바뀐 내용은 report에 표시됩니다.

예시 문서

제목: 말레이시아 은행 계좌 없이 사는 법
메뉴: 정착·생활
대표이미지: 대표이미지/대표_은행계좌.jpg
사진: 본문 [사진 NN] 번호대로 2장

========== 본문 ==========
말레이시아에 온 지 석 달이 넘었는데 아직 은행 계좌가 없다.

[사진 01]

1. 현금은 ATM에서 뽑았다
메이뱅크 ATM에서 트래블 카드로 인출했다.

[사진 02]

#말레이시아 #은행계좌 #해외생활

AI에게 붙여넣는 지시문 (프롬프트 스니펫)

글을 쓰게 하기 전에 아래 지시문을 AI에게 그대로 붙여넣으세요. 형식이 어긋나는 일이 크게 줄어듭니다.

아래 규칙을 지켜서 블로그 글을 "발행폴더 포맷 v1" 평문(.txt)으로 써줘.

[파일 구성]
- 문서 1개: 글.txt (UTF-8)
- 사진: 사진/01_설명.jpg, 사진/02_설명.jpg … (파일명은 반드시 두 자리 번호 + 밑줄로 시작: NN_)
- 대표 이미지(선택): 대표이미지/대표_설명.jpg

[글.txt 형식]
제목: (글 제목 한 줄)
메뉴: (네이버 블로그 카테고리 이름, 없으면 빈칸)
대표이미지: 대표이미지/대표_설명.jpg
사진: 본문 [사진 NN] 번호대로 N장

========== 본문 ==========
(본문 첫 문단)

[사진 01]

(이어지는 본문. 사진을 넣을 자리마다 [사진 02], [사진 03] … 을 한 줄에 단독으로 쓴다)

1. 소제목은 이렇게 "번호. 제목" 한 줄로 쓴다

(마지막 줄) #태그1 #태그2 #태그3

[규칙]
- 사진 번호는 01부터 연속으로, 본문에서 한 번씩만 쓴다. 파일명 번호와 [사진 NN]이 같은 사진을 가리킨다.
- 사진 파일명은 "01_", "02_" 처럼 번호+밑줄로 시작한다. ("3월 여행.jpg" 같은 이름은 번호로 인식되지 않는다)
- (API 로 보낼 때) 발행 시각을 지정하려면 타임존 오프셋을 반드시 붙인다. 예: 2026-11-05T09:00:00+09:00 (오프셋 없는 시각은 거절된다)
- 태그는 맨 끝에 # 로 붙여 쓰고 30개 이하로 한다.
- 구분선은 ──────── 한 줄만 쓴다. 표·HTML·마크다운 이미지 문법은 쓰지 않는다.
- 출처 링크는 "출처: 사이트명 주소" 형태의 한 줄로 쓴다.

엔드포인트

기준 주소는 https://www.one-scout.com입니다. 이미지는 서버리스 본문 한도 때문에 요청 본문에 넣지 않고, 접수 응답에 돌려주는 업로드 주소로 직접 올립니다(주소는 2시간 유효).

메서드경로설명
POST/api/tools/blog-post/drafts글 접수. JSON 본문: document(fileName·text) + images[](name·size) 또는 zip(size), 선택 publishMode(reserve 기본 / immediate)·scheduledAt(ISO 8601, 타임존 오프셋 필수 — 예 2026-11-05T09:00:00+09:00 또는 …Z; 지금부터 30분~30일, 10분 단위로 올림). 헤더 Idempotency-Key 권장.
POST/api/tools/blog-post/drafts/{id}/complete업로드를 마쳤다고 알림. 서버가 파일 존재·크기를 확인하고 대기열(queued)에 올립니다. 이미 올라갔으면 같은 응답(멱등).
GET/api/tools/blog-post/drafts/{id}상태·postUrl·오류 조회.
GET/api/tools/blog-post/drafts?status=queued,scheduled&limit=20내 글 목록(최신순, 최대 50).
DELETE/api/tools/blog-post/drafts/{id}앱이 가져가기 전(uploading·queued)에만 취소. 이미 가져갔다면 409 — 네이버 예약글에서 직접 삭제하세요.

앱이 쓰는 /inbox/claim, /inbox/{id}/status는 앱 로그인 세션 전용이라 API 키로는 호출할 수 없습니다.

curl 예시

글 + 사진 보내기

# 0) 환경 변수
export OSK="osk_여기에_키"
export API="https://www.one-scout.com"

# 1) 글 접수 — 문서 텍스트 + 이미지 목록(이름·바이트 크기)을 보낸다
curl -sS -X POST "$API/api/tools/blog-post/drafts" \
  -H "Authorization: Bearer $OSK" \
  -H "Idempotency-Key: my-post-2026-11-05-001" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile text 글.txt '{
        document: { fileName: "글.txt", text: $text },
        images: [
          { name: "사진/01_atm.jpg",  size: '"$(stat -f%z 사진/01_atm.jpg)"' },
          { name: "사진/02_cash.jpg", size: '"$(stat -f%z 사진/02_cash.jpg)"' }
        ]
      }')" | tee res.json

# 2) 응답의 uploads[] 주소로 이미지를 올린다 (이름 순서대로). Content-Type 은 반드시 image/* 로,
#    한 번 올린 파일은 덮어쓸 수 없다(잘못 올렸다면 글을 취소하고 새로 접수)
curl -sS -X PUT -H "Content-Type: image/jpeg" \
  --data-binary @사진/01_atm.jpg "$(jq -r '.uploads[0].uploadUrl' res.json)"
curl -sS -X PUT -H "Content-Type: image/jpeg" \
  --data-binary @사진/02_cash.jpg "$(jq -r '.uploads[1].uploadUrl' res.json)"

# 3) 업로드 끝 알림 — 서버가 파일을 확인하고 대기열(queued)에 올린다
ID="$(jq -r '.draft.jobId' res.json)"
curl -sS -X POST "$API/api/tools/blog-post/drafts/$ID/complete" -H "Authorization: Bearer $OSK"

# 4) 상태 확인 (내 PC 앱이 가져가 예약하면 scheduled, 시각이 지나면 published)
curl -sS "$API/api/tools/blog-post/drafts/$ID" -H "Authorization: Bearer $OSK"

폴더를 zip 으로 보내기

윈도우 탐색기로 만든 zip(한글 파일명)도 열 수 있습니다.

# 폴더를 zip 으로 묶어 보내는 방법 (글.txt + 사진/ 폴더)
SIZE="$(stat -f%z 발행폴더.zip)"
curl -sS -X POST "$API/api/tools/blog-post/drafts" \
  -H "Authorization: Bearer $OSK" -H "Content-Type: application/json" \
  -d "{\"zip\": {\"size\": $SIZE}}" | tee res.json
curl -sS -X PUT -H "Content-Type: application/zip" \
  --data-binary @발행폴더.zip "$(jq -r '.uploads[0].uploadUrl' res.json)"
curl -sS -X POST "$API/api/tools/blog-post/drafts/$(jq -r '.draft.jobId' res.json)/complete" \
  -H "Authorization: Bearer $OSK"

접수 응답 예시

{
  "draft": {
    "schema": "hankkeut.blogPostInboxDraft/v1",
    "toolId": "blog-post",
    "jobId": "3f6c…",
    "status": "uploading",
    "createdAt": "2026-11-05T01:00:00.000Z",
    "updatedAt": "2026-11-05T01:00:00.000Z",
    "payload": {
      "draftId": "3f6c…",
      "title": "말레이시아 은행 계좌 없이 사는 법",
      "inputKind": "files",
      "publishMode": "reserve",
      "scheduledAt": null,
      "resolvedScheduledAt": null,
      "postUrl": null,
      "fileCount": 2,
      "report": { "ok": true, "counts": { "…": 0 }, "items": [] }
    },
    "error": null
  },
  "uploads": [
    { "name": "사진/01_atm.jpg", "size": 482113, "method": "PUT",
      "uploadUrl": "https://…/upload/sign/blog-post-inbox/…?token=…",
      "headers": { "Content-Type": "image/jpeg" } }
  ],
  "completeUrl": "/api/tools/blog-post/drafts/3f6c…/complete"
}

같은 Idempotency-Key로 같은 내용을 다시 보내면 새 글을 만들지 않고 기존 글을 돌려줍니다(replayed: true). scheduledAt이 그 사이 30분 이내로 가까워져도 같은 요청이면 재생으로 응답합니다. 같은 키로 다른 내용을 보내면 422 idempotency_key_reuse이고, 이미 실패·취소·만료된 글의 키를 다시 쓰면409 idempotency_key_closed이니 새 키로 보내세요.

상태

uploading → queued → picked → scheduled → published
                          └──────────────→ failed
queued/uploading → cancelled   ·   queued/uploading 장기 대기 → expired
상태의미
uploading접수됨, 이미지 업로드 대기(2시간 안에 complete 필요)
queued앱이 가져가길 기다리는 중(7일 넘으면 expired)
picked내 PC 앱이 가져가 처리 중
scheduled네이버에 예약글로 등록됨(resolvedScheduledAt에 실제 예약 시각)
published예약 시각이 지나 발행됨. 주소를 알면 postUrl 포함
failederror.code·error.message에 이유(예: DOCUMENT_INVALID, QUOTA, RETRY_EXHAUSTED)
cancelled / expired취소됨 / 기간 안에 앱이 가져가지 못함(PC 꺼짐 등)

발행 한도는 기존 앱 규칙 그대로 적용됩니다(이용권 기준 하루 발행 수 등). 한도에 걸리면 앱이 최대 48시간 동안 다시 시도한 뒤failed로 알립니다.

한도

항목값
이미지 수글당 최대 30장
이미지 크기한 장 15MB 이하 (jpg·jpeg·png·webp·gif·heic·heif)
전체 크기글 하나(문서+이미지 또는 zip) 40MB 이하
문서 텍스트300KB 이하, 접수 요청 본문 1MB 이하
대기 중인 글계정당 동시에 10건(uploading·queued·picked). 넘으면 429 inbox_full
요청 횟수계정당 분당 60회
보관 기간대기 7일. 앱이 가져간 뒤에는 서버의 파일을 지웁니다.

자주 나는 오류

응답원인과 해결
422 invalid_document포맷 v1 검사 실패. 응답의 report.items(예: 제목 없음, 본문 비어 있음, 사진 번호 불일치)를 AI에게 그대로 전달해 고쳐 다시 보내세요.
422 invalid_uploadcomplete 때 업로드한 파일의 확장자·Content-Type(image/*, zip은 application/zip)·파일 내용이 선언과 다릅니다. 올린 파일은 덮어쓸 수 없어 이 글은 종료되므로 새로 접수하세요.
409 upload_incompletecomplete 전에 이미지가 모두 올라오지 않았거나 선언한 size와 실제 크기가 다릅니다. missing·wrongSize를 확인하세요. 올린 파일은 덮어쓸 수 없으므로 크기가 다른 파일이 있으면 DELETE로 글을 취소한 뒤 새 Idempotency-Key로 다시 접수하세요(누락만 있으면 해당 파일을 올리고 complete를 다시 호출).
413 too_large / too_many_images위 한도를 넘었습니다. 이미지를 줄이거나 압축하세요.
429 inbox_full대기 중인 글이 많습니다. 앱이 가져갈 때까지 기다리거나 DELETE로 취소하세요.
409 not_cancellable앱이 이미 가져간 글입니다. 네이버 예약글에서 직접 삭제하세요.
글의 사실 관계는 보내기 전에 확인해 주세요. 자동으로 보내도 발행 전에 네이버 예약글에서 한 번 더 볼 수 있도록 기본 6시간의 여유를 둡니다.