API 문서

콘텐츠 등록

콘텐츠를 원장에 올립니다. targets 나 target_language 를 함께 주면 등록과 채널별 예약이 한 번에 끝납니다(예약만 따로 걸려면 POST /contents/schedule 입니다).

버전: v1

02 콘텐츠 등록

2.1 설명

POST/contents

올릴 계정을 고르는 방법이 두 가지입니다.

  • targets — 계정을 하나하나 적습니다. 계정마다 시각·공개 범위·옵션을 달리 줄 수 있습니다.
  • target_language — 그 언어를 맡는 계정 전부가 대상이 됩니다. 이때 예약 시각은 publish_at 또는 local_publish_at 을 최상위에 함께 보내고, visibility · platform_options 도 최상위에서 받아 모든 대상에 같이 적용합니다.

둘을 함께 보내면 targets 가 이깁니다. 둘 다 보내지 않으면 예약 없이 등록만 됩니다.

본문 형식은 콘텐츠 형태에 따라 갈립니다.

  • text · article · image — application/json 으로 아래 필드를 최상위에 싣습니다.
  • video — multipart/form-data 여야 합니다. 아래 필드 전체를 JSON 문자열 하나로 만들어 metadata 칸에 싣고, 영상은 video 칸에, 썸네일은 thumbnail 칸에 싣습니다. video 는 video/mp4 · video/webm · video/quicktime, thumbnail 은 image/jpeg · image/png · image/webp 만 받습니다. 이 형식에서는 content_type 을 생략하면 video 로 봅니다.
  • carousel — 여러 장을 한 글로 묶습니다. multipart/form-data 이고, 필드 전체를 JSON 문자열로 만들어 metadata 칸에 싣는 것은 video 와 같습니다. 파일은 items[] 칸에 보낸 순서대로 싣습니다. 2장부터 10장까지이고 사진과 영상을 섞을 수 있습니다. 받는 형식은 image/jpeg · image/png · image/webp · video/mp4 · video/webm · video/quicktime 입니다.

external_id 를 주면 같은 값으로 다시 불러도 콘텐츠가 두 줄이 되지 않습니다. 이미 있으면 created 가 false 로 오고 그 콘텐츠를 그대로 돌려줍니다. 다만 같은 external_id 에 다른 content_type 이나 다른 title 을 보내면 409 입니다.

채널마다 제목·본문을 달리 쓰려면 platform_metadata 에 적습니다. 열쇠는 채널 키이고, 값은 그 채널에만 쓸 값을 담은 객체입니다. 적지 않은 채널은 공통 title · body 를 그대로 씁니다. 안쪽에 쓰는 칸은 title · body 둘이고 공통 필드와 이름이 같습니다. 해시태그는 여기서 덮어쓰지 않습니다 — hashtags 는 최상위 공통값 하나뿐입니다.

한 줄로 적으면 {"title": "9월 넷째 주 요약", "body": "이번 주에 있었던 일", "platform_metadata": {"youtube": {"title": "9월 4주차 주간 요약"}, "instagram": {"body": "이번 주 요약 — 자세한 건 프로필 링크에서"}}} 입니다.

쓸 수 있는 채널 키는 아홉입니다. youtube · instagram · tiktok · x · threads · facebook · linkedin · naverblog · wordpress. 이 가운데 계정을 연결할 수 있는 것은 앞의 일곱이고, naverblog 와 wordpress 는 채널 목록에는 있으나 연결할 수단이 없습니다.

검사는 「배열이 아니라 객체인가」까지입니다. 안쪽 열쇠 이름도, 칸 이름도, 길이도 보지 않습니다. 규약에서 벗어난 값은 오류가 아니라 침묵으로 버려지므로, 틀렸다는 신호가 돌아오지 않는 만큼 오히려 더 위험합니다. 모르는 채널 키(예: kakao)를 넣어도 거절되지 않고 그대로 보관되며 아무 데도 쓰이지 않습니다. 대소문자가 다르면 다른 열쇠여서 Instagram 은 instagram 이 아니고 조용히 무시됩니다. 배열([] 꼴 목록)을 보내면 400 / 1004 이고, 빈 객체 {} 는 받습니다.

이 값은 지금 보관하고, 조회할 때 그대로 돌려줍니다.

2.2 파라미터

