워크스페이스와 조직
X-Nodaro-Workspace로 Nodaro API 요청을 조직의 워크스페이스에서 실행하고, 토큰을 워크스페이스에 연결하고, 멤버, 초대, 공유, 사용량을 관리하세요.
Nodaro Cloud에서 사용할 수 있습니다
워크스페이스는 조직의 멤버가 함께 작업하는 곳으로, 학교의 반이나 회사의 팀이 해당됩니다. API 요청은 X-Nodaro-Workspace 헤더에 워크스페이스 ID를 보내면 그 워크스페이스에서 실행되며, 조직 엔드포인트는 그 주변의 모든 것을 관리합니다. 조직, 멤버, 초대, 참여 코드, 공유, 예산, 사용량이 여기에 해당합니다.
조직은 인스턴스별로 켜는 Nodaro Cloud 기능입니다. 조직이 켜져 있지 않은 곳에서는 아래 엔드포인트가 존재하지 않고 헤더도 무시됩니다. 셀프 호스팅 빌드에는 조직이 포함되지 않습니다. 개념은 워크스페이스에서 확인하세요.
워크스페이스에서 작업
X-Nodaro-Workspace: 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34이 헤더가 하는 일은 딱 두 가지입니다. 목록이 어느 워크스페이스를 반환할지와 새로 만든 것이 어디에 저장될지를 정합니다. 액세스 권한을 주지는 않습니다. ID로 지정한 대상을 읽고, 수정하고, 삭제하고, 실행하는 요청은 그 객체가 속한 워크스페이스를 따릅니다. 헤더를 빠뜨려도 내 작업이 가려지지 않으며, 헤더를 위조해도 다른 사람의 작업에 닿을 수 없습니다.
헤더가 없으면 조직이 없는 계정과 마찬가지로 개인 공간에서 작업합니다.
curl -s https://app.nodaro.ai/v1/workflows \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "X-Nodaro-Workspace: 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34"// Every request of this client acts in the workspace:
const classClient = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
workspaceId: '6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34',
})
// Or derive a second client from an existing one:
const classroom = client.withWorkspace(workspaceId)
await classroom.workflows.run(workflowId) // lands in the class
await client.workflows.run(workflowId) // lands in the personal spacenodaro --workspace 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34 workflows list # this command only
export NODARO_WORKSPACE=6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34 # this shell or CI job
nodaro workspace use 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34 # saved on the profile
nodaro workspace current # which one applies, and whyclient.withWorkspace는 기존 클라이언트를 바꾸지 않고 새 클라이언트를 반환합니다. 그래서 동시에 실행되는 두 작업이 어느 워크스페이스에 있는지를 두고 경합하는 일이 없습니다. 개인 공간에는 null을 넘기세요. CLI에서는 세 가지 방법 중 위에 있는 방법이 아래 방법보다 우선하며, nodaro workspace use는 저장하기 전에 워크스페이스를 확인합니다.
헤더는 내가 속한 워크스페이스에 대해서만 보내세요.
| 상태 | 코드 | 발생 시점 |
|---|---|---|
| 400 | validation_error | 값이 UUID가 아닙니다. |
| 403 | not_a_member | 그 워크스페이스의 멤버가 아니거나, 워크스페이스가 없거나, 그 조직이 활성 상태가 아닙니다. |
| 403 | member_suspended | 내 멤버십이 정지되었습니다. |
다음 요청은 오래된 선택을 거부하지 않으므로, 이 때문에 접근이 막히는 일은 없습니다. GET /v1/me, GET /v1/workspaces, 초대 읽기와 수락, 그리고 코드로 워크스페이스에 참여하기입니다. 이런 요청에서는 더 이상 선택할 수 없는 워크스페이스를 헤더를 보내지 않은 것처럼 처리합니다. GET /v1/me와 GET /v1/workspaces는 선택할 수 있는 워크스페이스를 알려 주므로, 여기에 더 이상 나오지 않는 워크스페이스는 캐시한 선택에서 지우세요.
토큰을 워크스페이스에 연결
API 토큰은 워크스페이스 하나에 연결할 수 있습니다. 연결한 뒤에는 모든 요청에서 그 헤더를 보낸 것처럼 작동하며, 다른 워크스페이스를 명시한 헤더에는 400 token_workspace_mismatch가 반환됩니다.
# Bind
curl -X PATCH https://app.nodaro.ai/v1/api-tokens/$TOKEN_ID \
-H "Authorization: Bearer $SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"workspaceId": "6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34"}'
# Unbind
curl -X PATCH https://app.nodaro.ai/v1/api-tokens/$TOKEN_ID \
-H "Authorization: Bearer $SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{"workspaceId": null}'토큰을 관리하려면 로그인한 세션의 JWT가 필요합니다. API 토큰이나 OAuth 토큰으로는 403 forbidden이 반환됩니다. 토큰은 헤더로 선택할 수 있는 워크스페이스에만 연결할 수 있으며, 연결 해제는 언제든지 가능합니다. 토큰 목록에는 연결된 워크스페이스가 workspaceId로 나옵니다.
새로 만든 것이 저장되는 곳
- 워크스페이스 안에서 만든 것은 그 워크스페이스에 저장되며, 개인 공간에 저장되지 않습니다. 프로젝트를 지정하지 않은 만들기는 워크스페이스의 프로젝트로 들어갑니다. 워크스페이스에 아직 프로젝트가 없으면
409 workspace_has_no_default_project가 반환되므로, 프로젝트를 지정하면 성공합니다. - 지정한 프로젝트는 작업 중인 워크스페이스에 속해야 합니다. 다른 워크스페이스나 개인 공간의 프로젝트를 지정하면
404 Project not found가 반환됩니다. 존재하지 않는 프로젝트와 같은 응답이므로, 헤더로 무엇이 존재하는지 알아낼 수 없습니다. - 프로젝트 만들기는 관리자로 제한될 수 있습니다. 이 경우 멤버에게는
403 project_create_not_allowed가 반환됩니다. - 조직은 개인 공간을 닫을 수 있습니다. 이 경우 멤버는 워크스페이스 안에서만 만들 수 있고, 헤더 없는 만들기에는
403 personal_space_disabled가 반환됩니다. 조직이 없는 계정은 영향을 받지 않습니다.
보관된 워크스페이스는 읽기 전용
워크스페이스를 보관하면 그 안의 모든 것은 계속 읽을 수 있지만, 새 작업은 추가할 수 없습니다. 목록은 이전과 같이 작동합니다. 프로젝트, 워크플로, 가져오기, 하위 워크플로 등 모든 만들기에는 409 workspace_archived가 반환되며, 작업을 워크스페이스 안으로 옮길 때도 마찬가지입니다. 작업을 밖으로 옮기는 것은 계속 허용됩니다. 보관된 워크스페이스를 여는 이유가 작업을 꺼내기 위해서이기 때문입니다. 워크플로 공유처럼 그 안에 쓰는 다른 요청에는 403 workspace_archived가 반환됩니다. 상태가 아니라 코드로 판단하세요.
내 정보: GET /v1/me
GET /v1/me는 내 프로필을 반환합니다. 조직이 있는 인스턴스에서는 organizations, workspaces, lastWorkspaceId도 담기며, 각 항목에는 내 역할과 상태가 있습니다. 조직 항목에는 그 조직이 워크스페이스를 부르는 단어 같은, 확정된 vocabulary가 포함되므로 클라이언트가 “반”이나 “팀”을 하드코딩할 필요가 없습니다. GET /v1/workspaces는 같은 워크스페이스 목록만 따로 반환합니다.
조직 필드의 세 가지 상태를 구분하세요.
| 보이는 것 | 의미 | 해야 할 일 |
|---|---|---|
| 필드가 없음 | 이 인스턴스에는 조직이 없습니다. | 워크스페이스 전환 메뉴를 표시하지 마세요. |
| 필드가 있지만 비어 있음 | 이 계정은 어느 조직에도 속하지 않습니다. | 조직을 만들거나 참여하도록 안내하세요. |
organizationsUnavailable: true | 조회에 실패했습니다. | 기존 선택을 유지하세요. |
조직 엔드포인트
모든 엔드포인트에는 인증이 필요합니다. 본문은 JSON이고, 응답은 { "data": … }, 목록은 { "data": [ … ], "nextCursor": … }이며, ID는 UUID입니다.
조직
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
POST | /v1/orgs | 로그인한 누구나 | { name, kind, slug?, acceptTerms?, settings? }이며, kind는 school 또는 team입니다. 201을 반환합니다. |
GET | /v1/orgs | 누구나 | 내 조직 목록을 내 role, memberStatus와 함께 반환합니다. |
GET | /v1/orgs/:id | 멤버 | |
PATCH | /v1/orgs/:id | 소유자, 관리자 | { name?, settings? }. 설정은 키별로 병합되며, 알 수 없는 키는 버려집니다. |
POST | /v1/orgs/:id/transfer-ownership | 소유자 | { userId }. 새 소유자는 활성 관리자여야 하며, 이전 소유자는 같은 단계에서 관리자가 됩니다. |
DELETE | /v1/orgs/:id | 소유자 | 보관되지 않은 워크스페이스가 하나라도 있으면 409 has_active_workspaces로 거부됩니다. 조직은 소프트 삭제되고 슬러그는 해제됩니다. |
POST | /v1/orgs/:id/leave | 멤버 | 소유자는 나갈 수 없으므로(409 owner_cannot_leave) 먼저 소유권을 이전하세요. 나가면 조직의 모든 워크스페이스에서도 제거됩니다. |
- 승인. 새 조직은 보통
pending으로 시작하고, 플랫폼이 승인하면active가 됩니다. 기다리는 동안에는 소유자만 조직을 볼 수 있고 다른 사람은 볼 수 없으며, 그 안의 어떤 것도 바꿀 수 없습니다. 만든 사람에게 이 사실을 알려 주세요. 그렇지 않으면 대기 상태가 실패처럼 보입니다. - 학교는 약관 동의가 필요합니다. 학교를 만들려면
acceptTerms: true가 필요하며, 이로써 소유자는 학생을 등록할 권한이 있음을 확인합니다. 이 값이 없으면400 terms_required가 반환됩니다. - 슬러그는 영문 소문자, 숫자, 하이픈으로 이루어진 1~50자입니다. 보내는 슬러그는 사용 중이 아니어야 합니다(
409 name_taken). 보내지 않으면 이름에서 만들어집니다. “Sunrise School”은sunrise-school이 되고, 그다음은sunrise-school-2가 됩니다. - 조직 만들기는 사용자마다 시간당 몇 개로 제한됩니다.
멤버
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
GET | /v1/orgs/:id/members | 소유자, 관리자 | ?limit=50&cursor=…, 최대 200. 행: userId, role, status, joinedAt, email, displayName, avatarUrl. |
PATCH | /v1/orgs/:id/members/:userId | 소유자, 관리자 | { role?, status? }: admin 또는 member, active 또는 suspended. 소유자의 행에는 쓸 수 없습니다. |
DELETE | /v1/orgs/:id/members/:userId | 소유자, 관리자 | 소유자에게는 쓸 수 없습니다. 그 사람을 조직의 모든 워크스페이스에서도 제거합니다. |
워크스페이스
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
POST | /v1/orgs/:id/workspaces | 소유자, 관리자 | { name, slug?, description?, settings? }. 201을 반환합니다. 슬러그는 조직 안에서 고유합니다. |
GET | /v1/orgs/:id/workspaces | 멤버 | 멤버는 자신의 워크스페이스를, 소유자와 관리자는 모든 워크스페이스를 봅니다. ?includeArchived=true를 붙이면 보관된 워크스페이스도 포함됩니다. |
GET | /v1/workspaces | 누구나 | 조직과 관계없이 내가 속한 모든 워크스페이스입니다. |
GET | /v1/workspaces/:id | 워크스페이스 멤버 | |
PATCH | /v1/workspaces/:id | 워크스페이스 관리자 | { name?, description?, settings? } |
POST | /v1/workspaces/:id/archive | 소유자, 관리자 | 보관합니다. 되돌릴 수 있으며 아무것도 삭제되지 않습니다. |
POST | /v1/workspaces/:id/unarchive | 소유자, 관리자 | 보관을 해제합니다. |
워크스페이스 멤버
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
GET | /v1/workspaces/:id/members | 워크스페이스 멤버 | ?limit&cursor. 멤버는 userId, role, displayName, avatarUrl, addedAt을 봅니다. 관리자는 status와 creditCap도 봅니다. 명단에 이메일 주소는 없습니다. |
POST | /v1/workspaces/:id/members | 워크스페이스 관리자 | { userId, role }이며, role은 admin 또는 member입니다. 그 사람은 이미 조직의 활성 멤버여야 합니다(400 not_org_member). 새로운 사람을 들이려면 초대하세요. |
PATCH | /v1/workspaces/:id/members/:userId | 워크스페이스 관리자 | { role?, status?, creditCap? }. creditCap은 멤버의 사용 한도이며, 한도가 없으면 null입니다. |
DELETE | /v1/workspaces/:id/members/:userId | 워크스페이스 관리자 | 그 사람을 이 워크스페이스에서만 제거합니다. 조직에는 남습니다. |
조직의 소유자와 관리자는 목록에 나오지 않아도 조직의 모든 워크스페이스에서 관리자입니다. 이들 중 한 명을 워크스페이스에 명시적으로 추가하면 대신 그 명시적인 역할이 적용됩니다. 이렇게 하면 조직 관리자가 어떤 반에서는 일반 멤버가 될 수 있습니다.
초대
초대 하나는 이메일 주소 하나를 대상으로 합니다. 초대 링크에는 이메일에만 있는 토큰이 담기며, 초대받은 사람은 로그인하기 전에 무엇에 초대되었는지 확인할 수 있습니다.
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
POST | /v1/orgs/:id/invitations | 소유자, 관리자, 그리고 조직이 허용하면 자기 워크스페이스로 초대하는 워크스페이스 관리자 | { emails, orgRole?, workspaceId?, workspaceRole? }. 주소는 최대 200개이며, 소문자로 바뀌고 중복은 제거됩니다. 주소마다 한 행씩 { email, status, link? }를 담아 201을 반환합니다. |
GET | /v1/orgs/:id/invitations | 위와 같은 사람 | ?status=(open, accepted, revoked, expired), workspaceId, limit, cursor. 토큰은 반환하지 않습니다. |
DELETE | /v1/invitations/:id | 위와 같은 사람 | 철회합니다. 링크가 더 이상 작동하지 않습니다. 수락된 초대에는 거부됩니다. |
POST | /v1/invitations/:id/resend | 위와 같은 사람 | 새 토큰과 만료 시각을 발급하고 다시 보냅니다. 이전 링크는 더 이상 작동하지 않습니다. |
GET | /v1/invitations/by-token/:token | 공개 | 초대받은 사람이 결정하는 데 필요한 정보입니다. orgName, kind, vocabulary, inviterName, workspaceName, 가려진 이메일 주소, expiresAt, state. IP 주소별로 제한됩니다. |
POST | /v1/invitations/:token/accept | 로그인한 초대 대상자 | 계정의 이메일이 초대와 일치해야 합니다(400 email_mismatch). 두 번째 수락은 거부됩니다. |
- 주소마다 자체 행이 있습니다.
status는sent,link_only,failed중 하나이며, 이메일을 보내지 못한 주소에는 항상link가 있습니다. 메일 공급자가 없는 설치 환경은 모든 주소를link_only로 반환합니다. 이 링크를 표시하세요. 어느 경우든 초대는 만들어져 있고, 링크가 없으면 아무도 초대에 들어갈 수 없습니다. - 초대는 14일 뒤에 만료되며, 조직은 하루에 500개까지 보낼 수 있습니다(
429 bulk_invite_cap_exceeded). - 같은 주소를 다시 초대하면 두 번째 초대를 만드는 대신 열려 있는 초대를 새 토큰으로 바꿉니다.
- 이미 멤버인 사람을 초대하면 초대는 사용 처리되지만, 그 사람의 역할이 바뀌거나 정지가 해제되지는 않습니다.
참여 코드
참여 코드는 8자로, 교실에서 소리 내어 읽기 좋은 길이입니다. 이 코드로는 워크스페이스 하나에 일반 멤버로만 참여할 수 있고, 정지는 해제되지 않으며, 조직이 허용한 이메일 도메인 제한이 적용됩니다.
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
GET | /v1/workspaces/:id/join-code | 워크스페이스 관리자 | { code, enabled, rotatedAt, rotatedBy }. 만든 코드가 없으면 null입니다. |
POST | /v1/workspaces/:id/join-code | 워크스페이스 관리자 | { action }: rotate, enable, disable. 코드가 한 번도 없었던 워크스페이스를 켜면 코드가 만들어집니다. 재발급하면 코드가 바뀌고 이전 코드는 즉시 작동하지 않습니다. |
POST | /v1/workspaces/join | 로그인한 누구나 | { code }. 소리 내어 읽은 형태도 받습니다. BCDF-GHJK, 소문자, 숫자 0 대신 쓴 O처럼 잘못 듣기 쉬운 글자도 됩니다. 계정마다 분당 10회, IP 주소마다 분당 30회로 제한됩니다. |
워크플로 하나 공유
권한 부여는 한 사람에게 워크플로 하나에 대한 액세스를 줍니다. 액세스를 더할 수만 있고 뺄 수는 없습니다.
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
GET | /v1/workflows/:id/collaborators | 워크플로를 볼 수 있는 누구나 | { userId, name, avatarUrl, role, createdAt } 행이며, 이메일 주소는 없습니다. |
POST | /v1/workflows/:id/collaborators | 아래 참고 | { userId } 또는 { email } 중 정확히 하나와 role: viewer 또는 editor. 201을 반환합니다. 분당 20명 추가로 제한됩니다. |
PATCH | /v1/workflows/:id/collaborators/:userId | POST와 같음 | { role } |
DELETE | /v1/workflows/:id/collaborators/:userId | POST와 같음, 또는 본인 | 누구나 자신의 액세스 권한을 제거할 수 있습니다. |
GET | /v1/workflows/shared-with-me | 로그인한 누구나 | 내 워크스페이스 밖에서 내가 권한을 받은 워크플로이며, 각각 grantedRole이 붙습니다. 최신순, 최대 200개입니다. |
GET | /v1/workflows/:id/access | 워크플로를 볼 수 있는 누구나 | { access, workspaceId, visibility, canChangeVisibility, canShare, canRun }. 그래프는 반환하지 않습니다. |
- 공유할 수 있는 사람: 만든 사람, 관리자가 멤버의 작업을 편집할 수 있는 워크스페이스의 워크스페이스 관리자, 그리고 공동 작업자가 초대할 수 있는 워크스페이스에서 그 워크플로를 편집할 수 있는 모든 사람입니다.
- 이메일로 추가하면 조직과 관계없이 모든 계정의 주소와 대조합니다.
404는 계정이 없다는 뜻이며, 그 이상은 공개하지 않습니다. - 워크스페이스 밖의 사람은 편집 권한을 받아도 보기만 할 수 있습니다. 실행하려면 활성 멤버십이 필요하므로, 외부 공동 작업자는 워크스페이스의 크레딧을 쓸 수 없습니다.
- 워크플로를 만든 사람이나 나 자신에게는 권한을 줄 수 없습니다(
400). 권한 부여는 수락 단계 없이 바로 적용됩니다. canChangeVisibility,canShare,canRun은 서로 별개의 규칙입니다. 하나로 다른 하나를 추측하지 마세요.
전혀 접근할 수 없는 워크플로에는 존재하지 않는 워크플로와 똑같이 404가 반환됩니다. 이미 볼 수 있는 워크플로에 더 많은 권한이 필요할 때만 403이 반환됩니다. 워크스페이스에 속한 워크플로를 실행하려면 편집 권한과 활성 멤버십이 필요합니다.
감사 로그
| 메서드 | 경로 | 권한 | 본문 또는 쿼리 |
|---|---|---|---|
GET | /v1/orgs/:id/audit | 소유자, 관리자 | ?cursor&limit, 최신순. 각 항목: { id, workspaceId, action, targetType, targetId, details, createdAt, actor }. 시스템이 수행한 작업이면 actor는 null입니다. |
감사 로그는 조직이 정지된 동안에도 읽을 수 있습니다. action은 제품과 함께 늘어나는 열린 목록입니다. 아는 작업은 표시하고, 나머지는 원래 값을 그대로 보여 주세요.
역할과 설정
| 범위 | 역할 | 할 수 있는 일 |
|---|---|---|
| 조직 | owner | 관리자가 할 수 있는 모든 일에 더해 소유권 이전과 조직 삭제. 정확히 한 명뿐입니다. 소유자는 정지, 제거, 강등될 수 없습니다. |
| 조직 | admin | 설정, 소유자를 제외한 멤버, 워크스페이스 관리. 모든 워크스페이스의 관리자입니다. |
| 조직 | member | 조직에 소속되어, 자신이 추가된 워크스페이스에서 작업. |
| 워크스페이스 | admin | 워크스페이스의 설정과 멤버 관리. |
| 워크스페이스 | member | 워크스페이스에서 작업. |
설정은 계층으로 적용됩니다. 워크스페이스 설정이 조직 설정보다 우선하고, 조직 설정은 조직 종류의 기본값보다 우선합니다. 설정되지 않은 키는 다음 계층의 값을 따르며, false도 실제 값으로 취급됩니다.
| 키 | 값 | 제어 대상 |
|---|---|---|
admin_access | view 또는 edit | 관리자가 멤버의 워크플로로 할 수 있는 일. |
default_workflow_visibility | private 또는 workspace | 새 워크플로의 공개 범위. |
member_access_to_shared | view 또는 edit | 멤버가 워크스페이스에 공유된 워크플로로 할 수 있는 일. |
members_can_create_projects | 불리언 | 멤버가 프로젝트를 만들 수 있는지 여부. |
member_caps_enabled | 불리언 | 멤버별 크레딧 한도를 적용할지 여부. |
personal_space_enabled | 불리언 | 멤버가 개인 공간을 가질 수 있는지 여부. |
workspace_admins_can_invite | 불리언 | 워크스페이스 관리자가 새 사람을 조직에 초대할 수 있는지 여부. |
collaborators_can_invite | 불리언 | 편집 권한이 있는 공동 작업자가 다른 공동 작업자를 초대할 수 있는지 여부. |
조직에만 있는 설정으로 ["school.example"] 같은 allowed_email_domains와 { "workspace": "Cohort" } 같은 vocabulary_overrides도 있습니다. 둘 다 수정하면 통째로 바뀝니다.
워크스페이스를 부르는 단어를 직접 정할 때, 그 단어가 문법상 남성형이면 vocabulary_overrides의 workspace_gender도 "m"으로 설정하세요. 예: { "workspace": "Grupo", "workspace_gender": "m" }. 그러면 워크스페이스를 언급하는 히브리어와 브라질 포르투갈어 문장이 그 단어에 맞게 일치합니다. "m"이 없으면 여성형으로 취급합니다. 이 설정은 API에만 있습니다.
학교의 초기 설정은 다음과 같습니다. 관리자는 편집 가능, 워크플로는 비공개, 멤버는 공유된 작업을 보기만 가능, 멤버는 프로젝트를 만들 수 없음, 한도 사용, 개인 공간 사용, 워크스페이스 관리자는 초대 가능, 공동 작업자는 초대 불가. 팀의 초기 설정은 다음과 같습니다. 관리자는 보기만 가능, 워크플로는 워크스페이스에 공개, 멤버는 공유된 작업을 편집 가능, 멤버는 프로젝트를 만들 수 있음, 한도 미사용, 개인 공간 사용, 두 초대 설정 모두 사용.
예산과 사용량
조직은 멤버의 작업 비용을 세 단계로 지불하며, 단계마다 범위가 좁아집니다.
- 풀. 조직은 자체 결제를 통해 조직 전체가 함께 쓰는 하나의 풀에 선불 크레딧 팩을 구매합니다.
- 할당. 소유자는 풀에서 워크스페이스의 예산으로 크레딧을 옮깁니다. 풀은 워크스페이스가 받은 만큼 정확히 줄어듭니다. 크레딧을 풀로 되돌릴 수도 있지만, 워크스페이스가 이미 예약했거나 사용한 양 아래로는 줄일 수 없습니다.
- 멤버 한도. 워크스페이스 안에서 관리자는 한 멤버가 쓸 수 있는 양에 한도를 정할 수 있습니다. 한도는 워크스페이스 설정에서 켠 경우에만 적용되며, 그 워크스페이스에서 작업하는 조직 관리자에게는 적용되지 않습니다.
워크스페이스 안에서 한 작업은 워크스페이스가 지불하며, 멤버 자신의 크레딧은 쓰이지 않습니다. 모든 단계의 여유분은 할당량에서 예약량과 사용량을 뺀 값이며, 이를 넘을 실행은 시작하기 전에 402 budget_exceeded 또는 402 member_cap_exceeded로 거부됩니다.
| 라우트 | 권한 | 기능 |
|---|---|---|
GET /v1/orgs/:id/credits | 소유자, 조직 관리자 | 풀, 누적 구매액, 그리고 각 워크스페이스의 할당량과 예약·사용된 크레딧. |
POST /v1/orgs/:id/credits/checkout | 소유자 | 선불 팩 하나의 결제 URL. 본문 { packId }. |
POST /v1/orgs/:id/workspaces/:wsId/allocate | 소유자 | 풀과 워크스페이스 사이에서 크레딧을 옮깁니다. 본문 { delta }. 새 여유분을 반환합니다. |
GET /v1/workspaces/:id/budget | 멤버 | 내 사용량, 한도, 여유분. 관리자는 멤버별 행도 받습니다. |
GET /v1/orgs/:id/usage | 소유자, 조직 관리자 | 조직의 사용량 보고서. |
GET /v1/workspaces/:id/usage | 멤버, 워크스페이스 관리자 | 워크스페이스 하나의 같은 보고서. |
환불과 결제 분쟁이 생기면 조직의 풀에서 비율에 맞춰 크레딧을 회수하며, 0 아래로는 내려가지 않습니다. 조직을 삭제하려면 먼저 모든 워크스페이스 할당량을 회수해야 합니다.
자동화는 워크스페이스의 크레딧을 씁니다. Webhook 트리거 URL은 Bearer 자격 증명입니다. URL을 가진 사람은 누구나 실행을 시작할 수 있고, 워크스페이스에 있는 워크플로의 실행은 그 워크스페이스가 지불합니다. Webhook, 일정, Telegram에서 시작된 모든 자동 실행은 먼저 트리거를 만든 사람이 여전히 그 워크플로를 실행할 수 있는지 확인합니다. 더 이상 실행할 수 없으면 자동화가 멈추고, 실행 기록에는 코드가 run_requires_authenticated_member인 실패 항목 하나가 표시됩니다.
사용량 보고서
두 사용량 라우트는 기간 동안 누가 무엇에 얼마를 썼는지 알려 줍니다.
| 쿼리 | 의미 |
|---|---|
from, to | 양 끝을 포함하는 날짜, YYYY-MM-DD. 기간은 최대 366일이며, 기본값은 최근 30일입니다. |
tz | 일별 집계에 쓰는 IANA 시간대. 기본값은 UTC입니다. |
groupBy | workspace(조직 전용), member, model, day, 또는 개별 실행을 최신순으로 cursor로 페이지를 나눠 보여 주는 none. |
workspaceId, userId | 보고서 범위를 좁힙니다. |
format | csv를 보내면 보고서를 CSV로 반환합니다. |
- 각 행에는 크레딧 수치가 세 개 있습니다.
credits는 지금까지 실행에 든 비용으로, 끝난 실행은 확정된 금액, 진행 중인 실행은 잡혀 있는 예약분입니다.settledCredits와inFlightCredits는 그 합계를 나눈 값입니다. - 워크스페이스에 남은 양보다 비용이 더 든 종량제 실행은 여유분까지만 청구되고, 나머지는 플랫폼이 부담합니다. 합계에는 이것이
platformAbsorbedCredits로 보고되며,settledCredits에서platformAbsorbedCredits를 뺀 값이 예산에 실제로 반영된 양입니다. 예산으로 충당하지 못한 승인된 앱의 추가 요금도 플랫폼이 부담하며,appMarkupAbsorbedCredits로 따로 보고됩니다. - 그룹이 5,000개를 넘는 보고서는
truncated: true를 반환합니다. 잘리는 것은 그룹별 행뿐이며, 합계는 전체 기간을 포함합니다. - 일반 멤버는 자신의 실행만 볼 수 있고, 멤버별 그룹화는 거부됩니다. 워크스페이스 관리자는 모든 사람을 보고
userId로 거를 수 있습니다. 조직 보고서는 소유자와 조직 관리자용입니다. - CSV 내보내기는 UTF-8, RFC 4180 형식이며, 줄 끝은 CRLF이고 바이트 순서 표시는 없습니다.
=,+,-,@로 시작하는 셀은 스프레드시트가 실행하지 못하도록 앞에 아포스트로피를 붙여 감쌉니다. 내보내기는 사용자마다 분당 10회로 제한되며 감사 로그에 기록됩니다. - 멤버가 계정을 삭제하면 그 사람의 실행 기록도 함께 사라지고, 보고서에는 빈 구간이 생깁니다. 그 사람의 워크플로와 프로젝트는 워크스페이스에 남습니다.
사용량 보고서가 없는 인스턴스는 404를 반환하고, 보고 기능을 아직 쓸 수 없는 동안에는 503 billing_unavailable을 반환합니다.
오류
| 상태 | 코드 | 발생 시점 |
|---|---|---|
| 400 | validation_error | 필드가 잘못되었거나, 커서 형식이 잘못되었거나, 그 행에 허용되지 않는 변경이거나, 사용량 보고서의 날짜, 시간대, 기간, 그룹화가 잘못되었습니다. |
| 400 | terms_required | acceptTerms: true 없이 학교를 만들었습니다. |
| 400 | not_org_member | 조직의 활성 멤버가 아닌 사람을 워크스페이스에 추가하려 했습니다. |
| 400 | token_workspace_mismatch | 한 워크스페이스에 연결된 토큰에 다른 워크스페이스를 지정한 헤더가 붙었습니다. |
| 400 | join_code_invalid | 그런 코드가 없거나, 코드가 꺼져 있거나, 그 워크스페이스가 보관되었습니다. 세 경우 모두 같은 응답입니다. |
| 400 | invitation_expired, invitation_revoked, invitation_accepted | 초대가 14일을 넘겼거나, 철회되었거나, 이미 수락되었습니다. |
| 400 | email_mismatch | 로그인한 계정의 이메일이 초대받은 이메일이 아닙니다. |
| 401 | unauthorized | 유효한 자격 증명이 없습니다. |
| 403 | insufficient_role | 멤버이지만 그 작업을 하기에는 역할이 낮습니다. |
| 403 | member_suspended | 내 멤버십이 정지되었습니다. |
| 403 | org_not_active | 조직이 대기 중이거나 정지되었는데, 작업이 무언가를 변경하려 합니다. |
| 403 | workspace_archived | 보관된 워크스페이스에 쓰려고 했습니다. |
| 403 | not_a_member | 헤더가 내가 선택할 수 없는 워크스페이스를 지정합니다. |
| 403 | domain_not_allowed | 조직이 목록에 있는 이메일 도메인만 받습니다. |
| 404 | not_found | 그런 조직, 워크스페이스, 멤버가 없거나, 내가 멤버가 아닌 곳입니다. |
| 404 | invitation_not_found | 그 토큰에 해당하는 초대가 없습니다. 조직이 활성 상태가 아닌 경우도 포함됩니다. |
| 409 | name_taken | 보낸 슬러그가 사용 중입니다. |
| 409 | already_a_member | 그 사람은 이미 워크스페이스에 있습니다. |
| 409 | owner_cannot_leave | 나가기 전에 소유권을 이전하세요. |
| 409 | has_active_workspaces | 조직을 삭제하기 전에 모든 워크스페이스를 보관하세요. |
| 429 | rate_limit_exceeded | 조직 만들기, 참여 시도, CSV 내보내기가 너무 많습니다. |
| 429 | bulk_invite_cap_exceeded | 조직이 하루 초대 한도에 도달했습니다. |
| 503 | billing_unavailable | 이 인스턴스에서는 아직 사용량 보고를 쓸 수 없습니다. |
| 503 | audit_unavailable | CSV 내보내기를 감사 로그에 기록하지 못해 거부되었습니다. 다시 시도하세요. |
SDK, CLI, MCP에서 사용
- SDK:
client.organizations와client.workspaces가 이 엔드포인트를 다루며, 보고서용usage,usageRows,usageCsv도 포함합니다.client.me()에는 조직 필드가 담깁니다. - CLI:
nodaro org와nodaro workspace입니다. 예를 들어nodaro org invite <orgId> --email ada@school.example --workspace <id>,nodaro org usage <orgId> --from 2026-09-01 --to 2026-09-30 --group-by member --csv > september.csv가 있습니다. 초대 명령은 주소마다 한 줄씩 출력하며, 이메일을 보내지 못한 주소에는 링크도 함께 출력합니다. - MCP:
list_workspaces와select_workspace도구입니다. 선택은 세션이 바뀌어도 기억되며, 세션마다 다시 확인합니다. 이 도구를 쓰는 OAuth 앱에는workspaces:read와workspaces:write스코프가 필요합니다.
전체 명령과 메서드 목록은 CLI와 SDK에서 확인하세요.
자주 묻는 질문
Nodaro API 요청을 워크스페이스에서 실행되게 하려면 어떻게 하나요?
X-Nodaro-Workspace 헤더에 워크스페이스 ID를 보내세요. 이 헤더가 목록이 어느 워크스페이스를 읽을지, 새로 만든 것이 어디에 저장될지를 정합니다. SDK에서는 createClient({ workspaceId }) 또는 client.withWorkspace(id)를, CLI에서는 --workspace를 사용하세요.
워크스페이스 헤더를 잘못 보내면 내 작업이 가려지거나 다른 사람의 작업에 닿을 수 있나요?
아니요. 헤더는 범위를 정할 뿐 액세스 권한을 정하지 않습니다. ID로 무언가를 읽고, 바꾸고, 삭제하고, 실행하는 요청은 그 객체가 속한 워크스페이스를 따르며, 내가 속하지 않은 워크스페이스에는 403 not_a_member가 반환됩니다.
API 토큰을 워크스페이스 하나에 묶을 수 있나요?
네. 로그인한 세션에서 PATCH /v1/api-tokens/:id에 workspaceId를 보내 연결하세요. 그러면 토큰은 모든 요청에서 그 헤더를 보낸 것처럼 작동하고, 다른 워크스페이스를 지정한 헤더에는 400 token_workspace_mismatch가 반환됩니다.
만들기 요청에 403 personal_space_disabled가 반환되는 이유는 무엇인가요?
조직이 멤버에게 워크스페이스 안에서만 작업을 만들도록 요구하기 때문입니다. 내 워크스페이스 중 하나를 지정한 X-Nodaro-Workspace 헤더를 보내면 같은 호출이 성공합니다.
셀프 호스팅 설치에서도 조직을 사용할 수 있나요?
아니요. 조직은 인스턴스별로 켜는 Nodaro Cloud 기능입니다. 셀프 호스팅 빌드에는 조직이 포함되지 않으며, 워크스페이스 헤더도 무시됩니다.
초대가 이메일로 발송되지 않고 링크로 돌아온 이유는 무엇인가요?
설치 환경에 메일 공급자가 없거나 발송이 실패했기 때문입니다. 어느 경우든 초대는 만들어져 있으므로, 반환된 링크를 직접 그 사람에게 보내세요.
관련 문서
워크스페이스
인증
크레딧
워크플로
CLI
최종 업데이트