Organizations
orgs 범주의 API 작업 12개를 안내합니다.
| Method | Path | Description |
|---|---|---|
| GET | /v1/orgs | — |
| POST | /v1/orgs | Create an organization |
| GET | /v1/orgs/{orgSlug}/notifications | Recent notifications (docs/limits.md §3.4) |
| GET | /v1/orgs/{orgSlug}/members | Organization members (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} | Change a role (runlot org set-role) |
| DELETE | /v1/orgs/{orgSlug}/members/{userId} | 멤버를 뺀다 · 스스로 나간다 (runlot member rm, runlot member leave) |
GET /v1/orgs
operationId listOrgs
| Status | Description | Response body |
|---|---|---|
| 200 | Organizations you belong to | object |
| 401 | — | — |
POST /v1/orgs
The creator becomes admin. Personal accounts are modelled as one-person organizations too (docs/mvp-scope.md), so there is no separate path here.
operationId createOrg
Request body: application/json · object
| Status | Description | Response body |
|---|---|---|
| 201 | The created organization | Org |
| 401 | — | — |
| 409 | — | — |
GET /v1/orgs/{orgSlug}/notifications
The list of limit, backup and restore events (notifications). Newest first.
If sentAt is present, an email went out as well. A row without it is either still queued or has no address to send to, and for the latter this list is the only channel — if a GitHub login uses a private email, users.email is NULL (§3.3).
Requires viewer or above.
operationId listNotifications
| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
limit | query | No | integer | — |
| Status | Description | Response body |
|---|---|---|
| 200 | Notification list | object |
| 403 | — | — |
| 404 | — | — |
GET /v1/orgs/{orgSlug}/members
Requires member or above.
operationId listMembers
| Status | Description | Response body |
|---|---|---|
| 200 | Member list | 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 라 관리자가 알 수 없는 값이고, 이 끝점은 그것을 이미 아는 자동화의 것이다.
Requires admin or above. Audit member.add.
operationId addMember
Request body: application/json · object
| Status | Description | Response body |
|---|---|---|
| 201 | The added member | Member |
| 400 | — | — |
| 403 | — | — |
| 404 | The organization or the user does not exist (user_not_found) | Error |
| 409 | Already a member (already_member) | Error |
GET /v1/orgs/{orgSlug}/invites
admin 이상. 멤버 목록은 member 이상이 보지만 초대 목록은 다르다 — 아직 조직에 없는 사람들의 이메일이고, 그것은 명부가 아니라 관리 작업의 상태다.
수락·폐기된 초대는 안 나온다 (이력의 자리는 감사 기록이다). 만료된 초대는 나온다 — 거기서 관리자가 할 일이 재발송이다.
operationId listInvites
| Status | Description | Response body |
|---|---|---|
| 200 | 초대 목록 | object |
| 403 | — | — |
| 404 | — | — |
POST /v1/orgs/{orgSlug}/invites
admin 이상. 메일은 즉시 나가지 않는다 — cp-core 의 발송 회차가 큐를 훑는다 (sentAt 이 비어 있으면 아직 큐에 있다는 뜻이다).
같은 주소로 다시 부르면 재발송이다: 새 초대가 아니라 같은 행이 갱신되고 (토큰·만료가 새로 서고 역할이 바뀐다) 200 으로 답한다. 새 초대는 201 이다.
이미 멤버인 주소는 409 already_member. 대기 중 초대가 너무 많으면 409 too_many_invites — 요금제 한도가 아니라 발신 남용 방지다.
수명은 이레, 한 번 쓰면 끝, 언제든 폐기할 수 있다. 감사 member.invite.
operationId createInvite
Request body: application/json · object
| Status | Description | Response body |
|---|---|---|
| 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
| Status | Description | Response body |
|---|---|---|
| 204 | 취소했다 | — |
| 403 | — | — |
| 404 | — | — |
GET /v1/invites/{token}
인증이 없다. 초대 링크를 받은 사람은 아직 로그인하지 않았고, 로그인 하기 전에 이것이 무엇인지 알아야 한다. 토큰은 32 바이트 난수라 열거할 수 없고, 그래서 여는 대가가 "우리가 메일을 보낸 사람이 조직 이름을 본다" 로 끝난다.
상태는 오류가 아니라 값이다 (state). 만료된 초대에 404 를 주면 화면이 "링크가 틀렸다" 와 "링크가 늙었다" 를 구분하지 못한다.
모르는 토큰은 404 — 오래전에 폐기된 토큰과 같은 답이다.
operationId getInvite
| Status | Description | Response body |
|---|---|---|
| 200 | 초대 | InviteView |
| 404 | — | — |
POST /v1/invites/{token}/accept
로그인이 필요하다. 로그인한 계정의 이메일이 초대한 주소와 같아야 한다는 규칙은 없다 — GitHub 비공개 이메일로 로그인한 사람은 users.email 이 아예 없고, 그 규칙을 넣으면 그 사람은 어떤 조직에도 들어올 수 없다. 대신 수명 이레 · 한 번 쓰면 끝 · 언제든 폐기이고, 감사 기록에 초대한 주소와 수락한 계정이 둘 다 남는다.
이미 멤버면 역할이 안 바뀐다 (alreadyMember: true). 초대장 하나가 admin 을 강등하는 표면이 되면 그 강등은 마지막-admin 검사를 지나지 않는다.
감사 member.accept.
operationId acceptInvite
| Status | Description | Response body |
|---|---|---|
| 200 | 수락했다 | object |
| 401 | — | — |
| 404 | — | — |
| 409 | 이미 수락됨 (invite_used) · 취소됨 (invite_revoked) · 만료됨 (invite_expired) | Error |
PUT /v1/orgs/{orgSlug}/members/{userId}
Requires admin or above. The last admin cannot be demoted (409 last_admin) — including yourself. Without that rule an organization can end up with nobody able to administer it, and the only way out is editing the database by hand. Audit member.role.
operationId setMemberRole
Request body: application/json · object
| Status | Description | Response body |
|---|---|---|
| 200 | The updated member | Member |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
| 409 | This is the last admin (last_admin) | Error |
DELETE /v1/orgs/{orgSlug}/members/{userId}
남을 빼는 것은 admin 이상, 자기를 빼는 것은 누구나 할 수 있다 (userId 가 자기 자신이면 탈퇴다). admin 만 뺄 수 있게 두면 viewer 로 초대된 사람이 조직을 나갈 길이 아예 없다.
마지막 admin 은 뺄 수도 나갈 수도 없다 (409 last_admin). 감사는 제거가 member.remove, 탈퇴가 member.leave — 같은 문장이지만 다른 사건이라 이름이 갈린다.
operationId removeMember
| Status | Description | Response body |
|---|---|---|
| 204 | Removed | — |
| 403 | — | — |
| 404 | — | — |
| 409 | This is the last admin (last_admin) | Error |