# 크레딧

> Nodaro 크레딧 잔액과 내역을 확인하고, 요금제와 가격을 나열하고, 실행 전에 모델과 실행 가격을 알아보며, 예약과 환불이 어떻게 작동하는지 확인합니다.

Source: https://nodaro.ai/ko/docs/developers/api/credits

**크레딧**은 Nodaro Cloud에서 결제에 쓰는 단위이며, API는 항상 금액이 아닌 크레딧으로 보고합니다. 크레딧 엔드포인트는 잔액과 크레딧 내역을 반환하고, 실행 전에 모델과 실행 가격을 알려 주며, 클라이언트가 연결된 배포 환경이 사용량에 따라 과금하는지 알려 줍니다.

크레딧 엔드포인트는 Nodaro Cloud에만 있습니다. Community Edition과 Business 에디션에는 크레딧 시스템이 없으므로 이 라우트가 `404`를 반환합니다. 단, `GET /v1/billing/surface`는 예외로 모든 에디션에서 응답합니다.

## 엔드포인트
| 메서드 | 경로 | 설명 |
| --- | --- | --- |
| `GET` | `/v1/credits/balance` | 내 잔액을 가져옵니다. 총 크레딧, 구독 크레딧, 충전 크레딧과 내 등급입니다. |
| `GET` | `/v1/user/credits` | 일일 사용량을 포함한 더 자세한 잔액 정보를 가져옵니다. |
| `GET` | `/v1/credits/transactions` | 내 크레딧 내역을 페이지 단위로 가져옵니다. |
| `POST` | `/v1/credits/model-costs` | 공개 라우트입니다. 최대 50개 모델 id의 크레딧 가격을 가져옵니다. |
| `GET` | `/v1/credits/model-cost` | 공개 라우트입니다. `?model=`로 보낸 모델 id 하나의 크레딧 가격을 가져옵니다. |
| `POST` | `/v1/credits/video-pro-estimate` | **동영상 생성 Pro** 실행의 가격입니다. [작업](https://nodaro.ai/docs/developers/api/jobs#estimate-a-run)을 참조하세요. |
| `POST` | `/v1/credits/seedance-ref-estimate` | 레퍼런스 동영상이 있는 Seedance 2 실행의 가격입니다. [Seedance 레퍼런스 동영상 견적](#seedance-reference-video-estimates)을 참조하세요. |
| `GET` | `/v1/billing/plans` | 공개 라우트입니다. 판매 중인 요금제와 크레딧 팩을 가격과 함께 가져옵니다. |
| `GET` | `/v1/billing/subscription` | 내 구독과 내 계정이 따르는 등급입니다. |
| `GET` | `/v1/billing/surface` | 공개 라우트입니다. 이 배포 환경이 사용량을 어떻게 과금하는지 알려 줍니다. |
| `GET` | `/v1/billing/account` | 배포 환경의 결제 시스템에서 가져온 내 계정 요약입니다. |
| `POST` | `/v1/jobs/cost-summary` | 여러 작업의 크레딧 합계입니다. |

공개 라우트에는 토큰이 필요하지 않습니다. 나머지 라우트는 다른 모든 라우트와 같은 토큰을 받습니다. 개인 API 토큰, OAuth 토큰, 세션 JWT 중 하나를 보내세요.

## 잔액 읽기
**curl**

```bash
curl -s https://app.nodaro.ai/v1/credits/balance \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{ "total": 1250, "subscription": 1000, "topup": 250, "tier": "pro", "effectiveTier": "pro" }
```

**TypeScript SDK**

```ts
const balance = await client.credits.balance()
console.log(`${balance.total} credits (${balance.effectiveTier})`)
```

`total`은 `subscription`과 `topup`을 더한 값입니다. `tier`는 저장된 구독 등급으로, `free`나 `pro` 등이 있습니다. `effectiveTier`는 Nodaro가 실제로 적용하는 등급입니다. `payg`는 구독은 없지만 크레딧을 구매한 상태로, 모든 모델이 열리고 워터마크와 일일 한도가 없습니다.

SDK의 `client.credits.balance()`가 읽는 `GET /v1/user/credits`는 더 많은 정보를 반환합니다.

| 필드 | 의미 |
| --- | --- |
| `total`, `subscription`, `topup` | 위와 같은 내 크레딧입니다. |
| `dailySpent` | 오늘 사용한 크레딧입니다. |
| `dailyLimit` | 일일 사용 한도이며, 한도가 없으면 `null`입니다. |
| `monthlyAllocation` | 요금제가 결제 주기마다 지급하는 크레딧입니다. |
| `tier`, `effectiveTier` | 저장된 등급과 실제로 적용되는 등급입니다. |
| `features` | 내 등급의 기능입니다. |
| `periodEnd` | 결제 기간이 끝나는 시각입니다. |
| `appCreditsAllowance` | 앱 사용으로 얻은 크레딧이며, 무료 등급에만 있습니다. |

크레딧을 쓸 때는 구독 크레딧이 먼저 차감됩니다. 구독 크레딧은 연간 요금제에서도 매달 요금제의 월간 할당량으로 초기화되며, 쓰지 않은 구독 크레딧은 이월되지 않습니다. 충전 크레딧은 구매일로부터 12개월 동안 유효합니다.

배포 환경에서 환영 혜택을 진행하는 동안에는 `GET /v1/user/credits`에 `welcomeOffer: { popupSeen, consentPending, credits }`도 담깁니다. `credits`는 혜택을 받으면 지급되는 크레딧 수이므로, 고정된 숫자 대신 이 값을 보여 주세요. 진행 중인 혜택이 없으면 이 키는 없습니다. 혜택은 Nodaro 웹 앱에서 받습니다. 두 라우트인 `POST /v1/credits/welcome-offer/claim`과 `POST /v1/credits/welcome-offer/seen`은 로그인한 브라우저 세션만 받으며, API 토큰이나 OAuth 앱 토큰에는 `403 forbidden`으로 응답합니다.

## 크레딧 차감 방식
- **작업이 시작될 때 예약됩니다.** 생성은 실행 전에 가격만큼 크레딧을 예약합니다. 잔액이 부족하면 호출이 `402 insufficient_credits`를 반환하며, `required`와 대부분의 계정에서는 `balance`가 함께 옵니다. 워크플로 실행에서는 각 노드가 시작될 때 가격을 예약하며, 잔액이 부족한 노드는 실패합니다.
- **결과를 전달하면 차감됩니다.** 예약이 차감으로 바뀝니다.
- **전달하지 못하면 환불됩니다.** 모델의 안전 필터나 배포 환경의 정책으로 차단된 생성은 항상 환불되며, 취소된 작업도 환불됩니다.
- **실제로 실행된 값으로 가격이 매겨집니다.** 서버가 선택한 모델에 맞게 설정을 수정하면, 예약도 수정된 값을 따릅니다. [매개변수 수정](https://nodaro.ai/docs/developers/api/workflows#parameter-corrections)을 참조하세요.

작업의 `credit_status`와 거래의 `status`는 같은 단계를 거칩니다. `reserved` 다음에 `committed` 또는 `refunded`가 됩니다. 따라서 작업 자체에서 크레딧이 돌아왔는지 알 수 있습니다. [작업의 크레딧](https://nodaro.ai/docs/developers/api/jobs#credits-of-a-job)을 참조하세요.

## 거래 내역
`GET /v1/credits/transactions`는 내 크레딧 내역을 최신순으로 반환합니다.

| 쿼리 | 의미 |
| --- | --- |
| `limit` | 1~50입니다. 기본값은 20입니다. |
| `cursor` | 이전 페이지의 `nextCursor`입니다. 마지막 행의 `created_at` 시각입니다. |

```bash
curl -s "https://app.nodaro.ai/v1/credits/transactions?limit=2" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{
"data": [
{
"id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"created_at": "2026-09-26T14:03:11.284Z",
"credits_charged": 50,
"credits_reserved": 50,
"credits_used": 50,
"action": "generate-image",
"provider": "nano-banana-pro",
"status": "committed",
"metadata": { "model": "nano-banana-pro", "from_sub": 50, "from_topup": 0 },
"payer": "user",
"workspaceId": null
}
],
"nextCursor": "2026-09-26T14:03:11.284Z"
}
```

| 필드 | 의미 |
| --- | --- |
| `credits_charged` | 정산이 끝난 뒤 실행에 실제로 든 비용입니다. 예약액보다 적을 수 있고, 예약이 환불되었으면 `0`이며, 예약이 아직 열려 있는 동안(`status: "reserved"`)에는 `null`입니다. 지출을 계산하려면 이 필드를 합산하세요. |
| `credits_reserved` | 실행이 접수될 때 예약된 크레딧입니다. 이 값은 바뀌지 않습니다. |
| `credits_used` | `credits_reserved`와 같은 예약액을 원래 이름으로 담은 필드로, 기존 연동을 위해 유지됩니다. 일부만 정산되어도 바뀌지 않으므로, 그런 실행에서는 청구액보다 큰 값을 보여 줍니다. `credits_charged`를 사용하세요. |
| `action`, `provider` | 무엇이 어떤 모델에서 실행되었는지입니다. |
| `status` | `reserved`, `committed`, `refunded` 중 하나입니다. |
| `payer` | 내 잔액이면 `user`, 수업이나 팀 예산이면 `workspace`입니다. |
| `workspaceId` | 비용을 낸 워크스페이스 또는 `null`입니다. |
| `metadata` | 실행이 어떻게 청구되었는지입니다. 항상 객체이며, 해당하는 내용이 없으면 `{}`입니다. |

**어느 금액을 읽어야 하나요?** 실행은 먼저 크레딧을 예약합니다. 그다음 실행이 정산됩니다. 예약액의 전부 또는 일부가 청구되고, 나머지는 잔액으로 돌아갑니다. 일부 실행은 예약액보다 적게 정산되는 경우가 많습니다. 예를 들어 다시 렌더링할 가능성까지 감안한 예약, 실행 후에 측정되는 길이, 사용량에 따라 과금되는 실행이 그렇습니다. 따라서 예약액은 실제로 낸 금액이 아닙니다. 예를 들어 888크레딧을 예약하고 444크레딧이 청구된 실행은 `credits_charged: 444`, `credits_reserved: 888`, `credits_used: 888`, `status: "committed"`로 표시됩니다. 나머지 444크레딧은 잔액으로 돌아갑니다.

`status`는 예약의 진행 상태입니다. `reserved`(크레딧이 예약되었고 아직 확정되지 않음), `committed`(실행이 결과를 전달해 청구됨, 청구액은 `credits_charged`) 또는 `refunded`(예를 들어 안전 차단 후 예약이 해제됨)입니다. 작업 페이로드에는 같은 진행 상태가 `credit_status`로, 정산된 금액이 `credits_charged`로 표시됩니다. 따라서 작업 비용을 알기 위해 거래 내역을 볼 필요가 없습니다. [작업의 크레딧](https://nodaro.ai/docs/developers/api/jobs#credits-of-a-job)을 참조하세요.

`metadata`에는 해당하는 경우에만 다음 키가 들어갑니다. `model`, `from_sub`와 `from_topup`(어느 잔액에서 결제했는지), `is_app_run`, `allowance_delta`, `web_free_mode`, `status`, `loop_trim_refunded`, `surround_refine_refunded`입니다.

더 이상 행이 없으면 `nextCursor`는 `null`입니다.

## 실행 전에 가격 확인
| 도구 | 가격을 알려 주는 대상 |
| --- | --- |
| `POST /v1/credits/model-costs` | 한 번에 최대 50개 모델 id입니다. |
| `GET /v1/credits/model-cost` | 모델 id 하나입니다. |
| `GET /v1/models` | 모든 모델이며, 변형마다 `pricing` 항목이 있습니다. [모델 찾기](https://nodaro.ai/docs/developers/api/nodes#discover-models)를 참조하세요. |
| `GET /v1/nodes/:type` | 노드의 `creditCost`로, 단일 가격 또는 범위입니다. |
| `GET /v1/api/schema` | 워크플로 전체를 `estimatedCredits`로 알려 줍니다. [워크플로](https://nodaro.ai/docs/developers/api/workflows#run-a-workflow-with-new-input-values)를 참조하세요. |
| `POST /v1/credits/video-pro-estimate` | 아무것도 예약하지 않고 **동영상 생성 Pro** 실행의 가격을 알려 줍니다. [작업](https://nodaro.ai/docs/developers/api/jobs#estimate-a-run)을 참조하세요. |
| `POST /v1/credits/seedance-ref-estimate` | 레퍼런스 동영상이 있는 Seedance 2 실행을 실행과 같은 방식으로 측정해 알려 줍니다. [아래](#seedance-reference-video-estimates)를 참조하세요. |
| `POST /v1/recast/estimate` | Recast 실행입니다. [Recast](https://nodaro.ai/docs/developers/api/recast)를 참조하세요. |
| `POST /v1/pro-3d-render/quote` | **3D 렌더링 Pro** 실행입니다. [3D 장면](https://nodaro.ai/docs/developers/api/3d-scenes)을 참조하세요. |

모델 가격, 노드 가격, `estimatedCredits`는 실행에 실제로 청구되는 가격이며, 에디터의 **실행** 버튼에 표시되는 숫자와 같습니다. **동영상 자르기**, **동영상 루프**, **동영상 합치기**의 노드 `creditCost`는 5초 단위 한 번의 가격이며, 실행 비용은 그 단위가 몇 번인지에 따라 정해집니다. `estimatedCredits`에서 [**동영상 효과음**](https://nodaro.ai/docs/nodes/video/video-sfx#credits) 노드는 8초 가격으로 계산됩니다. 클립 길이는 실행이 시작될 때에야 측정되기 때문입니다. 실행에서는 측정된 길이의 가격이 청구됩니다.

모델 가격은 설정에 따라 달라질 수 있으므로, 변형 id에 설정이 들어갑니다. 예: `nano-banana-pro:4K`, `gpt-image-2:2K`.

**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/credits/model-costs \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"models": ["nano-banana-pro", "nano-banana-pro:4K", "gpt-image-2:2K"]}'
```

```json
{
"data": { "nano-banana-pro": 50, "nano-banana-pro:4K": 66, "gpt-image-2:2K": 42 },
"missing": [],
"errors": []
}
```

**TypeScript SDK**

```ts
const { data, missing } = await client.credits.modelCosts(['nano-banana-pro', 'nano-banana-pro:4K'])
console.log(data['nano-banana-pro:4K'])
if (missing.length) console.warn('No price for:', missing)
```

### 단위별로 가격이 매겨지는 실행의 정확한 가격
일부 실행은 단위별로 가격이 매겨지며, 실행은 전체로 가격을 계산한 뒤 한 번만 반올림합니다. 따라서 단위 가격에 단위 수를 곱한 값이 실행 비용보다 클 수 있습니다. 이런 실행의 견적을 내려면 해당 행의 N단위 정확한 가격인 식별자 `<row>:<N>u`를 요청하세요. `POST /v1/credits/model-costs`와 `GET /v1/credits/model-cost` 모두 이 식별자에 답합니다.

| 실행 | 단위 | 예시 id |
| --- | --- | --- |
| 음성 요청 | 시작된 100자 단위마다, 8부터 모델의 요청당 상한까지 | `elevenlabs-v4:per-100-chars:12u` |
| EDL 적용 또는 화자 뷰 렌더링 | 1분마다 | `apply-edl:4u` |
| 동영상 자르기, 동영상 루프, 동영상 합치기, 내레이션 동영상 만들기, 마스크 합성 | 5초마다 | `trim-video:5u` |
| LTX 2.3 Pro 연장 또는 리테이크 | 1초마다, N은 소수일 수 있음 | `ltx-2.3-pro-retake:per-second:2.5u` |
| 더빙 | 시작된 1분마다, 1~30 | `elevenlabs-dubbing:3u`, 히브리어는 `elevenlabs-dubbing-v2:3u` |

단위별로 청구되지 않는 행이나, 그 행의 어떤 실행도 가질 수 없는 단위 수는 `missing`에 들어갑니다. 음성을 길이로 가격을 매기지 않는 설치에서는 모든 `<model>:per-100-chars:<N>u` id도 `missing`에 들어갑니다. 그런 설치에서는 모델의 고정 가격 행으로 견적을 내세요.

[보이스 체인저 Pro](https://nodaro.ai/docs/nodes/audio/voice-changer-pro#credits) 변환 전체에는 고유한 식별자가 있습니다. `voice-changer-pro:<slot>,<slot>,…`이며, 음색을 바꾸는 보이스마다 슬롯이 하나씩 들어갑니다. 슬롯은 음성 대 음성 보이스이면 스템 길이에 따라 `sts-<seconds>`입니다. 다시 읽기 보이스는 v3에서 `respeak-<characters>`, v4에서 `respeak-v4-<characters>`, v4로 번역을 말할 때 `respeak-v4t-<characters>`입니다. 번역에는 녹취록의 글자 수를 보내세요. 다시 렌더링되는 대사에 대한 여유분은 서버가 더합니다. 측정값을 모르면 그냥 종류만 쓰세요. 예: `voice-changer-pro:sts-26.76,respeak-v4-1200,respeak-v4t-800`. 두 엔드포인트 모두 변환이 예약하는 크레딧으로 답합니다. 변환은 합계를 한 번 반올림해 청구하므로, 보이스별 가격을 더해도 그 값이 되지 않습니다. 슬롯이 8개를 넘거나, 스템이 1시간보다 길거나, 슬롯 형식이 잘못되면 `missing`에 들어갑니다. `@nodaro/shared` 패키지의 `voiceChangerProQuoteCreditId(slots)`로 이 식별자를 만들 수 있으며, 다시 읽기 슬롯은 선택 항목인 `engine`(`"v3"` 또는 `"v4"`)과 `translated` 플래그를 받습니다.

가격이 없는 id는 `missing`에, 조회에 실패한 id는 `errors`에 들어가므로, 잘못된 id 하나 때문에 요청 전체가 실패하지는 않습니다. 이런 id에는 0이 아니라 대시를 표시하세요. 위 가격은 예시입니다. 실시간 응답을 읽거나, [모델](https://nodaro.ai/docs/models)에서 각 모델 페이지를 확인하세요.

CLI에는 크레딧 명령이 없지만, `nodaro models list`로 각 모델의 크레딧 등급을 볼 수 있습니다.

### Seedance 레퍼런스 동영상 견적
레퍼런스 동영상이 있는 Seedance 2 실행은 레퍼런스 클립의 초와 출력 초를 더해 청구되므로, 가격은 클립의 실제 길이에 따라 달라집니다. 레퍼런스 동영상이 연결된 [동영상 생성](https://nodaro.ai/docs/nodes/video/generate-video)과, 원본 클립이 레퍼런스 동영상 1이 되는 [동영상으로 동영상 만들기](https://nodaro.ai/docs/nodes/video/video-to-video)의 Seedance 2.5 Edit 선택지가 여기에 해당합니다. `POST /v1/credits/seedance-ref-estimate`는 클립을 측정해 실행의 예약과 똑같이 가격을 계산합니다. 작업을 만들지 않고 아무것도 예약하지 않습니다. 토큰이 필요하며, 분당 60회 요청을 허용합니다.

```json
{ "provider": "seedance-2-5", "resolution": "720p", "duration": 8, "referenceVideoUrls": ["https://example.com/clip.mp4"] }
```

| 필드 | 타입 | 의미 |
| --- | --- | --- |
| `provider` | string | `seedance-2`, `seedance-2-fast`, `seedance-2-mini`, `seedance-2-5` 중 하나. 다른 모델은 `400 validation_error`로 응답합니다. |
| `resolution` | string, 선택 사항 | 실행이 보내는 값 그대로입니다. 생략하면 모델의 기본값입니다. |
| `duration` | number, 선택 사항 | 출력 초. `-1`은 자동을 뜻하며, 실행이 예약하는 것처럼 모델의 가장 긴 클립으로 가격을 계산합니다. 생략하면 모델의 기본값입니다. |
| `draft` | boolean, 선택 사항 | Seedance 2.5 초안이며, 480p로 가격을 계산합니다. |
| `referenceVideoUrls` | string[] | 실행이 보낼 레퍼런스 클립을 순서대로 담습니다. |
| `unknownReferenceVideos` | number, 선택 사항 | 업스트림 노드가 아직 만들지 않아 파일이 아직 없는 연결된 클립의 수입니다. |

응답은 `{ "data": { "credits", "min", "exact" } }`입니다. 모든 클립을 지정하면 `credits`는 실행이 예약하는 값과 정확히 같고, `min`도 같습니다. 측정할 수 없는 클립은 실행에서처럼 모델이 받는 가장 긴 클립으로 계산합니다. `unknownReferenceVideos`가 있으면 `exact`는 `false`입니다. `credits`는 알 수 없는 각 클립을 모델이 받는 가장 긴 클립으로, `min`은 가장 짧은 클립으로 계산합니다. 실행이 거부할 클립은 여기서도 실행과 같은 `400 video_too_long`으로 거부됩니다. 편집기도 연결된 클립이 아직 만들어지지 않은 동안 노드에 같은 범위를 표시합니다.

## 요금제와 가격
`GET /v1/billing/plans`는 판매 중인 요금제와 크레딧 팩을 각각의 가격과 함께 나열합니다. **공개** 라우트입니다. 토큰이 필요 없고 읽지도 않으므로, 가격 페이지는 누군가 로그인하기 전에도 이 라우트를 호출할 수 있습니다. 모든 호출자가 같은 응답을 받으며, `Cache-Control: public, max-age=300`과 함께 전송됩니다. Nodaro Cloud에서만 응답합니다. SDK에서는 `client.billing.plans()`, CLI에서는 `nodaro billing plans`이며, 로그인이 필요 없습니다.

```text
{
"data": [
{ "id": "free", "name": "Free", "prices": null,
"signupCredits": <credits>, "parallelism": <n>, "storageBytes": <bytes> },
{ "id": "basic", "name": "Basic",
"prices": {
"monthly": { "amount": <amount>, "currency": "USD",
"credits": { "amount": <credits>, "per": "month" } },
"yearly":  { "amount": <amount>, "currency": "USD", "perMonth": <amount>,
"credits": { "amount": <credits>, "per": "month" } }
},
"signupCredits": null, "parallelism": <n>, "storageBytes": <bytes> }
],
"topUps": [
{ "id": "topup-10", "credits": <credits>, "price": { "amount": <amount>, "currency": "USD" } }
],
"topUpRange": { "min": <amount>, "max": <amount>, "currency": "USD" }
}
```

| 필드 | 의미 |
| --- | --- |
| `data` | 요금제입니다. 무료 요금제가 먼저 오고, 그다음 유료 요금제가 작은 것부터 옵니다. `id`는 바뀌지 않습니다: `free`, `basic`, `standard`, `pro`, `business`. |
| `prices` | 무료 요금제는 `null`입니다. 그 밖에는 `monthly`와 `yearly`가 있습니다. `amount`는 통화의 주 단위 금액, `currency`는 ISO 4217 코드이며, `yearly.perMonth`는 연간 금액을 월별로 나눈 값입니다. 지금 판매할 수 없는 가격은 `null`입니다. 무료가 아니라 이용할 수 없다고 표시하세요. |
| `prices.*.credits` | 그 가격으로 지급되는 크레딧: `{ amount, per }`이며, `per`는 `"month"` 또는 `"year"`입니다. 연간 가격도 월간 가격처럼 매달 요금제의 크레딧을 지급합니다. 가격에 명시되지 않았으면 `null`입니다. |
| `signupCredits` | 무료 요금제의 가입 시 일회성 지급량입니다. 유료 요금제에서는 크레딧이 각 가격과 함께 오므로 `null`입니다. |
| `parallelism` | 한 워크플로에서 동시에 실행되는 노드 수입니다. |
| `storageBytes` | 내 미디어용 저장 공간이며, 바이트 단위입니다. |
| `topUps` | 크레딧 팩 `{ id, credits, price }`이며, 작은 것부터 옵니다. 충전 크레딧은 12개월 동안 유효합니다. 지금 가격을 명시할 수 없으면 `price`는 `null`입니다. |
| `topUpRange` | `{ min, max, currency }`: 이 범위 안의 정수 금액이라면 얼마든 앱에서 팩과 같은 단가로 크레딧을 충전할 수 있습니다. |

| 상태 | 코드 | 경우 |
| --- | --- | --- |
| `403` | `self_serve_disabled` | 배포 환경이 사용자에게 판매하지 않습니다. |
| `503` | `stripe_not_configured` | 배포 환경이 카드 결제를 받지 않습니다. |
| `503` | `plan_prices_unavailable` | 가격을 읽을 수 없었습니다. 잠시 후 다시 시도하세요. |

구매는 앱에서 이루어집니다. 요금제를 판매하려면 사용자를 Nodaro 앱의 요금제 페이지로 보내세요.

### 내 요금제
`GET /v1/billing/subscription`은 로그인한 세션, 워크플로 제한이 없는 개인 API 토큰, 앱의 OAuth 토큰에 응답합니다. 워크플로가 제한된 토큰은 `403 forbidden`을 받습니다. 응답은 `{ data, effectiveTier }`입니다.

- **`data`**는 계정의 최신 구독이며, 구독한 적이 없으면 `null`입니다. `tier`, `status`(`active`, `trialing`, `past_due`, `paused`, `canceled`, `incomplete`, `expired`), `current_period_start`, `current_period_end`, `cancel_at_period_end`, `cancel_at`, `canceled_at`이 담깁니다.
- **`effectiveTier`**는 계정의 기능이 따르는 등급입니다. 대개 `GET /v1/billing/plans`의 요금제 `id`입니다. 구독 없이 크레딧을 구매한 계정이라면 `"payg"`일 수도 있고, `"enterprise"`처럼 판매하지 않는 등급일 수도 있습니다.

현재 요금제를 표시하려면 `effectiveTier`를 요금제 목록의 `data[].id`와 비교하세요. 일치하는 것이 없으면 종량제이거나 판매하지 않는 등급입니다. SDK에서는 `client.billing.subscription()`, CLI에서는 `nodaro billing subscription`입니다.

## 종량제
API를 사용하는 데 구독은 필요하지 않습니다.

- **구매하면 바로 활성화됩니다.** 크레딧 팩을 구매하거나, 결제 페이지에서 $5~$1,000 사이의 금액을 1달러 단위로 충전하면 계정이 종량제로 전환됩니다. 많이 충전할수록 크레딧당 단가가 낮아집니다.
- **모든 기능이 열립니다.** `effectiveTier`가 `payg`가 되어, 모든 모델을 쓸 수 있고 결과에 워터마크가 없으며 일일 사용 한도도 없습니다.
- 구매일로부터 **크레딧이 12개월 동안 유효합니다**.
- **개발자용 환경에서 씁니다.** 종량제 크레딧은 API, SDK, CLI, MCP에서 사용할 수 있습니다. 웹 에디터에는 구독이 필요하며, 종량제 계정이 웹 에디터에서 크레딧을 쓰면 `403 subscription_required`를 받습니다. 토큰 호출에서는 이 오류가 발생하지 않습니다.
- 꾸준히 사용한다면 **구독이 크레딧당 더 저렴합니다**.

알아 둘 점이 두 가지 있습니다.

- **결과는 기본적으로 공개됩니다.** 비공개 결과는 표준 요금제 이상의 구독 기능이므로, 종량제 결과는 공개 갤러리에 표시됩니다. MCP로 만든 작업은 항상 비공개입니다.
- **계정이 활성 상태인 동안 미디어가 유지됩니다.** 구매도 크레딧 사용도 없이 약 3개월이 지나면, 60일보다 오래된 파일이 삭제될 수 있습니다. 크레딧을 다시 사용하면 정리가 중단됩니다. 정리는 생성 작업이 자기 결과로 저장한 파일만 삭제하며, 결과가 되풀이한 입력이나 내 라이브러리 항목이 아직 쓰는 파일은 삭제하지 않습니다. 파일이 삭제되면 그 파일을 담은 모든 작업 결과에서 링크가 `null`로 바뀝니다.

구매 자체는 웹 앱에서 이루어집니다. 결제 진행, 충전, 요금제 변경, 자동 충전, 구매 내역, 결제 포털을 다루는 결제 라우트는 API 토큰과 OAuth 토큰을 거부합니다. 결제는 [app.nodaro.ai/billing](https://app.nodaro.ai/billing)에서 관리하세요.

## 배포 환경의 과금 방식 읽기
두 라우트를 쓰면 클라이언트가 배포 환경의 결제 방식을 가정하지 않고도 비용과 사용량 화면을 만들 수 있습니다.

<strong>`GET /v1/billing/surface`</strong>는 토큰이 필요 없으며 모든 사용자에게 같은 응답을 반환합니다.

```json
{
"data": {
"contract": 2,
"providerId": "…",
"displayUnit": "credits",
"canReport": true,
"canQuote": true,
"canAccount": true,
"mountCostTab": true,
"deploymentPayer": false
}
}
```

- `displayUnit`은 비용 화면에 기본으로 표시할 단위입니다. 예: `credits`, `usd`.
- 크레딧이 없는 Community 설치에서는 `providerId`가 `none`이고 `mountCostTab`이 `false`입니다. 이때는 비용 화면을 표시하지 마세요.
- `deploymentPayer`는 하나의 결제 계정이 모든 사용자의 비용을 낼 때 `true`입니다. 그 계정이 어느 계정인지는 표시되지 않습니다.

<strong>`GET /v1/billing/account`</strong>는 내 계정 요약을 `{ "data": … }` 형태로 반환합니다. 결제 서비스가 응답하지 못하면 `data`가 `null`입니다. 이때는 잔액 0이 아니라 “사용할 수 없음”으로 표시하세요. 요약에는 항상 다음 필드가 있습니다.

| 필드 | 의미 |
| --- | --- |
| `plan` | 내 요금제를 나타내는 문자열입니다. `unknown`도 유효한 응답입니다. |
| `balance` | 내 잔액 또는 `null`입니다. |
| `dailyAllowance` | 내 일일 허용량 또는 `null`입니다. |
| `unit` | 이 숫자들의 단위입니다. |

배포 환경은 `periodStart`, `generations`, `spent`, `payg`, `daily`, `reserveValue`, `byCategory` 같은 선택 필드를 추가할 수 있으며, 클라이언트는 받은 필드만 표시합니다. 금액은 `{ amount, currency }` 객체입니다. `daily`에서 `limit`이 `0`이면 무제한이 아니라 차단을 뜻하며, 둘 다 있으면 `daily`가 `dailyAllowance`보다 우선합니다. 모든 `null`은 0이 아니라 “사용할 수 없음”을 뜻하므로 대시를 표시하세요.

<strong>`POST /v1/jobs/cost-summary`</strong>는 여러 작업의 크레딧을 합산합니다. 최상위와 각 세부 행의 `total_credits`는 숫자 또는 `null`입니다. 응답에는 `unit`이 명시되고, 가격을 매길 수 없었던 작업 수가 `unavailable`에 들어갑니다. `null` 합계는 일괄 처리의 어떤 작업에도 알려진 청구 금액이 없었다는 뜻입니다. 0이라는 뜻이 아닙니다.

## 워크스페이스와 공유 결제
- **워크스페이스 예산.** 조직의 워크스페이스 안에서의 사용은 내 잔액이 아니라 워크스페이스 예산으로 결제되며, 그 거래에는 `payer: "workspace"`가 붙습니다. 예산을 넘는 실행은 `402 budget_exceeded`를, 그 워크스페이스에서 내 한도를 넘는 실행은 `402 member_cap_exceeded`를 반환합니다. [워크스페이스와 조직](https://nodaro.ai/docs/developers/api/workspaces#budgets-and-usage)을 참조하세요.
- **결제 계정이 하나인 배포 환경.** 하나의 결제 계정이 모든 사용자의 비용을 내는 배포 환경에서는 `GET /v1/user/credits`에 내 허용량을 크레딧으로 나타낸 `allowance: { granted, remaining, enforced }`가 추가됩니다. `enforced: false`는 허용량이 표시되기만 하고 실행을 막지는 않는다는 뜻입니다. 결제 계정 자신이거나 허용량을 읽지 못한 경우 `allowance`는 `null`이므로, `null`을 0으로 해석하지 마세요. 배포 환경이 허용량을 적용하면, 내 허용량을 넘는 실행은 `402 user_allowance_exceeded`를 반환합니다. 결제 계정은 자신의 브라우저 세션에서만 풀을 관리합니다. 토큰으로 잔액을 읽으면 `403 payer_balance_jwt_only`가 반환됩니다. 자체 지갑으로 사용을 승인하는 배포 환경은 [외부 지갑](https://nodaro.ai/docs/developers/external-wallet)을 참조하세요.

## 자주 묻는 질문

### API로 크레딧 잔액을 확인하려면 어떻게 하나요?

GET /v1/credits/balance를 보내세요. total, subscription, topup 크레딧과 tier, effectiveTier를 반환합니다. SDK에서는 client.credits.balance()가 일일 사용량까지 포함한 더 자세한 정보를 반환합니다.

### API로 생성하면 크레딧은 언제 차감되나요?

크레딧은 작업이 시작될 때 예약되고, 결과를 전달할 때 차감됩니다. 작업이 안전 필터나 정책으로 차단되거나 실행 전에 취소되면 예약된 크레딧이 환불됩니다. 어떻게 처리되었는지는 작업의 credit_status에서 확인할 수 있습니다.

### 구독 없이 Nodaro API를 사용할 수 있나요?

네. 크레딧 팩을 하나라도 구매하면 종량제가 활성화되며, 모든 모델이 열리고 워터마크와 일일 한도가 없습니다. 종량제 크레딧은 API, SDK, CLI, MCP에서 사용할 수 있으며 12개월 동안 유효합니다.

### 실행을 시작하기 전에 비용을 알려면 어떻게 하나요?

POST /v1/credits/model-costs는 최대 50개 모델 id의 크레딧 가격을, GET /v1/models는 모든 모델의 가격 등급을 반환하고, GET /v1/api/schema는 워크플로 전체의 예상 비용을 계산합니다. 동영상 생성 Pro에는 별도의 예상 비용 라우트가 있습니다.

### 내 가격 페이지에 Nodaro의 요금제와 가격을 보여 줄 수 있나요?

네. GET /v1/billing/plans는 공개되어 있어 토큰이 필요 없습니다. 요금제, 월간 및 연간 가격, 각 요금제가 지급하는 크레딧, 크레딧 팩을 나열합니다. 구매는 Nodaro 앱에서 이루어집니다.

### 셀프 호스팅 설치에서도 크레딧 엔드포인트가 작동하나요?

아니요. Community Edition과 Business 에디션에는 크레딧 시스템이 없으므로 크레딧 라우트가 404를 반환하며, 노드와 모델 정보에서도 크레딧 가격이 빠집니다. 모델 공급자에게 직접 비용을 지불합니다.
