API 사용
CastLine 의 스트림·공유 링크·외부 송출은 /api/v1 REST API 로도 다룰 수 있습니다. 요청·응답은 JSON 이고 필드 이름은 camelCase 입니다.
API 키 인증
- 콘솔 관리 → API 키(
/console/api-keys)에서 소유자가 키를 발급합니다. 키 역할은manager(스트림·링크·송출 생성·수정·켜기·끄기) 또는viewer(조회만) 중에서 고릅니다. - 키(
ck_…)는 발급할 때 한 번만 보입니다. 잃어버리면 폐기하고 새로 발급하세요. - 모든 요청에
Authorization헤더를 붙입니다.
Authorization: Bearer ck_…curl -H "Authorization: Bearer ck_…" "https://castline.dotoritos.net/api/v1/streams?limit=20"curl -X POST -H "Authorization: Bearer ck_…" "https://castline.dotoritos.net/api/v1/streams/<streamId>/on"- 키는 자기 고객사 자원만 다룹니다. 다른 고객사 자원은
404 NOT_FOUND입니다. - 등급의 API 키 한도가 0 이면 키를 발급할 수 없습니다(
403 FEATURE_NOT_ALLOWED). - 비밀번호 변경·로그인 세션 관리·탈퇴·콘솔 미리보기처럼 사람 계정이 필요한 작업은 API 키로 할 수 없습니다.
- 해지된 고객사의 키는
410 CUSTOMER_TERMINATED, 정지된 고객사의 변경 요청은423 CUSTOMER_SUSPENDED를 받습니다.
API 키는 서버에만 두세요
웹페이지·앱 코드에 넣으면 누구나 볼 수 있습니다. 유출이 의심되면 콘솔에서 즉시 폐기하세요. 키의 모든 변경 작업은 활동 기록에 “API 키 이름”으로 남습니다.
예약 켜기·끄기 (cron)
영업시간에만 송출하려면 서버의 스케줄러(cron 등)가 manager 역할 키로 일괄 켜기·끄기를 부르게 합니다(한 번에 최대 50개).
curl -X POST -H "Authorization: Bearer ck_…" -H "Content-Type: application/json" -d '{"ids":["<streamId>"],"action":"on"}' "https://castline.dotoritos.net/api/v1/streams/bulk"# manager 역할 API 키로 평일 09:00 켜기, 18:00 끄기
CASTLINE_KEY=ck_…
0 9 * * 1-5 curl -fsS -X POST -H "Authorization: Bearer $CASTLINE_KEY" -H "Content-Type: application/json" -d '{"ids":["<streamId>"],"action":"on"}' https://castline.dotoritos.net/api/v1/streams/bulk
0 18 * * 1-5 curl -fsS -X POST -H "Authorization: Bearer $CASTLINE_KEY" -H "Content-Type: application/json" -d '{"ids":["<streamId>"],"action":"off"}' https://castline.dotoritos.net/api/v1/streams/bulk일괄 요청은 항목별 결과를 207 Multi-Status 로 돌려줍니다. 일부만 실패할 수 있으니 ok 가 false 인 항목의 error.code 를 확인하세요.
{
"data": {
"results": [
{ "id": "<streamId>", "ok": true },
{ "id": "<streamId2>", "ok": false, "error": { "code": "RESOURCE_LOCKED", "message": "관리자가 잠근 자원입니다." } }
]
}
}응답과 오류 형식
성공하면 {"data": …, "meta": {…}} 를, 실패하면 아래 형식을 돌려줍니다. message 는 사람이 읽는 한국어 문구이고, 프로그램은 code 로만 분기하세요. 모든 응답에는 X-CastLine-Request-Id 헤더가 있으며, 문의할 때 이 값(requestId)을 알려 주면 빠르게 찾을 수 있습니다.
{
"error": {
"code": "QUOTA_EXCEEDED",
"message": "동시 활성 스트림 한도(5개)를 초과했습니다.",
"details": { "limitKey": "max_active_streams", "limit": 5, "current": 5, "requested": 1 },
"requestId": "req_…"
}
}- 수정 가능한 자원은
version을 돌려줍니다.PATCH에If-Match: <version>을 보내면 그 사이 다른 사람이 바꿨을 때409 VERSION_CONFLICT로 알려 줍니다. - 목록의 알 수 없는 쿼리 파라미터와 본문 필드는
422 VALIDATION_FAILED(rule=unknown_field) 입니다.
오류 코드
| code | HTTP | 의미 / details |
|---|---|---|
| VALIDATION_FAILED | 422 | 입력 검증 실패. details.fields: [{field, rule, message}]. 초대·재설정 토큰을 제출했는데 쓸 수 없으면 details.reason |
| INVALID_SOURCE_URL | 422 | 원본·목적지 URL 오류. details.reason: SCHEME_NOT_ALLOWED, HTTP_SOURCE_NOT_HLS, PRIVATE_ADDRESS, SELF_ADDRESS, DNS_FAILED, MALFORMED |
| UNAUTHENTICATED | 401 | 세션·API 키 없음 또는 만료 |
| INVALID_CREDENTIALS | 401 | 이메일·비밀번호 불일치 |
| ACCOUNT_LOCKED | 423 | 로그인 잠금. details.lockedUntil |
| ACCOUNT_DISABLED | 403 | 비활성 사용자, 또는 해지된 고객사 멤버의 로그인 |
| FORBIDDEN | 403 | 역할 권한 없음. details.action (권한 행 ID) |
| CSRF_FAILED | 403 | Origin 불일치 (쿠키 인증 변경 요청) |
| NOT_FOUND | 404 | 없음, 또는 다른 고객사의 자원 |
| CONFLICT | 409 | 유일성 위반(이름 등). details.field |
| VERSION_CONFLICT | 409 | 낙관적 잠금 실패(If-Match). details.currentVersion |
| LAST_OWNER | 409 | 마지막 소유자는 제거·강등·비활성화·탈퇴할 수 없음 |
| LAST_SUPER_ADMIN | 409 | 마지막 최고 관리자 보호 (스태프 전용) |
| SELF_ACTION_FORBIDDEN | 409 | 자기 계정 비활성화·강등·삭제 (스태프 전용) |
| TIER_IN_USE | 409 | 고객이 배정된 등급 삭제 (스태프 전용). details.customerCount |
| QUOTA_EXCEEDED | 403 | 개수 한도 초과. details: {limitKey, limit, current, requested} |
| PROTOCOL_NOT_ALLOWED | 403 | 등급이 허용하지 않는 프로토콜. details: {limitKey, protocol, allowed} |
| FEATURE_NOT_ALLOWED | 403 | 등급이 허용하지 않는 기능(임베드, 비밀번호 링크, on-demand, API 키). details.limitKey |
| PLATFORM_CAPACITY | 503 | 플랫폼 전체 수용량 도달 — 잠시 뒤 다시 시도 |
| CUSTOMER_SUSPENDED | 423 | 정지된 고객사의 변경 요청 |
| CUSTOMER_TERMINATED | 410 | 해지된 고객사의 API 키 요청 |
| RESOURCE_LOCKED | 423 | 관리자가 잠근 스트림·송출. details: {reason, lockedResource} |
| LINK_NOT_FOUND | 404 | 공개 API: 무효·폐기 토큰 |
| LINK_EXPIRED | 410 | 공개 API: 만료된 링크 |
| LINK_DISABLED | 403 | 공개 API: 비활성·한도정지 링크 |
| LINK_PASSWORD_REQUIRED | 401 | 공개 API: 링크 비밀번호 필요 |
| LINK_PASSWORD_INVALID | 401 | 공개 API: 링크 비밀번호 불일치 |
| LINK_PASSWORD_LOCKED | 429 | 공개 API: 비밀번호 실패가 많아 일시 거절. Retry-After, details.until |
| MAIL_UNAVAILABLE | 503 | 메일 발송이 설정되지 않아 처리할 수 없음 (비밀번호 재설정 등) |
| EMBED_NOT_ALLOWED | 403 | 임베드 비허용 또는 허용 도메인 불일치 |
| VIEWER_KICKED | 403 | 공개 API: 강제 종료된 시청자의 재접속 차단 중. details.until |
| STREAM_NOT_LIVE | 409 | 미리보기·티켓 요청 시 스트림이 꺼져 있음 |
| MEDIA_SERVER_UNAVAILABLE | 503 | 미디어 서버에 연결할 수 없음 (상태 조회 계열) |
| RATE_LIMITED | 429 | 속도 제한 초과. Retry-After 헤더(초) 뒤 다시 시도 |
| INTERNAL_ERROR | 500 | 서버 오류. 문의 시 requestId 를 알려 주세요 |
속도 제한
한도를 넘으면 429 RATE_LIMITED 와 Retry-After 헤더(초)를 받습니다. 그 시간만큼 기다린 뒤 다시 시도하세요. 같은 스트림의 “다시 연결”(POST /streams/{id}/resync)은 30초에 한 번입니다.
| 대상 | 한도 |
|---|---|
| API 키 요청 | API 키당 300회 / 1분 |
| 로그인한 사용자(웹 화면) 요청 | 사용자당 600회 / 1분 |
| 고객 자료 내보내기 | 사용자당 10회 / 1시간 |
| 공개 재생 티켓 | IP당 30회 / 1분 |
| 로그인 시도 | IP당 10회 / 1분 |
| 비밀번호 재설정 메일 요청 | IP당 5회 / 1분 |
| 초대·재설정 토큰 제출 | IP당 20회 / 1분 |
페이지네이션과 정렬
목록은 커서 방식입니다. limit 은 기본 20, 최대 100 이고, 응답의 meta.nextCursor 를 다음 요청의 cursor 로 넘깁니다. nextCursor 가 null 이면 마지막 페이지입니다. 커서는 그 목록 전용이라 다른 목록이나 조건에 쓰면 422(rule=invalid_cursor)입니다.
curl -H "Authorization: Bearer ck_…" "https://castline.dotoritos.net/api/v1/streams?limit=50&cursor=<meta.nextCursor>"정렬은 sort=<필드>:asc 또는 sort=<필드>:desc 로 지정합니다(예: sort=name:asc). 목록마다 정렬할 수 있는 필드가 정해져 있고, 그 밖의 값은 422 입니다.