콘텐츠 등록 파라미터
이름구분설명
workspace필수워크스페이스 코드
title필수공통 제목. 1자 이상 500자 이하
content_type필수video · text · article · image · carousel. video 와 carousel 은 위 설명의 multipart 형식으로만 보낼 수 있습니다
external_id선택외부 제작 시스템의 멱등 식별자. 영문·숫자·.·_·- 로 1~200자
body선택공통 본문 또는 설명. 10000자 이하
hashtags선택공통 해시태그. 문자열 60개까지, 한 개는 100자 이하
language선택언어 코드(ko · en-US 꼴). 생략하면 ko
platform_metadata선택채널마다 제목·본문을 달리 쓸 때 씁니다. 열쇠는 채널 키, 값은 title · body 를 담은 객체입니다. 배열이 아니라 객체여야 합니다. 위 설명을 참고하세요
metadata선택제작 시스템 확장 메타데이터. 배열이 아니라 객체여야 합니다
produced_at선택제작 완료 시각. UTC 오프셋을 포함한 ISO-8601(2026-09-27T05:00:00Z)
duration_seconds선택영상 길이(초). 0보다 커야 합니다
width선택가로 픽셀. 0보다 커야 합니다
height선택세로 픽셀. 0보다 커야 합니다
fps선택초당 프레임. 0보다 커야 합니다
targets선택등록과 동시에 걸 채널별 예약. 50개까지. 한 줄의 모양은 POST /contents/schedule 과 같습니다
target_language선택이 언어를 맡는 계정 전부를 대상으로 삼습니다(ko · en · pt-br 꼴, 2~5자 소문자). targets 를 함께 주면 무시됩니다
publish_at선택target_language 로 고를 때의 예약 시각. UTC 오프셋을 포함한 ISO-8601(2026-09-28T01:00:00Z)
local_publish_at선택target_language 로 고를 때의 예약 시각. 워크스페이스 시간대의 현지 시각(2026-09-28T10:00). publish_at 과 둘 중 하나만 보냅니다
visibility선택target_language 로 고른 모든 대상의 공개 범위. public · unlisted · private. 생략하면 public
platform_options선택target_language 로 고른 모든 대상의 플랫폼별 발행 옵션

2.3 요청

Terminal — bash
curl -X POST https://moacast.com/api/moacast/v1/contents \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"workspace":"a1b2c3d4","title":"value","content_type":"value"}'

2.4 성공 응답

Response — JSON
{
    "result": "SUCCESS",
    "result_code": 0,
    "message": "콘텐츠를 등록했습니다.",
    "data": {
        "created": true,
        "content": {
            "id": 12,
            "external_id": "job-2026-0927-01",
            "content_type": "video",
            "title": "9월 넷째 주 요약",
            "assets": [
                {
                    "asset_type": "MASTER_VIDEO",
                    "mime": "video/mp4",
                    "size_bytes": 18234112,
                    "url": "https://…/moacast/3/12/1790000000-ab12cd.mp4?X-Amz-Signature=…",
                    "url_expires_in": 300
                }
            ],
            "publications": []
        }
    }
}

2.5 필드

콘텐츠 등록 필드
필드타입설명
createdbool새로 만들었으면 true, 이미 있던 것을 돌려주면 false
contentobject등록된 콘텐츠. 모양은 GET /contents 의 한 줄과 같습니다

2.6 실패

콘텐츠 등록 실패
코드HTTP언제
9912400workspace 를 주지 않았습니다
1004400값이 규칙에 맞지 않습니다(형식·길이·metadata 칸 누락, targets 의 규칙 위반, 연결되지 않았거나 활성 상태가 아닌(다시 연결해야 하는) 계정, target_language 형식이나 그 언어의 계정이 없음, 같은 플랫폼에 계정이 둘 이상인데 external_id 를 주지 않음 등)
1001401헤더가 없거나 토큰이 올바르지 않습니다
1002403정지된 워크스페이스입니다
1003403세션으로 부른 쓰기인데 요청 출처가 이 사이트가 아닙니다
2000404그 워크스페이스가 없거나, 이 토큰으로 다룰 수 없습니다
2001409같은 external_id 에 다른 콘텐츠가 등록되어 있습니다
4090409이미 발행 처리된 예약이 있어 targets 를 갱신할 수 없습니다
1004413업로드 크기 제한을 넘었습니다
4150415content_type 이 video 인데 multipart/form-data 가 아닙니다
5000500등록에 실패했습니다
5000503콘텐츠 저장소가 준비되지 않았습니다. 파일을 함께 보냈는데 지금 파일을 받을 수 없는 상태인 경우도 같습니다

계정의 언어는 채널 화면에서 정합니다(GET /channels 의 language). 언어를 정하지 않은 계정은 target_language 로 고를 수 없습니다. 그 언어의 계정이 하나도 없으면 400 이고 콘텐츠도 등록되지 않습니다.

보낸 순서가 곧 올라가는 순서입니다. 칸 이름에 번호를 붙이지 마세요 (item1 · item2 꼴은 받지 않습니다). 같은 이름 items[] 을 파일 수만큼 반복합니다.

이 형태를 받는 채널은 지금 인스타그램 하나입니다. 다른 채널에 예약을 걸면 그 채널은 발행하지 못합니다.

인스타그램은 캡션을 글 하나에 하나만 받습니다. 장마다 다른 글을 붙일 수는 없습니다. 본문은 공통 body(와 platform_metadata.instagram.body)가 쓰입니다.

새로 만든 경우에만 201 이고, 이미 있던 것을 돌려줄 때는 200 입니다.

영상 규격(duration_seconds · width · height · fps)을 보내면 metadata 의 production 아래로 옮겨 저장됩니다. 조회할 때도 그 자리에서 나옵니다.

PUT /contents/{id} 에서는 platform_metadata 가 통째로 갈아치워집니다. 한 채널만 고치려 해도 전체를 다시 보내야 합니다.