組織とワークスペース
TypeScript から Nodaro の組織とワークスペースを管理します。学校やチームを作成し、メンバーを招待し、参加コードを発行し、クレジットの使用状況レポートを読み取ります。
Nodaro Cloud で利用できます
client.organizations は、組織(学校やチーム)を、そのメンバー、招待、監査ログ、使用状況レポートとともに管理します。client.workspaces は、その中にあるワークスペース(クラスやチームプロジェクトなど)を、そのメンバーと参加コードとともに管理します。これらのメソッドは、ワークスペースの REST API を呼び出します。概念については、ワークスペースを参照してください。
組織は Nodaro Cloud の機能で、インスタンスごとに有効にします。組織がないインスタンスでは、ここにあるすべてのメソッドが NotFoundError をスローします。
所属と操作の違い
ワークスペースに所属することと、そこで操作することは、別のことです。この 2 つのリソースは、誰がどこに所属しているかを管理します。ワークスペースで操作して、一覧をそこから読み取り、新しい作業をそこに置くには、client.withWorkspace(workspaceId) でクライアントを作成するか、createClient に workspaceId を渡します。
const classroom = client.withWorkspace(workspaceId)
await classroom.workflows.run(workflowId) // runs in the class
await client.workflows.run(workflowId) // runs in your personal spaceclient.me() は、自分が属する組織とワークスペース、そして lastWorkspaceId を返します。
すべての権限は、サーバーが決めます。呼び出し元が招待、削除、名前変更を行えるかどうかはサーバーが判断し、できない場合はエラーとして返します。設定によって変わることがあるため、自分のクライアントでは推測しないでください。
メソッド
client.organizations
organizations.list()
自分が属する組織を一覧表示します(GET /v1/orgs)。
list(): Promise<{ data: OrganizationView[] }>const { data: orgs } = await client.organizations.list()organizations.get(id)
1 つの組織を読み取ります(GET /v1/orgs/:id)。
get(id: string): Promise<{ data: OrganizationView }>Prop
Type
const { data: org } = await client.organizations.get(orgId)
console.log(org.status) // pending, active, suspended or deletedorganizations.create(input)
組織を作成し、自分がそのオーナーになります(POST /v1/orgs)。新しい組織は、通常は pending から始まり、Nodaro が承認すると active になります。pending の間は、自分だけがそれを見ることができ、中身は何も変更できないため、承認を待っていることをユーザーに伝えてください。
create(input: { name: string; kind: "school" | "team"; slug?: string; acceptTerms?: boolean; settings?: OrgSettings }): Promise<{ data: OrganizationView }>Prop
Type
const { data: school } = await client.organizations.create({
name: "Sunrise School",
kind: "school",
acceptTerms: true,
})acceptTerms: true のない学校は、400 terms_required で失敗します。
organizations.update(id, input)
組織の名前を変更するか、設定を変更します(PATCH /v1/orgs/:id)。
update(id: string, input: { name?: string; settings?: OrgSettings }): Promise<{ data: OrganizationView }>Prop
Type
await client.organizations.update(orgId, {
settings: { default_workflow_visibility: "workspace", allowed_email_domains: ["school.example"] },
})設定は階層化されています。ワークスペースの設定は組織の設定より優先され、組織の設定は、その種類のデフォルトより優先されます。キーを 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 | 真偽値 | 編集者権限の共同編集者が、さらに共同編集者を招待できるかどうかです。 |
allowed_email_domains | 文字列のリスト | 組織のみ:参加を許可するメールドメインです。 |
vocabulary_overrides | オブジェクト | 組織のみ:{ "workspace": "Cohort" } のような、その種類の語句の新しいラベルです。 |
組織のみの 2 つのキーは、更新すると全体が置き換わります。
organizations.delete(id)
組織を削除します(DELETE /v1/orgs/:id)。元のメンバーからは見えなくなりますが、何も破棄されません。先にすべてのワークスペースをアーカイブしてください。そうしないと、呼び出しは 409 has_active_workspaces で失敗します。
delete(id: string): Promise<{ data: { id: string; status: string } }>Prop
Type
await client.organizations.delete(orgId)organizations.transferOwnership(id, userId)
別のメンバーをオーナーにします(POST /v1/orgs/:id/transfer-ownership)。自分は管理者になります。
transferOwnership(id: string, userId: string): Promise<{ data: { orgId: string; ownerUserId: string } }>Prop
Type
await client.organizations.transferOwnership(orgId, newOwnerId)organizations.leave(id)
組織を脱退します(POST /v1/orgs/:id/leave)。オーナーは脱退できません。先にオーナー権限を譲渡してください。そうしないと、呼び出しは 409 owner_cannot_leave で失敗します。
leave(id: string): Promise<{ data: { orgId: string; left: boolean } }>Prop
Type
await client.organizations.leave(orgId)組織のメンバー
組織のメンバーを一覧表示、変更、削除します(/v1/orgs/:id/members)。メンバーは active または suspended です。停止されたメンバーは席を保持しますが、操作はできません。オーナーは、停止、削除、降格のいずれもされません。
listMembers(orgId: string, opts?: { cursor?: string; limit?: number }): Promise<OrgPage<OrgMemberView>>
updateMember(orgId: string, userId: string, input: { role?: "admin" | "member"; status?: "active" | "suspended" }): Promise<{ data: OrgMemberView }>
removeMember(orgId: string, userId: string): Promise<{ data: { removed: boolean } }>Prop
Type
const { data: members, nextCursor } = await client.organizations.listMembers(orgId)
await client.organizations.updateMember(orgId, userId, { role: "admin" })organizations.invite(orgId, input)
メールでメンバーを招待します(POST /v1/orgs/:id/invitations)。アドレスごとに 1 行を返します。
invite(orgId: string, input: {
emails: string[]
orgRole?: "admin" | "member"
workspaceId?: string
workspaceRole?: "admin" | "member"
}): Promise<{ data: InvitationDelivery[] }>Prop
Type
const { data: rows } = await client.organizations.invite(orgId, {
emails: ["teacher@school.example"],
workspaceId,
workspaceRole: "admin",
})
for (const row of rows) {
if (row.status !== "sent") showInviteLink(row.email, row.link) // not emailed: share the link yourself
}リンクを表示してください。status が sent ではない行には、代わりに link が付きます。インストール環境にメールプロバイダーがないか、配信が失敗したためです。どちらの場合も招待自体は存在しますが、リンクがなければ誰もそこに到達できません。招待は 14 日で期限切れになります。組織の 1 日あたりの招待の上限に達すると、RateLimitedError が返されます。
招待の管理
招待を一覧表示、取り消し、再送信します。
listInvitations(orgId: string, opts?: { status?: "open" | "accepted" | "revoked" | "expired"; workspaceId?: string; cursor?: string; limit?: number }): Promise<OrgPage<InvitationView>>
revokeInvitation(id: string): Promise<{ data: { id: string; revoked: boolean } }>
resendInvitation(id: string): Promise<{ data: InvitationDelivery & { id: string } }>Prop
Type
const { data: open } = await client.organizations.listInvitations(orgId, { status: "open" })
await client.organizations.resendInvitation(open[0].id)招待のプレビューと承認
previewInvitation() は公開されています。招待された人がまだログインしていない状態でも動作し、アドレスをマスクして返します。acceptInvitation() には、招待のものと一致するメールアドレスを持つ、ログイン済みの呼び出し元が必要です。
previewInvitation(token: string): Promise<{ data: InvitationPreview }>
acceptInvitation(token: string): Promise<{ data: { orgId: string; workspaceId: string | null } }>Prop
Type
const { data: preview } = await client.organizations.previewInvitation(token)
const { data: joined } = await signedInClient.organizations.acceptInvitation(token)承認は、400 invitation_expired、invitation_revoked、invitation_accepted、email_mismatch で失敗するほか、組織が特定のメールドメインだけを受け入れる場合は ForbiddenError で失敗します。
organizations.audit(orgId, opts?)
組織の監査ログを、新しい順に読み取ります(GET /v1/orgs/:id/audit)。組織が停止されている間も、読み取り可能なままです。
audit(orgId: string, opts?: { cursor?: string; limit?: number }): Promise<OrgPage<OrgAuditEntry>>Prop
Type
const { data: entries } = await client.organizations.audit(orgId, { limit: 50 })
for (const e of entries) console.log(e.createdAt, e.action, e.actor?.displayName ?? "system")各エントリーには、id、workspaceId、action、targetType、targetId、details、createdAt、actor があり、actor はシステムが行った操作では null です。action はオープンなリストです。知っている操作は表示し、それ以外は生の文字列を表示してください。固定リストだけを扱うコードは、最初の新しい操作で壊れます。
組織の使用状況
オーナーと組織の管理者向けの、日付範囲を指定したクレジットの使用状況レポートです(GET /v1/orgs/:id/usage)。usage() はレポートをグループ化し、usageRows() はその背後にある実行を新しい順にページ単位で返し、usageCsv() はレポートまたは行を CSV テキストとして返します。
usage(orgId: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: "workspace" | "member" | "model" | "day"; workspaceId?: string; userId?: string }): Promise<{ data: UsageReport }>
usageRows(orgId: string, opts?: { from?: string; to?: string; tz?: string; workspaceId?: string; userId?: string; cursor?: string; limit?: number }): Promise<OrgPage<UsageLogEntry>>
usageCsv(orgId: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: string; workspaceId?: string; userId?: string }): Promise<string>Prop
Type
const { data: report } = await client.organizations.usage(orgId, {
from: "2026-09-01",
to: "2026-09-30",
tz: "Europe/Rome",
groupBy: "workspace",
})
const csv = await client.organizations.usageCsv(orgId, { from: "2026-09-01", to: "2026-09-30" })各行は、3 つのクレジットの数値を報告します。credits は、それまでに実行にかかった金額です。終わった実行では確定した金額、進行中の実行では保持されている確保分です。settledCredits と inFlightCredits は、その内訳です。合計は、グループ化が truncated の場合でも、範囲全体をカバーします。
platformAbsorbedCreditsは、ワークスペースの残りの予算を超えた従量制の実行の超過分で、プラットフォームが負担します。chargedToBudgetはsettledCreditsからplatformAbsorbedCreditsを引いたもので、予算に反映された確定済みのクレジットです。appMarkupAbsorbedCreditsは、予算でまかなえなかったアプリの上乗せ分です。レポート上に対応する実行がないため、chargedToBudgetには含まれません。
CSV のエクスポートは、ユーザーごとに 1 分あたり 10 回までに制限されます。
client.workspaces
workspaces.list()
自分が属するワークスペースと、自分の lastWorkspaceId を一覧表示します(GET /v1/workspaces)。返されるのはサマリーで、client.me() が持つのと同じ一覧です。
list(): Promise<{ data: WorkspaceSummary[]; lastWorkspaceId: string | null }>const { data: workspaces, lastWorkspaceId } = await client.workspaces.list()workspaces.listForOrg(orgId, opts?)
1 つの組織のワークスペースを一覧表示します(GET /v1/orgs/:id/workspaces)。
listForOrg(orgId: string, opts?: { includeArchived?: boolean }): Promise<{ data: WorkspaceView[] }>Prop
Type
const { data: classes } = await client.workspaces.listForOrg(orgId)workspaces.get(id)
1 つのワークスペースの完全な情報を読み取ります(GET /v1/workspaces/:id)。
get(id: string): Promise<{ data: WorkspaceView }>Prop
Type
const { data: workspace } = await client.workspaces.get(workspaceId)workspaces.create(orgId, input)
組織の中にワークスペースを作成します(POST /v1/orgs/:id/workspaces)。組織が active である必要があります。
create(orgId: string, input: { name: string; slug?: string; description?: string; settings?: WorkspaceSettings }): Promise<{ data: WorkspaceView }>Prop
Type
const { data: classOne } = await client.workspaces.create(orgId, { name: "Class 1" })workspaces.update(id, input)
ワークスペースを変更します(PATCH /v1/workspaces/:id)。
update(id: string, input: { name?: string; description?: string | null; settings?: WorkspaceSettings }): Promise<{ data: WorkspaceView }>Prop
Type
await client.workspaces.update(workspaceId, { settings: { members_can_create_projects: true } })workspaces.setArchived(id, archived)
ワークスペースをアーカイブするか、アーカイブを解除します(POST /v1/workspaces/:id/archive または /unarchive)。アーカイブは元に戻せて、何も破棄されません。ワークスペースは新しい作業を受け付けなくなりますが、完全に読み取り可能なままです。アーカイブされたワークスペースへの書き込みは、ForbiddenError で失敗します。
setArchived(id: string, archived: boolean): Promise<{ data: WorkspaceView }>Prop
Type
await client.workspaces.setArchived(workspaceId, true)ワークスペースのメンバー
ワークスペースのメンバーを一覧表示、追加、変更、削除します(/v1/workspaces/:id/members)。追加するには、その人がすでに組織に属している必要があります。新しい人を迎えるには、招待してください。組織のオーナーと管理者は、一覧に載らなくても、すべてのワークスペースの管理者です。
listMembers(id: string, opts?: { cursor?: string; limit?: number }): Promise<OrgPage<WorkspaceMemberView>>
addMember(id: string, input: { userId: string; role: "admin" | "member" }): Promise<{ data: WorkspaceMemberView }>
updateMember(id: string, userId: string, input: { role?: "admin" | "member"; status?: "active" | "suspended"; creditCap?: number | null }): Promise<{ data: WorkspaceMemberView }>
removeMember(id: string, userId: string): Promise<{ data: { removed: boolean } }>Prop
Type
await client.workspaces.addMember(workspaceId, { userId, role: "member" })
await client.workspaces.updateMember(workspaceId, userId, { creditCap: 500 })組織のアクティブなメンバーではない人を追加すると、400 not_org_member で失敗します。同じ人を 2 回追加すると、409 already_a_member で失敗します。
参加コード
参加コードを使うと、招待なしでワークスペースに参加できます。読み取りと変更ができるのは、ワークスペースの管理者だけです。
getJoinCode(id: string): Promise<{ data: JoinCodeView | null }>
actOnJoinCode(id: string, action: "rotate" | "enable" | "disable"): Promise<{ data: JoinCodeView }>Prop
Type
const { data: code } = await client.workspaces.actOnJoinCode(workspaceId, "enable")
console.log(code.code) // for example BCDFGHJKコードがまだ作られていない場合、getJoinCode() は null を返します。
workspaces.join(code)
参加コードで、ワークスペースに参加します(POST /v1/workspaces/join)。自分のクライアントがどのワークスペースで操作していても動作するため、古くなった選択がこれを妨げることはありません。
join(code: string): Promise<{ data: { orgId: string; workspaceId: string } }>Prop
Type
const { data: joined } = await client.workspaces.join("BCDFGHJK")不明なコード、無効化されたコード、アーカイブされたワークスペースのコードは、いずれも 400 join_code_invalid で失敗します。3 つとも同じ応答なので、コードから何が存在するかがわかることはありません。試行が多すぎる場合は RateLimitedError が返されます。
ワークスペースの使用状況
組織と同じクレジットレポートを、1 つのワークスペースについて返します(GET /v1/workspaces/:id/usage)。メンバーには自分の実行だけが見え、管理者には全員の実行が見え、userId で絞り込めます。
usage(id: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: "member" | "model" | "day"; userId?: string }): Promise<{ data: UsageReport }>
usageRows(id: string, opts?: { from?: string; to?: string; tz?: string; userId?: string; cursor?: string; limit?: number }): Promise<OrgPage<UsageLogEntry>>
usageCsv(id: string, opts?: { from?: string; to?: string; tz?: string; groupBy?: string; userId?: string }): Promise<string>Prop
Type
const { data: byModel } = await client.workspaces.usage(workspaceId, { groupBy: "model" })エラー
すべての権限はサーバーが決め、エラーで答えます。SDK では、403 は ForbiddenError に、404 は NotFoundError に、429 は RateLimitedError になります。理由は、その message を読んでください。それ以外のステータスは、サーバーの code を持つ NodaroError として届きます。
| ステータス | コード | 発生するとき |
|---|---|---|
| 400 | validation_error | フィールドが無効、カーソルの形式が不正、または使用状況レポートの日付、タイムゾーン、範囲が不正です |
| 400 | terms_required | acceptTerms: true なしで学校が作成されました |
| 400 | not_org_member | ワークスペースに追加された人が、その組織のアクティブなメンバーではありません |
| 400 | token_workspace_mismatch | 1 つのワークスペースに紐付けたトークンが、別のワークスペースで使われました |
| 400 | join_code_invalid | 参加コードが存在しない、無効になっている、またはアーカイブされたワークスペースのものです |
| 400 | invitation_expired、invitation_revoked、invitation_accepted、email_mismatch | 招待を承認できません |
| 403 | ロールが不足している、メンバーが停止されている、組織が active ではない、ワークスペースがアーカイブされている、またはメールドメインが許可されていません | |
| 404 | その組織、ワークスペース、メンバー、招待が存在しないか、自分がそのメンバーではありません | |
| 409 | name_taken | スラッグが使用中です |
| 409 | already_a_member | その人はすでにワークスペースにいます |
| 409 | owner_cannot_leave | オーナーが脱退しようとしました |
| 409 | has_active_workspaces | 組織に、アーカイブされていないワークスペースがまだあります |
| 429 | 組織の作成、参加の試行、CSV エクスポート、招待が多すぎます | |
| 503 | billing_unavailable | このインスタンスでは、使用状況レポートがまだ利用できません |
| 503 | audit_unavailable | CSV エクスポートを監査ログに記録できませんでした。もう一度試してください。 |
よくある質問
関連ページ
ワークスペース
クライアント
ワークスペースと組織
ワークフローとプロジェクト
最終更新