# ワークスペースと組織

> X-Nodaro-Workspace を使って、Nodaro API から組織のワークスペースで操作します。トークンをワークスペースに紐付け、メンバー、招待、共有、使用状況を管理します。

Source: https://nodaro.ai/ja/docs/developers/api/workspaces

**ワークスペース**は、組織のメンバーが一緒に作業する場所です。学校ならクラス、会社ならチームにあたります。API のリクエストは、`X-Nodaro-Workspace` ヘッダーにその ID を送信すると、1 つのワークスペースで操作します。組織のエンドポイントは、それを取り巻くすべてを管理します。組織、メンバー、招待、参加コード、共有、予算、使用状況です。

組織は Nodaro Cloud の機能で、インスタンスごとに有効にします。有効になっていない環境では、以下のエンドポイントは存在せず、ヘッダーも無視されます。セルフホスティングのビルドには、組織は含まれません。概念については、[ワークスペース](https://nodaro.ai/docs/concepts/workspaces)を参照してください。

## ワークスペースで操作する
```http
X-Nodaro-Workspace: 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34
```

このヘッダーが行うのは、次の 2 つだけです。**一覧がどのワークスペースを返すか**と、**作成したものがどこに置かれるか**です。アクセス権を与えることはありません。ID を指定して何かを読み取る、更新する、削除する、実行するといった操作は、そのオブジェクト自身のワークスペースに従います。ヘッダーを送り忘れても自分の作業が見えなくなることはなく、偽装したヘッダーでほかの人の作業にアクセスすることもできません。

ヘッダーがない場合は、組織を持たないアカウントが常にそうであるように、自分の個人スペースで作業します。

**curl**

```bash
curl -s https://app.nodaro.ai/v1/workflows \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "X-Nodaro-Workspace: 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34"
```

**TypeScript SDK**

```ts
// Every request of this client acts in the workspace:
const classClient = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
workspaceId: '6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34',
})

// Or derive a second client from an existing one:
const classroom = client.withWorkspace(workspaceId)
await classroom.workflows.run(workflowId) // lands in the class
await client.workflows.run(workflowId)    // lands in the personal space
```

**CLI**

```bash
nodaro --workspace 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34 workflows list   # this command only
export NODARO_WORKSPACE=6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34            # this shell or CI job
nodaro workspace use 6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34                # saved on the profile
nodaro workspace current                                                # which one applies, and why
```

`client.withWorkspace` は、既存のクライアントを変更するのではなく、**新しい**クライアントを返します。そのため、同時に実行される 2 つの操作が、どちらのワークスペースにいるかで競合することはありません。個人スペースには `null` を渡します。CLI では、3 つの方法のうち、上にあるものほど優先されます。`nodaro workspace use` は、保存する前にワークスペースを確認します。

ヘッダーは、自分が属しているワークスペースに対してのみ送信してください。

| ステータス | コード | 発生するとき |
| --- | --- | --- |
| 400 | `validation_error` | 値が UUID ではありません。 |
| 403 | `not_a_member` | そのワークスペースのメンバーではないか、そのワークスペースが存在しないか、その組織が有効ではありません。 |
| 403 | `member_suspended` | メンバーシップが停止されています。 |

2 つの場所では、古くなった選択が拒否されることはなく、締め出されることもありません。`GET /v1/me` と `GET /v1/workspaces`、そして招待を承認する操作です。そこでは、もう選択できなくなったワークスペースは、ヘッダーを送らなかった場合と同じように扱われます。これらは、どのワークスペースを選択できるかを教えてくれるエンドポイントなので、そこに一覧されなくなったら、キャッシュした選択をクリアしてください。

## トークンをワークスペースに紐付ける
API トークンは、1 つのワークスペースに紐付けられます。紐付けた後は、すべてのリクエストでそのヘッダーを送ったかのように動作し、別のワークスペースを明示的に指定したヘッダーには `400 token_workspace_mismatch` が返されます。

```bash
# Bind
curl -X PATCH https://app.nodaro.ai/v1/api-tokens/$TOKEN_ID \
  -H "Authorization: Bearer $SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{"workspaceId": "6f1e6b4c-6a4e-4b7b-9d2a-2f0f0a1d9c34"}'

# Unbind
curl -X PATCH https://app.nodaro.ai/v1/api-tokens/$TOKEN_ID \
  -H "Authorization: Bearer $SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{"workspaceId": null}'
```

トークンの管理には、ログイン中のセッションの JWT が必要です。API トークンや OAuth トークンでは `403 forbidden` が返されます。トークンを紐付けられるのは、ヘッダーで選択できるワークスペースだけですが、紐付けの解除はいつでもできます。トークンの一覧表示では、紐付けが `workspaceId` として返されます。

## 作成の保存先
- **ワークスペースの中では、作成したものはそのワークスペースに置かれ**、個人スペースに置かれることはありません。プロジェクトを指定しない作成は、ワークスペースのプロジェクトに置かれます。ワークスペースにまだプロジェクトがない場合、作成は `409 workspace_has_no_default_project` を返します。プロジェクトを指定すれば成功します。
- **指定するプロジェクトは、自分が操作しているワークスペースに属している必要があります。**別のワークスペースや自分の個人スペースのプロジェクトを指定すると、`404 Project not found` が返されます。これは、存在しないプロジェクトを指定した場合と同じ応答なので、ヘッダーを使って何が存在するかを調べることはできません。
- **プロジェクトの作成は、管理者だけに制限されることがあります。**その場合、メンバーには `403 project_create_not_allowed` が返されます。
- **組織は、個人スペースを閉じることができます。**その場合、メンバーはワークスペースの中でのみ作成でき、ヘッダーなしでの作成には `403 personal_space_disabled` が返されます。組織を持たないアカウントは、影響を受けません。

## アーカイブしたワークスペースは読み取り専用
ワークスペースをアーカイブすると、中身はすべて読み取り可能なまま残り、新しい作業の追加だけが止まります。一覧表示は、これまでどおり動作します。プロジェクト、ワークフロー、インポート、サブワークフローのいずれであれ、作成はすべて `409 workspace_archived` を返し、ワークスペースへの作業の移動も同様です。作業を外に移動することは、引き続き許可されます。アーカイブしたワークスペースを開く理由は、そこから作業を救い出すためだからです。ワークフローの共有のような、それ以外の書き込みには `403 workspace_archived` が返されます。判定には、ステータスではなくコードを使ってください。

## 自分が誰か：GET /v1/me
`GET /v1/me` は、自分のプロフィールを返します。組織が有効なインスタンスでは、`organizations`、`workspaces`、`lastWorkspaceId` も含まれ、各エントリーには自分のロールとステータスが付きます。組織のエントリーには、その組織がワークスペースに使う語句のような、解決済みの `vocabulary` が含まれます。そのため、クライアントが「Class」や「Team」をハードコードする必要はありません。`GET /v1/workspaces` は、同じワークスペースの一覧を単独で返します。

組織のフィールドが取りうる、次の 3 つの状態を区別してください。

| 表示される内容 | 意味 | 対応 |
| --- | --- | --- |
| フィールドが存在しない | このインスタンスには組織がありません。 | ワークスペース切り替えを表示しません。 |
| 存在するが空 | このアカウントはどの組織にも属していません。 | 組織の作成または参加を案内します。 |
| `organizationsUnavailable: true` | 取得に失敗しました。 | それまでの選択を保ちます。 |

## 組織のエンドポイント
すべてのエンドポイントに認証が必要です。ボディは JSON で、レスポンスは `{ "data": … }`、一覧は `{ "data": [ … ], "nextCursor": … }` で、ID は UUID です。

### 組織
| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `POST` | `/v1/orgs` | ログイン済みなら誰でも | `{ name, kind, slug?, acceptTerms?, settings? }`。`kind` は `school` か `team` です。`201` を返します。 |
| `GET` | `/v1/orgs` | 誰でも | 自分の組織を、自分の `role` と `memberStatus` とともに返します。 |
| `GET` | `/v1/orgs/:id` | メンバー | |
| `PATCH` | `/v1/orgs/:id` | オーナー、管理者 | `{ name?, settings? }`。設定はキーごとにマージされ、不明なキーは取り除かれます。 |
| `POST` | `/v1/orgs/:id/transfer-ownership` | オーナー | `{ userId }`。新しいオーナーはアクティブな管理者である必要があり、元のオーナーは同じ操作の中で管理者になります。 |
| `DELETE` | `/v1/orgs/:id` | オーナー | アーカイブされていないワークスペースが 1 つでもあると、`409 has_active_workspaces` で拒否されます。組織は論理削除され、そのスラッグは解放されます。 |
| `POST` | `/v1/orgs/:id/leave` | メンバー | オーナーは脱退できません（`409 owner_cannot_leave`）。先に譲渡してください。脱退すると、その組織のすべてのワークスペースから外れます。 |

- **承認。**新しい組織は、通常は `pending` から始まり、プラットフォームが承認すると `active` になります。待っている間、それが見えるのはオーナーだけで、ほかの人には見えず、中身も何も変更できません。作成した人に伝えておかないと、この待ち時間が失敗のように見えてしまいます。
- **学校には規約への同意が必要です。**学校を作成するには `acceptTerms: true` が必要で、これによって、オーナーは生徒を登録する権限があることを確認します。これがないと、作成は `400 terms_required` を返します。
- **スラッグ**は、小文字、数字、ハイフンからなる 1〜50 文字です。指定するスラッグは、使われていない必要があります（`409 name_taken`）。指定しない場合は、名前から導き出されます。「Sunrise School」は `sunrise-school` になり、次は `sunrise-school-2` になります。
- 組織の作成は、ユーザー 1 人につき 1 時間あたり数件までに制限されています。

### メンバー
| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `GET` | `/v1/orgs/:id/members` | オーナー、管理者 | `?limit=50&cursor=…`、最大 200 件。各行は `userId`、`role`、`status`、`joinedAt`、`email`、`displayName`、`avatarUrl` です。 |
| `PATCH` | `/v1/orgs/:id/members/:userId` | オーナー、管理者 | `{ role?, status? }`：`admin` か `member`、`active` か `suspended` です。オーナー自身の行には使えません。 |
| `DELETE` | `/v1/orgs/:id/members/:userId` | オーナー、管理者 | オーナーには使えません。その組織のすべてのワークスペースからも、その人を削除します。 |

### ワークスペース
| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `POST` | `/v1/orgs/:id/workspaces` | オーナー、管理者 | `{ name, slug?, description?, settings? }`。`201` を返します。スラッグは組織内で一意です。 |
| `GET` | `/v1/orgs/:id/workspaces` | メンバー | メンバーには自分のワークスペースが、オーナーと管理者にはすべてのワークスペースが見えます。`?includeArchived=true` を付けると、アーカイブしたものも含まれます。 |
| `GET` | `/v1/workspaces` | 誰でも | 組織をまたいだ、自分が属するすべてのワークスペースです。 |
| `GET` | `/v1/workspaces/:id` | ワークスペースのメンバー | |
| `PATCH` | `/v1/workspaces/:id` | ワークスペースの管理者 | `{ name?, description?, settings? }` |
| `POST` | `/v1/workspaces/:id/archive` | オーナー、管理者 | アーカイブします。元に戻せて、何も失われません。 |
| `POST` | `/v1/workspaces/:id/unarchive` | オーナー、管理者 | アーカイブを解除します。 |

### ワークスペースのメンバー
| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `GET` | `/v1/workspaces/:id/members` | ワークスペースのメンバー | `?limit&cursor`。メンバーには `userId`、`role`、`displayName`、`avatarUrl`、`addedAt` が見えます。管理者には `status` と `creditCap` も見えます。名簿にメールアドレスは含まれません。 |
| `POST` | `/v1/workspaces/:id/members` | ワークスペースの管理者 | `{ userId, role }`。`role` は `admin` か `member` です。対象の人物は、すでにその組織のアクティブなメンバーである必要があります（`400 not_org_member`）。新しい人を迎えるには、招待してください。 |
| `PATCH` | `/v1/workspaces/:id/members/:userId` | ワークスペースの管理者 | `{ role?, status?, creditCap? }`。`creditCap` はメンバーの利用上限で、上限なしの場合は `null` です。 |
| `DELETE` | `/v1/workspaces/:id/members/:userId` | ワークスペースの管理者 | そのワークスペースからだけ、その人を削除します。組織には残ります。 |

組織のオーナーと管理者は、一覧に載らなくても、その組織のすべてのワークスペースの管理者です。ワークスペースに明示的に追加すると、代わりにその明示的なロールが与えられます。これが、組織の管理者が 1 つのクラスでは単なるメンバーになれる仕組みです。

### 招待
招待は、1 つのメールアドレスを対象にします。そのリンクには、メールの中にしか存在しないトークンが含まれ、招待された人は、ログインする前に、自分が何に招待されているかを確認できます。

| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `POST` | `/v1/orgs/:id/invitations` | オーナー、管理者。組織が許可していれば、ワークスペースの管理者も自分のワークスペースに限り可能 | `{ emails, orgRole?, workspaceId?, workspaceRole? }`。最大 200 件のアドレスを、小文字化・重複排除して受け付けます。アドレスごとに 1 行、`{ email, status, link? }` とともに `201` を返します。 |
| `GET` | `/v1/orgs/:id/invitations` | 上と同じ | `?status=`（`open`、`accepted`、`revoked`、`expired`）、`workspaceId`、`limit`、`cursor`。トークンが返されることはありません。 |
| `DELETE` | `/v1/invitations/:id` | 上と同じ | 取り消します。リンクは使えなくなります。承認済みの招待では拒否されます。 |
| `POST` | `/v1/invitations/:id/resend` | 上と同じ | 新しいトークンと有効期限を発行し、再送します。以前のリンクは使えなくなります。 |
| `GET` | `/v1/invitations/by-token/:token` | 公開 | 招待された人が判断するために必要な情報です。`orgName`、`kind`、`vocabulary`、`inviterName`、`workspaceName`、マスクされたメールアドレス、`expiresAt`、`state` です。IP アドレスごとに制限されます。 |
| `POST` | `/v1/invitations/:token/accept` | ログインした招待対象者 | アカウントのメールアドレスは、招待のものと一致している必要があります（`400 email_mismatch`）。2 回目の承認は拒否されます。 |

- **アドレスごとに、それぞれ 1 行が対応します。**`status` は `sent`、`link_only`、`failed` のいずれかで、`link` は、そのアドレスにメールが送られなかった場合に含まれます。メールプロバイダーのないインストール環境では、すべてのアドレスが `link_only` として返されます。これらのリンクは表示してください。招待はどちらの場合も存在し、リンクがなければ誰もそこに到達できません。
- **招待は 14 日で期限切れになります。**組織が送信できるのは、1 日 500 件までです（`429 bulk_invite_cap_exceeded`）。
- **同じアドレスを再度招待すると**、2 件目を作成する代わりに、`open` 状態の招待が新しいトークンに切り替わります。
- **すでにメンバーである人を招待すると**、招待自体は消費されますが、ロールが変わったり、停止が解除されたりすることはありません。

### 参加コード
参加コードは 8 文字で、部屋の中で声に出して読み上げられる長さです。1 つのワークスペースに、単なる**メンバー**として参加させるためのもので、停止を解除することはなく、組織が許可するメールドメインには従います。

| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `GET` | `/v1/workspaces/:id/join-code` | ワークスペースの管理者 | `{ code, enabled, rotatedAt, rotatedBy }`。作成されていない場合は `null` です。 |
| `POST` | `/v1/workspaces/:id/join-code` | ワークスペースの管理者 | `{ action }`：`rotate`、`enable`、`disable` のいずれかです。コードを一度も持ったことがないワークスペースを有効にすると、コードが作成されます。ローテーションすると置き換わり、古いコードはすぐに使えなくなります。 |
| `POST` | `/v1/workspaces/join` | ログイン済みなら誰でも | `{ code }`。読み上げを聞いて入力した形でも使えます。`BCDF-GHJK` のようなハイフン入りの形、小文字、ゼロの代わりに入力した `O` のような、聞き間違えやすい文字です。1 分あたり、アカウントごとに 10 回、IP アドレスごとに 30 回までに制限されます。 |

### 1 つのワークフローを共有する
権限付与は、1 人に 1 つのワークフローへのアクセスを与えます。アクセスを追加することしかできず、取り除くことはできません。

| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `GET` | `/v1/workflows/:id/collaborators` | ワークフローを閲覧できる人なら誰でも | `{ userId, name, avatarUrl, role, createdAt }` の行です。メールアドレスは含まれません。 |
| `POST` | `/v1/workflows/:id/collaborators` | 下記を参照 | `{ userId }` か `{ email }` のどちらか一方と、`role`（`viewer` か `editor`）です。`201` を返します。1 分あたり 20 件の追加までに制限されます。 |
| `PATCH` | `/v1/workflows/:id/collaborators/:userId` | `POST` と同じ | `{ role }` |
| `DELETE` | `/v1/workflows/:id/collaborators/:userId` | `POST` と同じ、または本人 | 誰でも、自分自身のアクセスは削除できます。 |
| `GET` | `/v1/workflows/shared-with-me` | ログイン済みなら誰でも | 自分のワークスペースの外で、権限を持っているワークフローを、それぞれの `grantedRole` とともに返します。新しい順に、最大 200 件です。 |
| `GET` | `/v1/workflows/:id/access` | 閲覧できる人なら誰でも | `{ access, workspaceId, visibility, canChangeVisibility, canShare, canRun }`。グラフ自体は含まれません。 |

- **共有できる人**：作成者。管理者がメンバーの作業を編集できるワークスペースでは、その管理者。共同編集者が招待できるワークスペースでは、そのワークフローを編集できる人です。
- **メールアドレスで追加する場合**、どの組織のものであっても、アカウントのアドレスと照合されます。`404` は、そのアカウントが存在しないことを意味し、それ以上の情報は開示されません。
- **ワークスペースの外にいる人は、編集者の権限があっても、閲覧だけに制限されます。**実行にはアクティブなメンバーシップが必要なので、外部の共同編集者がワークスペースのクレジットを消費することはありません。
- ワークフローの作成者や自分自身には、権限を付与できません（`400`）。権限付与は、承認のステップなしに即座に有効になります。
- `canChangeVisibility`、`canShare`、`canRun` は、それぞれ独立したルールです。1 つから別の 1 つを推測しないでください。

まったくアクセスできないワークフローは、存在しないワークフローと同じように `404` を返します。すでに見えていて、それ以上の権限が必要な場合にだけ、`403` が返されます。ワークスペースに属するワークフローの実行には、編集権限とアクティブなメンバーシップが必要です。

### 監査ログ
| メソッド | パス | 権限 | ボディまたはクエリ |
| --- | --- | --- | --- |
| `GET` | `/v1/orgs/:id/audit` | オーナー、管理者 | `?cursor&limit`、新しい順です。各エントリーは `{ id, workspaceId, action, targetType, targetId, details, createdAt, actor }` です。システムが行った操作では、`actor` は `null` です。 |

監査ログは、組織が停止されている間も読み取り可能なままです。`action` は、プロダクトの成長とともに増えていくオープンなリストです。知っている操作は表示し、それ以外は生の値をそのまま表示してください。

## ロールと設定
| 範囲 | ロール | できること |
| --- | --- | --- |
| 組織 | `owner` | 管理者にできることすべてに加えて、オーナー権限の譲渡と組織の削除です。オーナーは必ず 1 人で、停止、削除、降格のいずれもされません。 |
| 組織 | `admin` | 設定、オーナー以外のメンバー、ワークスペースの管理です。すべてのワークスペースの管理者でもあります。 |
| 組織 | `member` | 組織に属し、自分が追加されたワークスペースで作業します。 |
| ワークスペース | `admin` | ワークスペースの設定とメンバーを管理します。 |
| ワークスペース | `member` | ワークスペースで作業します。 |

設定は階層化されています。ワークスペースの設定は組織の設定より優先され、組織の設定は、その組織の種類のデフォルトより優先されます。設定されていないキーには下の階層の値が使われ、`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` | 真偽値 | 編集者権限の共同編集者が、さらに共同編集者を招待できるかどうかです。 |

組織だけが持つ設定として、`["school.example"]` のような `allowed_email_domains` と、`{ "workspace": "Cohort" }` のような `vocabulary_overrides` もあります。どちらも、更新すると全体が置き換わります。

**学校**の初期設定は、管理者は編集可、ワークフローは非公開、メンバーは共有された作業を閲覧のみ、メンバーはプロジェクトを作成不可、上限は有効、個人スペースは有効、ワークスペースの管理者は招待可、共同編集者は招待不可、です。**チーム**の初期設定は、管理者は閲覧のみ、ワークフローはワークスペースに公開、メンバーは共有された作業を編集可、メンバーはプロジェクトを作成可、上限は無効、個人スペースは有効、両方の招待設定が有効、です。

## 予算と使用状況
組織は、メンバーの作業の代金を、段階を追うごとに範囲が狭くなる 3 つの段階で支払います。

1. **プール。**組織は、独自のチェックアウトを通じて、組織全体で 1 つのプールにプリペイドのクレジットパックを購入します。
2. **割り当て。**オーナーは、プールからワークスペースの予算にクレジットを移動します。プールは、ワークスペースが得た分だけ減少します。クレジットをプールに戻すこともできますが、割り当てを減らせるのは、そのワークスペースがすでに確保または使用した分までです。
3. **メンバーの上限。**ワークスペースの中では、管理者が 1 人のメンバーの利用額に上限を設定できます。上限は、ワークスペースの設定で有効になっている場合にのみ適用され、そのワークスペースで作業する組織の管理者には、決して適用されません。

ワークスペースの中で行われた作業は、そのワークスペースが支払い、メンバー自身のクレジットは使われません。どの階層でも、余裕は「割り当て」から「確保」と「使用」を引いた値です。これを超える実行は、開始前に `402 budget_exceeded` または `402 member_cap_exceeded` で拒否されます。

| ルート | 権限 | 内容 |
| --- | --- | --- |
| `GET /v1/orgs/:id/credits` | オーナー、組織の管理者 | プール、これまでの購入合計、各ワークスペースの割り当てと、その確保済み・使用済みのクレジットです。 |
| `POST /v1/orgs/:id/credits/checkout` | オーナー | 1 つのプリペイドパックのチェックアウト URL です。ボディは `{ packId }` です。 |
| `POST /v1/orgs/:id/workspaces/:wsId/allocate` | オーナー | プールとワークスペースの間でクレジットを移動します。ボディは `{ delta }` です。新しい余裕を返します。 |
| `GET /v1/workspaces/:id/budget` | メンバー | 自分の使用額、上限、余裕です。管理者には、メンバーごとの行も返されます。 |
| `GET /v1/orgs/:id/usage` | オーナー、組織の管理者 | 組織の使用状況レポートです。 |
| `GET /v1/workspaces/:id/usage` | メンバー、ワークスペースの管理者 | 同じレポートを、1 つのワークスペースについて返します。 |

返金と異議申し立てでは、組織のプールから按分でクレジットが引き戻されますが、ゼロを下回ることはありません。組織を削除するには、先にすべてのワークスペースの割り当てを回収する必要があります。

**自動化は、ワークスペースのクレジットを消費します。**[**Webhook トリガー**（Webhook Trigger）](https://nodaro.ai/docs/nodes/automate/webhook-trigger)の URL はベアラー認証情報です。それを持っている人なら誰でも実行を開始でき、ワークスペース内のワークフローの実行は、そのワークスペースが支払います。Webhook、スケジュール、Telegram のいずれから始まった自動実行も、まずトリガーの作成者がそのワークフローを引き続き実行できるかを確認します。できなくなっている場合、自動化は停止し、実行履歴には `run_requires_authenticated_member` というコードの失敗エントリーが 1 件表示されます。

### 使用状況レポート
2 つの使用状況ルートは、指定した期間について、誰が何にいくら使ったかを答えます。

| クエリ | 意味 |
| --- | --- |
| `from`、`to` | 両端を含む日付で、`YYYY-MM-DD` 形式です。範囲は最大 366 日で、デフォルトは直近 30 日です。 |
| `tz` | 日単位の集計に使う IANA タイムゾーンです。デフォルトは `UTC` です。 |
| `groupBy` | `workspace`（組織のみ）、`member`、`model`、`day`、または個別の実行を新しい順に `cursor` でページングする `none` です。 |
| `workspaceId`、`userId` | レポートを絞り込みます。 |
| `format` | `csv` を指定すると、レポートを CSV で返します。 |

- 各行には、3 つのクレジットの数値があります。`credits` は、それまでに実行にかかった金額です。終わった実行では確定した金額、進行中の実行では保持されている確保分です。`settledCredits` と `inFlightCredits` は、その合計を内訳にしたものです。
- ワークスペースの残りより高くついた従量制の実行では、余裕分までが課金され、残りはプラットフォームが負担します。合計では、これが `platformAbsorbedCredits` として報告され、`settledCredits` から `platformAbsorbedCredits` を引いた分が、実際に予算に反映された金額です。予算でまかなえなかった承認済みアプリの上乗せ分も同様に負担され、`appMarkupAbsorbedCredits` として別に報告されます。
- グループが 5,000 件を超えるレポートは、`truncated: true` を返します。切り詰められるのはグループ化された行だけで、合計は期間全体をカバーします。
- 一般のメンバーには自分の実行しか見えず、メンバーごとのグループ化も拒否されます。ワークスペースの管理者には全員が見え、`userId` で絞り込めます。組織のレポートは、オーナーと組織の管理者のためのものです。
- CSV のエクスポートは、UTF-8、RFC 4180 で、改行は CRLF、バイトオーダーマークはありません。`=`、`+`、`-`、`@` で始まるセルは、表計算ソフトで実行されないように、先頭にアポストロフィが付けられます。エクスポートは、ユーザーごとに 1 分あたり 10 回までに制限され、監査ログに記録されます。
- メンバーがアカウントを削除すると、その実行履歴も一緒に消え、レポートにはその分の欠落が生じます。そのワークフローとプロジェクトは、ワークスペースに残ります。

使用状況レポートのないインスタンスは `404` を返し、レポート機能がまだ利用できない間は `503 billing_unavailable` を返します。

## エラー
| ステータス | コード | 発生するとき |
| --- | --- | --- |
| 400 | `validation_error` | フィールドが無効、カーソルの形式が正しくない、その行に対する変更が許可されていない、または使用状況レポートの日付、タイムゾーン、範囲、グループ化が不正です。 |
| 400 | `terms_required` | `acceptTerms: true` なしで学校を作成しました。 |
| 400 | `not_org_member` | その組織のアクティブなメンバーではない人を、ワークスペースに追加しようとしました。 |
| 400 | `token_workspace_mismatch` | 1 つのワークスペースに紐付けたトークンに、別のワークスペースを指定するヘッダーが付いていました。 |
| 400 | `join_code_invalid` | そのコードが存在しない、コードが無効になっている、またはそのワークスペースがアーカイブされています。3 つとも同じ応答です。 |
| 400 | `invitation_expired`、`invitation_revoked`、`invitation_accepted` | 招待が 14 日を過ぎている、取り消されている、またはすでに承認されています。 |
| 400 | `email_mismatch` | ログイン中のアカウントのメールアドレスが、招待されたものと異なります。 |
| 401 | `unauthorized` | 有効な認証情報がありません。 |
| 403 | `insufficient_role` | メンバーではありますが、その操作に対してロールが不足しています。 |
| 403 | `member_suspended` | メンバーシップが停止されています。 |
| 403 | `org_not_active` | 組織が保留中か停止中で、その操作が何かを変更しようとしています。 |
| 403 | `workspace_archived` | アーカイブしたワークスペースへの書き込みです。 |
| 403 | `not_a_member` | ヘッダーが、選択できないワークスペースを指定しています。 |
| 403 | `domain_not_allowed` | その組織が受け入れるのは、登録済みのメールドメインだけです。 |
| 404 | `not_found` | その組織、ワークスペース、メンバーが存在しないか、自分がそのメンバーではありません。 |
| 404 | `invitation_not_found` | そのトークンに対する招待がありません。組織が有効でない場合も含みます。 |
| 409 | `name_taken` | 指定したスラッグは、すでに使われています。 |
| 409 | `already_a_member` | その人は、すでにワークスペースに参加しています。 |
| 409 | `owner_cannot_leave` | 脱退する前に、オーナー権限を譲渡してください。 |
| 409 | `has_active_workspaces` | 組織を削除する前に、すべてのワークスペースをアーカイブしてください。 |
| 429 | `rate_limit_exceeded` | 組織の作成、参加の試行、CSV エクスポートが多すぎます。 |
| 429 | `bulk_invite_cap_exceeded` | 組織が、1 日の招待の上限に達しました。 |
| 503 | `billing_unavailable` | このインスタンスでは、使用状況レポートがまだ利用できません。 |
| 503 | `audit_unavailable` | CSV エクスポートを監査ログに記録できなかったため、拒否されました。もう一度試してください。 |

## SDK、CLI、MCP から
- **SDK**：`client.organizations` と `client.workspaces` が、これらのエンドポイントをカバーします。レポート用の `usage`、`usageRows`、`usageCsv` も含みます。`client.me()` には、組織のフィールドが含まれます。
- **CLI**：`nodaro org` と `nodaro workspace` です。たとえば `nodaro org invite <orgId> --email ada@school.example --workspace <id>` や `nodaro org usage <orgId> --from 2026-09-01 --to 2026-09-30 --group-by member --csv > september.csv` です。invite コマンドは、アドレスごとに 1 行を出力し、メールが送られなかったアドレスには、リンクも表示します。
- **MCP**：`list_workspaces` と `select_workspace` ツールです。選択内容はセッションをまたいで記憶され、セッションごとに再確認されます。これらのツールを使う OAuth アプリには、`workspaces:read` と `workspaces:write` のスコープが必要です。

コマンドとメソッドの完全な一覧は、[CLI](https://nodaro.ai/docs/developers/cli) と [SDK](https://nodaro.ai/docs/developers/sdk) を参照してください。

## Frequently asked questions

### Nodaro API のリクエストを、ワークスペースで操作させるにはどうすればよいですか？

X-Nodaro-Workspace ヘッダーに、ワークスペースの ID を送信します。これによって、一覧がどのワークスペースを読み取るか、作成したものがどこに置かれるかが決まります。SDK では createClient({ workspaceId }) または client.withWorkspace(id) を、CLI では --workspace を使います。

### ワークスペースヘッダーを誤ると、自分の作業が見えなくなったり、ほかの人の作業にアクセスできたりしますか？

いいえ。ヘッダーが決めるのは範囲であり、アクセス権ではありません。ID を指定して何かを読み取る、変更する、削除する、実行するといった操作は、そのオブジェクト自身のワークスペースに従います。自分が属していないワークスペースには、403 not_a_member が返されます。

### API トークンを 1 つのワークスペースに紐付けられますか？

はい。ログイン中のセッションから、PATCH /v1/api-tokens/:id と workspaceId で紐付けます。その後、トークンはすべてのリクエストでそのヘッダーを送ったかのように動作し、別のワークスペースを指定するヘッダーには 400 token_workspace_mismatch が返されます。

### 作成が 403 personal_space_disabled を返すのはなぜですか？

自分の組織が、メンバーにワークスペース内での作業の作成を義務付けているためです。自分のワークスペースのいずれかを指定した X-Nodaro-Workspace ヘッダーを送信すれば、同じ呼び出しが成功します。

### セルフホスティング環境でも、組織を使えますか？

いいえ。組織は Nodaro Cloud の機能で、インスタンスごとに有効にします。セルフホスティングのビルドには組織が含まれず、ワークスペースヘッダーも無視されます。

### 招待がメールで届かず、代わりにリンクが返ってきたのはなぜですか？

インストール環境にメールプロバイダーがないか、配信が失敗したためです。どちらの場合も招待自体は存在するので、返されたリンクを自分で本人に送ってください。
