runlot
참조API

조직

orgs 카테고리에서 API 작업 14개를 다룹니다.

메서드경로설명
GET/v1/orgs
POST/v1/orgsorg 생성
PATCH/v1/orgs/{orgSlug}org의 표시 이름 변경 (runlot org rename)
DELETE/v1/orgs/{orgSlug}org 삭제 (runlot org delete)
GET/v1/orgs/{orgSlug}/notifications최근 알림(docs/limits.md §3.4)
GET/v1/orgs/{orgSlug}/membersOrg 멤버(runlot org members)
POST/v1/orgs/{orgSlug}/members기존 사용자를 멤버로 추가(운영 및 자동화용)
GET/v1/orgs/{orgSlug}/invites대기 중인 초대(runlot member invites)
POST/v1/orgs/{orgSlug}/invites이메일로 초대(runlot member invite)
DELETE/v1/orgs/{orgSlug}/invites/{inviteId}초대 취소(runlot member uninvite)
GET/v1/invites/{token}초대 미리보기(인증 없음)
POST/v1/invites/{token}/accept초대 수락
PUT/v1/orgs/{orgSlug}/members/{userId}역할 변경(runlot org set-role)
DELETE/v1/orgs/{orgSlug}/members/{userId}멤버 제거 또는 자진 탈퇴(runlot member rm, runlot member leave)

GET /v1/orgs

operationId listOrgs

상태 코드설명응답 본문
200소유 조직object
401

POST /v1/orgs

생성자는 admin이 됩니다. 개인 계정도 1인 조직으로 모델링되므로(docs/mvp-scope.md), 여기에 별도 경로는 없습니다.

operationId createOrg

요청 본문: application/json · object

상태 코드설명응답 본문
201생성된 조직Org
401
409

PATCH /v1/orgs/{orgSlug}

admin 이상의 권한이 필요합니다. org.rename으로 감사 기록됩니다.

slug는 이 표면에 포함되지 않습니다. slug는 주소가 되는 이름입니다(<project>.<slug>.runlot.app, 인증서 SAN, 클론 주소, 고객 저장소에 커밋되는 runlot.json"org" 필드). 따라서 이를 변경하려면 이전 이름을 얼마간 유지해야 합니다 — 그리고 그 별칭이 바로 0027이 도입하고 0037이 제거한 것입니다(docs/org-settings.md §2).

operationId updateOrg

요청 본문: application/json · object

상태 코드설명응답 본문
200업데이트된 orgOrgMembership
400
403
404

DELETE /v1/orgs/{orgSlug}

admin 이상의 권한이 필요합니다. 이 작업은 되돌릴 수 없습니다.

confirm 필드에는 정확한 org slug를 입력해야 합니다(앞뒤 공백은 제거됩니다). 이 API는 이 검사를 강제하는데, 대시보드·CLI·스크립트라는 세 호출자가 이 표면을 사용하기 때문입니다 — 각자 따로 구현하면 누군가 빠뜨려도 아무도 알아채지 못합니다.

삭제는 org가 비어 있을 때만 성공합니다. 프로젝트, 도메인, 저장소, 또는 DNS 존이 남아 있으면 응답은 409 org_not_empty이며, 봉투(envelope)의 details.holdings에 종류별 개수와 몇 개의 이름이 나열됩니다. 그 이유는 스키마 수준입니다(0044, 0020, 0032의 RESTRICT 제약 조건): 행을 삭제해도 노드의 데이터는 그대로 남으며, 아무것도 가리키지 않는 데이터는 삭제할 수도 청구할 수도 없습니다.

그 외 두 가지 거부 사유가 있습니다: 개인 org는 personal_org를 반환하며(첫 로그인 시 생성된 org로, 다시 생성되지 않습니다), 유료 구독이 활성 상태이면 active_subscription을 반환합니다(카드에 요금이 계속 청구되는 동안 org가 사라지는 것을 막기 위함입니다).

org.delete 감사 이벤트는 org보다 오래 남습니다audit_events.org_id는 ON DELETE SET NULL을 사용하며, slug는 target에 남습니다.

operationId deleteOrg

요청 본문: application/json · object

상태 코드설명응답 본문
204삭제됨
400
403
404
409비어 있지 않음 (org_not_empty) · 개인 org (personal_org) · 활성 구독 (active_subscription)Error

GET /v1/orgs/{orgSlug}/notifications

한도, 백업, 복원 이벤트 목록입니다(notifications). 최신순입니다.

sentAt이 설정되어 있으면 이메일도 발송된 것입니다. 설정되지 않은 행은 아직 대기 중이거나 받을 이메일이 없는 경우입니다 — 후자의 경우 이 목록이 유일한 채널입니다: GitHub 로그인이 비공개 이메일을 사용하면 users.email은 NULL입니다(§3.3).

뷰어 이상.

operationId listNotifications

매개변수위치필수타입설명
limitquery아니오정수
상태 코드설명응답 본문
200알림 목록 조회object
403
404

GET /v1/orgs/{orgSlug}/members

member 이상의 권한이 필요합니다.

operationId listMembers

상태 코드설명응답 본문
200멤버 목록object
403
404

POST /v1/orgs/{orgSlug}/members

이것은 초대가 아닙니다. 사용자는 이미 한 번 로그인해서 users에 존재해야 합니다(provider+subject로 식별). 그렇지 않으면 404 user_not_found를 반환합니다. 이미 멤버라면 409 already_member를 반환합니다 — 역할 변경은 PUT입니다.

사람을 초대하는 창구는 이 엔드포인트가 아니라 POST …/invites입니다(docs/members.md): GitHub의 subject는 숫자 id로, 관리자가 알 수 없는 값이므로 이 엔드포인트는 이미 그 값을 아는 자동화를 위한 것입니다.

