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 요청
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 성공 응답
{
"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 필드
| 필드 | 타입 | 설명 |
|---|---|---|
created | bool | 새로 만들었으면 true, 이미 있던 것을 돌려주면 false |
content | object | 등록된 콘텐츠. 모양은 GET /contents 의 한 줄과 같습니다 |
2.6 실패
| 코드 | HTTP | 언제 |
|---|---|---|
9912 | 400 | workspace 를 주지 않았습니다 |
1004 | 400 | 값이 규칙에 맞지 않습니다(형식·길이·metadata 칸 누락, targets 의 규칙 위반, 연결되지 않았거나 활성 상태가 아닌(다시 연결해야 하는) 계정, target_language 형식이나 그 언어의 계정이 없음, 같은 플랫폼에 계정이 둘 이상인데 external_id 를 주지 않음 등) |
1001 | 401 | 헤더가 없거나 토큰이 올바르지 않습니다 |
1002 | 403 | 정지된 워크스페이스입니다 |
1003 | 403 | 세션으로 부른 쓰기인데 요청 출처가 이 사이트가 아닙니다 |
2000 | 404 | 그 워크스페이스가 없거나, 이 토큰으로 다룰 수 없습니다 |
2001 | 409 | 같은 external_id 에 다른 콘텐츠가 등록되어 있습니다 |
4090 | 409 | 이미 발행 처리된 예약이 있어 targets 를 갱신할 수 없습니다 |
1004 | 413 | 업로드 크기 제한을 넘었습니다 |
4150 | 415 | content_type 이 video 인데 multipart/form-data 가 아닙니다 |
5000 | 500 | 등록에 실패했습니다 |
5000 | 503 | 콘텐츠 저장소가 준비되지 않았습니다. 파일을 함께 보냈는데 지금 파일을 받을 수 없는 상태인 경우도 같습니다 |
