Nodaro ドキュメント
ドキュメントノードリファレンスモデルAI エージェント(MCP)開発者向けセルフホスティングリサーチ
TypeScript SDK

組織とワークスペース

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 space

client.me() は、自分が属する組織とワークスペース、そして lastWorkspaceId を返します。

すべての権限は、サーバーが決めます。呼び出し元が招待、削除、名前変更を行えるかどうかはサーバーが判断し、できない場合はエラーとして返します。設定によって変わることがあるため、自分のクライアントでは推測しないでください。

メソッド

メソッド内容
organizations.list()自分が属する組織を一覧表示します
organizations.get(id)1 つの組織を読み取ります
organizations.create(input)学校またはチームを作成します
organizations.update(id, input)名前を変更するか、設定を変更します
organizations.delete(id)組織を削除します
organizations.transferOwnership(id, userId)別のメンバーをオーナーにします
organizations.leave(id)組織を脱退します
organizations.listMembers()、updateMember()、removeMember()メンバーを管理します
organizations.invite(orgId, input)メールでメンバーを招待します
organizations.listInvitations()、revokeInvitation()、resendInvitation()送信済みの招待を管理します
organizations.previewInvitation()、acceptInvitation()招待を表示し、承認します
organizations.audit(orgId, opts?)監査ログを読み取ります
organizations.usage()、usageRows()、usageCsv()クレジットの使用状況を読み取ります
workspaces.list()自分が属するワークスペースを一覧表示します
workspaces.listForOrg(orgId, opts?)組織のワークスペースを一覧表示します
workspaces.get(id)1 つのワークスペースを読み取ります
workspaces.create(orgId, input)ワークスペースを作成します
workspaces.update(id, input)ワークスペースを変更します
workspaces.setArchived(id, archived)ワークスペースをアーカイブまたはアーカイブ解除します
workspaces.listMembers()、addMember()、updateMember()、removeMember()ワークスペースのメンバーを管理します
workspaces.getJoinCode()、actOnJoinCode()参加コードを管理します
workspaces.join(code)コードでワークスペースに参加します
workspaces.usage()、usageRows()、usageCsv()ワークスペースのクレジットの使用状況を読み取ります

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 deleted

organizations.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_accessview または edit管理者がメンバーのワークフローに対して行える操作です。
default_workflow_visibilityprivate または workspace新しいワークフローの公開範囲です。
member_access_to_sharedview または 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 として届きます。

ステータスコード発生するとき
400validation_errorフィールドが無効、カーソルの形式が不正、または使用状況レポートの日付、タイムゾーン、範囲が不正です
400terms_requiredacceptTerms: true なしで学校が作成されました
400not_org_memberワークスペースに追加された人が、その組織のアクティブなメンバーではありません
400token_workspace_mismatch1 つのワークスペースに紐付けたトークンが、別のワークスペースで使われました
400join_code_invalid参加コードが存在しない、無効になっている、またはアーカイブされたワークスペースのものです
400invitation_expired、invitation_revoked、invitation_accepted、email_mismatch招待を承認できません
403ロールが不足している、メンバーが停止されている、組織が active ではない、ワークスペースがアーカイブされている、またはメールドメインが許可されていません
404その組織、ワークスペース、メンバー、招待が存在しないか、自分がそのメンバーではありません
409name_takenスラッグが使用中です
409already_a_memberその人はすでにワークスペースにいます
409owner_cannot_leaveオーナーが脱退しようとしました
409has_active_workspaces組織に、アーカイブされていないワークスペースがまだあります
429組織の作成、参加の試行、CSV エクスポート、招待が多すぎます
503billing_unavailableこのインスタンスでは、使用状況レポートがまだ利用できません
503audit_unavailableCSV エクスポートを監査ログに記録できませんでした。もう一度試してください。

よくある質問

最終更新

目次