admin 이상 권한이 필요합니다. member.add로 감사 기록됩니다.

operationId addMember

요청 본문: application/json · object

상태 코드설명응답 본문
201멤버 추가됨Member
400
403
404조직이 존재하지 않거나 사용자가 존재하지 않습니다(user_not_found)Error
409이미 멤버입니다(already_member)Error

GET /v1/orgs/{orgSlug}/invites

Admin 이상. Member 이상은 멤버 목록을 볼 수 있지만, 초대 목록은 다릅니다 — 이는 아직 조직에 속하지 않은 사람들의 이메일이며, 명단이 아니라 관리자 작업의 상태입니다.

수락되었거나 취소된 초대는 나타나지 않습니다(이력은 감사 로그에 남습니다). 만료된 초대는 나타납니다 — 재전송은 거기서 관리자의 몫입니다.

operationId listInvites

상태 코드설명응답 본문
200초대 목록object
403
404

POST /v1/orgs/{orgSlug}/invites

Admin 이상. 이메일은 즉시 발송되지 않습니다 — cp-core 발송 주기가 큐를 훑습니다(sentAt이 비어 있으면 아직 대기 중이라는 뜻입니다).

같은 주소로 다시 호출하면 재전송됩니다: 새 초대 대신 같은 행이 업데이트되고(새 토큰과 만료 시각이 설정되며 역할도 바뀔 수 있습니다), 이때는 200을 반환합니다. 새 초대는 201을 반환합니다.

이미 멤버인 주소는 409 already_member를 반환합니다. 대기 중인 초대가 너무 많으면 409 too_many_invites를 반환합니다 — 이는 요금제 제한이 아니라 발송에 대한 남용 방지입니다.

초대는 7일간 유효하며 1회용이고 언제든 취소할 수 있습니다. member.invite로 감사 기록됩니다.

operationId createInvite

요청 본문: application/json · object

상태 코드설명응답 본문
200기존 초대 재전송Invite
201새 초대Invite
400
403
404
409이미 멤버임(already_member) 또는 대기 중인 초대가 너무 많음(too_many_invites)Error

DELETE /v1/orgs/{orgSlug}/invites/{inviteId}

Admin 이상. 아직 발송되지 않은 이메일은 발송되지 않습니다 — 취소하면 발송 큐도 함께 비워집니다. 이미 수락되었거나 취소된 초대는 404를 반환합니다. member.uninvite로 감사 기록됩니다.

operationId revokeInvite

상태 코드설명응답 본문
204취소됨
403
404

GET /v1/invites/{token}

인증 없음. 초대 링크를 받는 사람은 아직 로그인하지 않았고, 로그인하기 전에 그것이 무엇인지 알아야 합니다. 토큰은 32바이트 무작위 값이라 열거할 수 없으며, 이를 여는 대가는 "이메일을 받은 사람이 조직 이름을 보게 되는 것"에서 끝납니다.

상태는 오류가 아니라 값입니다(state). 만료된 초대에 404를 반환하면 UI가 "링크가 잘못되었다"와 "링크가 오래되었다"를 구분할 수 없게 됩니다.

알 수 없는 토큰은 404를 반환합니다 — 오래전에 취소된 토큰과 같은 응답입니다.

operationId getInvite

상태 코드설명응답 본문
200초대InviteView
404

POST /v1/invites/{token}/accept

로그인이 필요합니다. 로그인한 계정의 이메일이 초대받은 주소와 일치해야 한다는 규칙은 없습니다 — GitHub 비공개 이메일로 로그인한 사람은 users.email이 아예 없으며, 그런 규칙을 추가하면 모든 org에서 접근이 막히게 됩니다. 대신 초대는 7일간 유효하고, 1회용이며, 언제든 취소할 수 있고, 감사 기록에는 초대받은 주소와 수락한 계정이 모두 남습니다.

이미 멤버인 사람이라면 역할은 변경되지 않습니다 (alreadyMember: true). 초대가 admin을 강등시키는 경로가 된다면, 그 강등은 마지막 admin 검사를 통과하지 못할 것입니다.

member.accept로 감사 기록됩니다.

operationId acceptInvite

상태 코드설명응답 본문
200수락됨object
401
404
409이미 수락됨(invite_used), 취소됨(invite_revoked), 또는 만료됨(invite_expired)Error

PUT /v1/orgs/{orgSlug}/members/{userId}

admin 이상 권한이 필요합니다. 마지막 admin은 강등할 수 없습니다 (409 last_admin) — 본인도 포함됩니다. 이 규칙이 없으면 조직을 관리할 수 있는 사람이 아무도 없게 될 수 있고, 유일한 해결 방법은 DB를 직접 수정하는 것뿐입니다. member.role로 감사 기록됩니다.

operationId setMemberRole

요청 본문: application/json · object

상태 코드설명응답 본문
200변경된 멤버Member
400
403
404
409마지막 admin입니다(last_admin)Error

DELETE /v1/orgs/{orgSlug}/members/{userId}

다른 사람을 제거하려면 admin 이상 권한이 필요하지만, 자기 자신을 제거하는 것은 누구나 할 수 있습니다 (userId가 자신의 것이면 탈퇴가 됩니다). admin만 멤버를 제거할 수 있다면, viewer로 초대된 사람은 org를 떠날 방법이 없게 됩니다.

마지막 admin은 제거되거나 탈퇴할 수 없습니다 (409 last_admin). 감사 로그는 제거를 member.remove로, 탈퇴를 member.leave로 기록합니다 — 같은 문장이지만 이벤트가 다르므로 이름도 다릅니다.

operationId removeMember

상태 코드설명응답 본문
204제거됨
403
404
409마지막 admin입니다(last_admin)Error

이 페이지의 목차