組織
org カテゴリの API オペレーションを 14 個カバーします。
| メソッド | パス | 説明 |
|---|---|---|
| GET | /v1/orgs | — |
| POST | /v1/orgs | org を作成する |
| 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}/members | org メンバー(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 になります。個人アカウントも一人組織としてモデル化されているため(docs/mvp-scope.md)、ここに別のパスはありません。
operationId createOrg
リクエストボディ: application/json ・ object
| ステータスコード | 説明 | レスポンスボディ |
|---|---|---|
| 201 | 作成された組織 | Org |
| 401 | — | — |
| 409 | — | — |
PATCH /v1/orgs/{orgSlug}
admin 以上が必要です。org.rename として監査されます。
slug はこの API サーフェスの対象外です。 slug はアドレスになる名前です(<project>.<slug>.runlot.app、証明書の SAN、クローンアドレス、顧客のリポジトリにコミットされる runlot.json 内の "org" フィールド)。そのため変更する際は、しばらくの間古い名前も生かしておく必要があります — そしてそのエイリアスこそ、0027 が導入し 0037 が削除したものです(docs/org-settings.md §2)。
operationId updateOrg
リクエストボディ: application/json ・ object
| ステータスコード | 説明 | レスポンスボディ |
|---|---|---|
| 200 | 更新後の org | OrgMembership |
| 400 | — | — |
| 403 | — | — |
| 404 | — | — |
DELETE /v1/orgs/{orgSlug}
admin 以上が必要です。これは取り消せません。
confirm フィールドには org の slug を正確に指定する必要があります(前後の空白は取り除かれます)。この API サーフェスにはダッシュボード・CLI・スクリプトという 3 つの呼び出し元があり、それぞれが個別に実装すると、どれか一つが対応漏れになっても気づけないため、API 側でこのチェックを強制しています。
削除は org が空の場合にのみ成功します。 project・ドメイン・リポジトリ・DNS ゾーンが残っている場合、レスポンスは 409 org_not_empty となり、エンベロープの details.holdings に種類ごとの件数と一部の名前が列挙されます。この理由はスキーマレベルのものです(0044・0020・0032 の RESTRICT 制約)— 行を削除してもノードのデータはそのまま残り、何からも参照されなくなったデータは削除も課金もできなくなるためです。
他に 2 つの拒否理由があります。個人 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)。
viewer以上です。
operationId listNotifications
| パラメーター | 場所 | 必須 | 型 | 説明 |
|---|---|---|---|---|
limit | query | 不可 | integer | — |
| ステータスコード | 説明 | レスポンスボディ |
|---|---|---|
| 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 以上はメンバー一覧を閲覧できますが、招待一覧は別です — これはまだ org に属していない人のメールアドレスであり、名簿ではなく管理タスクの状態です。
承諾済みまたは取り消し済みの招待は表示されません(履歴は監査ログにあります)。期限切れの招待は表示されます — その場合の再送は管理者の仕事です。
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 日間で、一度限り使用可能で、いつでも取り消せます。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 バイトのランダムな値なので列挙できず、これを開くことの影響は「メールを送った相手が org 名を見る」で終わります。
状態はエラーではなく値です(state)。期限切れの招待に対して 404 を返すと、UI は「リンクが間違っている」のか「リンクが古い」のかを区別できなくなります。
不明なトークンは 404 を返します — ずっと前に取り消されたトークンと同じ応答です。
operationId getInvite
| ステータスコード | 説明 | レスポンスボディ |
|---|---|---|
| 200 | 招待 | InviteView |
| 404 | — | — |
POST /v1/invites/{token}/accept
ログインが必要です。ログイン中のアカウントのメールアドレスが招待先アドレスと一致しなければならないというルールはありません — GitHub のプライベートメールでサインインした人には users.email 自体が存在しない場合があり、そのルールを追加するとすべての org から締め出されてしまいます。代わりに、招待は 7 日間有効で、使い切りであり、いつでも取り消すことができ、監査記録には招待先アドレスと承諾したアカウントの両方が残ります。
すでにメンバーである場合、ロールは変更されません(alreadyMember: true)。もし招待が管理者を降格させる経路になってしまうと、その降格は last-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}
他人を削除するには管理者以上の権限が必要ですが、自分自身を削除することは誰にでもできます(userId が自分自身の場合は脱退になります)。もし管理者だけがメンバーを削除できるとしたら、閲覧者として招待された人には org を抜ける方法がなくなってしまいます。
最後の管理者は削除することも脱退することもできません(409 last_admin)。監査ログでは削除は member.remove、脱退は member.leave として記録されます — 同じ操作でもイベントが異なるため、名前も異なります。
operationId removeMember
| ステータスコード | 説明 | レスポンスボディ |
|---|---|---|
| 204 | 削除済み | — |
| 403 | — | — |
| 404 | — | — |
| 409 | これは最後の admin です(last_admin) | Error |