트렌드
Nodaro 트렌드 카탈로그를 둘러보고, 트렌드의 슬롯을 내 사진으로 채우고, 무료로 견적을 받은 뒤 REST, SDK, CLI, MCP로 실행하세요.
Nodaro Cloud에서 사용할 수 있습니다
트렌드 API는 미리 만들어진 Cast Swap 레시피를 실행합니다. 트렌드는 슬롯이 있는 짧은 클립입니다. 슬롯은 교체할 수 있는 인물, 반려동물, 제품, 장소입니다. 슬롯을 내 사진으로 채우고 트렌드를 실행하면 그 사진이 들어간 클립을 받습니다.
트렌드는 Nodaro Cloud의 기능입니다. 셀프 호스팅 설치에서는 모든 라우트가 404로 응답합니다. 라우트는 Bearer 토큰을 받습니다. 인증을 참조하세요.
여기에는 게시된 트렌드만 있습니다. 초안, 게시되지 않은 슬러그, 알 수 없는 슬러그는 모든 라우트에서 404입니다.
엔드포인트
| 메서드 | 경로 | 하는 일 |
|---|---|---|
GET | /v1/trends | 카탈로그 자체의 순서로 된 카탈로그입니다. { data: TrendCard[], next }. 쿼리: q(검색), tag, favorites=1(내 즐겨찾기만), locale, cursor, limit(1~60, 기본값 30). |
GET | /v1/trends/:slug | 트렌드 하나입니다. { data: TrendDetail }이며, 슬롯, 예시, 미리보기 길이, 해상도가 들어 있습니다. 쿼리: locale. |
POST | /v1/trends/:slug/estimate | 실행 견적을 받습니다. 무료입니다. { data: { expectedCredits, holdCredits, breakdown } }로 응답합니다. |
POST | /v1/trends/:slug/runs | 실행을 시작합니다. 202 { data: { jobId } }로 응답합니다. GET /v1/jobs/:id로 작업을 추적하세요. |
GET | /v1/trends/runs | 내 트렌드 실행을 최신순으로 반환합니다. { data: TrendRun[], next }. 쿼리: trend(슬러그), cursor, limit(1~60, 기본값 30). |
PUT | /v1/trends/:slug/favorite | 트렌드를 즐겨찾기에 추가합니다. 204로 응답합니다. |
DELETE | /v1/trends/:slug/favorite | 트렌드를 즐겨찾기에서 제거합니다. 204로 응답합니다. |
이름, 설명, 슬롯 레이블, 힌트는 ?locale=이 플랫폼 언어이면 그 언어로 옵니다. 그렇지 않으면 내 프로필의 언어로, 그다음에는 영어로 옵니다.
트렌드가 새 버전으로 바뀌거나 게시가 취소된 뒤에도 실행은 GET /v1/trends/runs에서 계속 읽을 수 있습니다.
트렌드에 담긴 것
GET /v1/trends/:slug는 다음을 반환합니다.
slots: 각 슬롯의key,label,kind(person,pet,product,location중 하나),required, 선택 사항인hint, 기본 칩.examples: 각 예시의 결과 동영상과 각 슬롯이 쓴 사진.previewSec: 트렌드가 4초 미리보기 실행을 제공하면4, 아니면null.resolutions: 트렌드를 실행할 수 있는 해상도.
GET /v1/trends의 카탈로그 카드에는 슬러그, 이름, 태그, needs(종류별 슬롯 수), 길이(초), isFavorite가 담깁니다.
실행 본문
POST /v1/trends/:slug/estimate와 POST /v1/trends/:slug/runs는 같은 본문 { slots, example?, details?, preview?, resolution?, draft?, sessionId?, projectId? }를 받습니다.
| 필드 | 하는 일 |
|---|---|
slots | 필수입니다. 슬롯 키 → { images?, …chips }이며, 슬롯마다 한 대상의 이미지 1~3장을 받습니다. 필수 슬롯에는 모두 이미지가 있어야 합니다. |
example | 선택 사항입니다. 1부터 시작하는 예시 번호입니다. 비워 둔 선택 슬롯을 이 예시의 사진이 채웁니다. 기본값은 1입니다. |
details | 선택 사항입니다. 내 문구로, 최대 1,000자입니다. 슬롯은 {slot:<key>}로 언급하세요. |
preview | 선택 사항입니다. 트렌드의 previewSec가 4이면 true로 4초를 실행합니다. |
resolution | 선택 사항입니다. 기본값인 "480p", "720p", "1080p" 중 하나입니다. |
draft | 선택 사항입니다. 기본값은 true입니다. 초안은 480p에서만 쓸 수 있습니다. |
sessionId | 선택 사항입니다. 받아서 확인하지만 아직 적용되지 않습니다. 트렌드 실행은 아직 세션에 보관되지 않습니다. |
projectId | 선택 사항입니다. 받지만 현재 실행에는 쓰이지 않습니다. |
칩은 슬롯 종류의 고유 특성입니다. 각 칩은 내 사진으로 만드는 "new", 또는 클립 그대로 유지하는 "original"입니다.
| 슬롯 종류 | 칩 |
|---|---|
person | body, outfit, hair |
pet | body, coat |
product | shape, finish |
location | 없음 |
{
"slots": {
"dancer": { "images": ["https://example.com/me.jpg"], "outfit": "original" }
},
"preview": true,
"resolution": "480p"
}견적
견적은 실행에 청구될 것으로 예상되는 expectedCredits와, 실행이 시작될 때 예약되는 holdCredits로 응답합니다. breakdown은 견적의 구성 항목을 나열합니다.
반복 요청
POST /v1/trends/:slug/runs는 8~200자의 Idempotency-Key 헤더를 받습니다. 같은 키로 반복된 요청은 첫 실행의 작업으로 응답합니다. 새로 시작되거나 청구되는 것은 없습니다.
권한
OAuth 앱에는 다음이 필요합니다.
- 카탈로그, 트렌드, 내 실행을 읽으려면
jobs:read. - 실행 견적을 받거나 시작하려면
workflows:execute,sessionId를 보내려면assets:write도. - 즐겨찾기에는
assets:write.
오류
거부의 본문은 { error: { code, message } }입니다. 슬롯 오류에는 슬롯의 키도 slot으로 담깁니다. 견적 라우트와 실행 라우트의 400 거부는 아무것도 청구되기 전에 일어납니다.
| 상태 | code | 발생 시점 |
|---|---|---|
400 | validation_error | 본문이나 쿼리가 유효하지 않거나, cursor가 이 목록이 준 값이 아니거나, Idempotency-Key가 8~200자가 아닙니다. |
400 | missing_slot | 필수 슬롯에 이미지가 없거나, 선택 슬롯을 비워 두었는데 선택한 예시에 그 슬롯의 사진이 없습니다. |
400 | unknown_slot | slots가 트렌드에 없는 키를 지정합니다. |
400 | invalid_choice | 슬롯의 종류가 받지 않는 칩입니다. 예를 들어 인물의 coat나, 장소에 붙은 칩입니다. |
400 | example_out_of_range | example이 트렌드에 없는 예시를 지정하거나, 선택 슬롯을 비워 두었는데 트렌드에 그 슬롯을 채울 예시가 없습니다. |
400 | preview_unavailable | 미리보기를 제공하지 않는 트렌드에 preview: true를 보냈습니다. |
400 | invalid_slot_mention | details가 트렌드에 없는 키로 {slot:<key>}를 언급합니다. |
400 | prompt_too_long | 이 트렌드에는 details가 너무 깁니다. 줄이세요. |
401 | unauthorized | 인증된 사용자가 없습니다. |
404 | not_found | 트렌드가 게시되지 않았거나, sessionId가 내가 쓸 수 없는 세션을 가리킵니다. |
502 | engine_error | 동영상 엔진이 예상하지 못한 방식으로 응답했습니다. |
503 | engine_unsupported | 이 플랫폼 버전에서는 아직 트렌드를 실행할 수 없습니다. |
503 | trend_unavailable | 지금은 트렌드를 실행할 수 없습니다. |
503 | not_available | 이 서버에는 아직 트렌드 카탈로그가 없습니다. GET /v1/trends/runs를 제외한 모든 라우트에 해당합니다. |
동영상 엔진이 견적이나 실행을 거부하면, 트렌드 라우트는 자체 코드와 고정 메시지로 응답합니다.
| 상태 | code | 발생 시점 |
|---|---|---|
400 | trend_run_rejected | 엔진이 아래에 없는 이유로 이 입력을 거부했습니다. |
400 | clip_out_of_range, preview_out_of_range, video_unreadable | 트렌드 자체의 클립을 있는 그대로 실행할 수 없습니다. |
400 | unsafe_url | 사진이 공개 http 또는 https 주소가 아닙니다. |
400 | raw_reference_token | details에 {image:, {video:, {audio:가 들어 있습니다. |
402 | insufficient_credits | 내 크레딧으로 실행 비용을 감당할 수 없습니다. 워크스페이스에서는 거부한 한도에 따라 budget_exceeded, member_cap_exceeded, user_allowance_exceeded로 응답할 수도 있습니다. |
413 | storage_limit_exceeded | 내 저장 공간이 가득 찼습니다. |
422 | job_blocked | 배포 환경의 작업 정책이 실행을 막았습니다. |
429 | rate_limited | 요청이 너무 많습니다. 잠시 후 다시 시도하세요. |
503 | engine_unsupported, estimate_unavailable | 지금은 실행하거나 견적을 낼 수 없습니다. 잠시 후 다시 시도하세요. |
502 | engine_error | 그 밖의 모든 경우입니다. |
끝나지 않은 실행
GET /v1/trends/runs에서 끝나지 않은 실행에는 error: { code, message }가 있습니다. 메시지는 항상 같은 고정 문장이므로 코드로 분기하세요.
code | 의미 |
|---|---|
safety-block, policy-block | 작업이 차단되었습니다. |
credits | 실행 도중 크레딧이 부족해졌습니다. 환불되었습니다. 크레딧을 충전한 뒤 다시 실행하세요. |
cancelled | 실행이 취소되었습니다. |
failed | 그 밖의 모든 실패입니다. |
실패하지도 취소되지도 않은 실행은 error: null입니다. 완료된 실행에는 videoUrl, posterUrl, creditsCharged가 담깁니다. 실행의 각 슬롯은 그 사진이 예시에서 왔는지(fromExample) 알려 줍니다.
SDK에서 사용
client.trends는 이 라우트들을 감쌉니다. @nodaro/sdk/trends로도 가져올 수 있습니다.
list(params?: ListTrendsParams): Promise<TrendPage<TrendCard>>
get(slug: string, params?: { locale?: string }): Promise<TrendDetail>
estimate(slug: string, request: TrendRunRequest): Promise<TrendEstimate>
run(slug: string, request: TrendRunRequest, options?: { idempotencyKey?: string }): Promise<{ jobId: string }>
runs(params?: ListTrendRunsParams): Promise<TrendPage<TrendRun>>
favorite(slug: string): Promise<void>
unfavorite(slug: string): Promise<void>list()와 runs()는 도착한 그대로의 페이지 { data, next }를 반환합니다. next를 cursor로 다시 넘기세요. 마지막 페이지에서는 null입니다. get(), estimate(), run()은 { data } 봉투 없이 페이로드를 반환합니다. "runs"는 슬러그가 될 수 없습니다. 슬러그를 받는 메서드는 요청을 보내기 전에 이 값에 대해 TypeError를 발생시킵니다.
import type { TrendRunRequest } from "@nodaro/sdk"
const { data: cards } = await client.trends.list({ q: "dance" })
const trend = await client.trends.get(cards[0].slug)
const request: TrendRunRequest = {
// Every required slot needs 1–3 pictures; optional slots left out take the example's.
slots: Object.fromEntries(
trend.slots.filter((s) => s.required).map((s) => [s.key, { images: [myPhotoUrl] }]),
),
}
const quote = await client.trends.estimate(trend.slug, request)
console.log(`about ${quote.expectedCredits} credits, ${quote.holdCredits} held while it runs`)
const { jobId } = await client.trends.run(trend.slug, request, { idempotencyKey: crypto.randomUUID() })
const job = await client.jobs.get(jobId)CLI에서 사용
nodaro trends list --search dance
nodaro trends get <slug>
nodaro trends estimate <slug> --slot dancer=https://example.com/me.jpg --choice dancer.outfit=original
nodaro trends run <slug> --slot dancer=https://example.com/me.jpg --idempotency-key <key>
nodaro trends runs --trend <slug>
nodaro trends favorite <slug>--slot은 반복할 수 있으며, 슬롯마다 사진 1~3장을 받습니다. trends run은 크레딧을 쓰므로 먼저 trends estimate를 실행하세요. CLI 명령을 참조하세요.
MCP에서 사용
네 MCP 도구가 트렌드를 다룹니다. list_trends, get_trend, estimate_trend, run_trend입니다. MCP › 동영상을 참조하세요. confirm 없이 호출한 run_trend는 견적을 반환하고 아무것도 청구하지 않습니다. 사용자가 가격을 수락한 뒤 confirm: true로 호출하면 실행을 시작하고 작업 ID를 반환합니다.
자주 묻는 질문
Nodaro에서 트렌드란 무엇인가요?
트렌드는 미리 만들어진 Cast Swap 레시피입니다. 교체할 수 있는 인물, 반려동물, 제품, 장소를 위한 슬롯이 있는 짧은 클립입니다. 슬롯을 내 사진으로 채우면 그 사진이 들어간 클립을 받습니다.
트렌드 실행 비용은 어떻게 알 수 있나요?
실행할 때와 같은 본문으로 POST /v1/trends/:slug/estimate를 호출하세요. 무료입니다. 실행에 청구될 것으로 예상되는 expectedCredits와, 실행 중에 예약되는 holdCredits를 반환합니다.
모든 슬롯을 채워야 하나요?
아니요. 필수 슬롯에는 모두 사진 1~3장이 필요합니다. 비워 둔 선택 슬롯은 트렌드 예시의 사진을 쓰며, 기본값은 첫 번째 예시입니다.
트렌드 실행을 안전하게 재시도할 수 있나요?
네. 8~200자의 같은 Idempotency-Key 헤더를 보내세요. 같은 키로 반복된 요청은 첫 실행의 작업으로 응답하며, 새로 시작되거나 청구되는 것은 없습니다.
트렌드는 셀프 호스팅 설치에서도 작동하나요?
아니요. 트렌드는 Nodaro Cloud의 기능입니다. 셀프 호스팅 설치에서는 모든 트렌드 라우트가 404로 응답합니다.
관련 문서
Cast Swap
작업
동영상
명령
최종 업데이트