From 384524a027d8fa52e2f72ab59b2ea074b8b7d92c Mon Sep 17 00:00:00 2001 From: Reynaldi Chernando <12949382+reynaldichernando@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:45:54 +0700 Subject: [PATCH] improve teams docs (#4025) --- src/docs/src/Objects.md | 2 + src/docs/src/Objects/team.md | 32 ++++ src/docs/src/Objects/teamdirectoryentry.md | 16 ++ src/docs/src/Teams.md | 178 +++++++++--------- src/docs/src/Teams/list.md | 70 +++---- src/docs/src/Teams/listDirectory.md | 101 +++++----- .../playground/examples/teams-directory.html | 11 +- .../src/playground/examples/teams-list.html | 11 +- src/docs/src/sidebar.js | 14 ++ 9 files changed, 232 insertions(+), 203 deletions(-) create mode 100644 src/docs/src/Objects/team.md create mode 100644 src/docs/src/Objects/teamdirectoryentry.md diff --git a/src/docs/src/Objects.md b/src/docs/src/Objects.md index 2c4e30cd0..72ffc9dfa 100644 --- a/src/docs/src/Objects.md +++ b/src/docs/src/Objects.md @@ -23,6 +23,8 @@ Various object types and classes that represent different entities in the Puter - **[PuterPeerServer](/Objects/puterpeerserver/)** - Represents a peer server and its connected clients - **[Speech2TxtResult](/Objects/speech2txtresult/)** - Represents speech-to-text transcription results - **[Subdomain](/Objects/subdomain/)** - Represents a subdomain +- **[Team](/Objects/team/)** - Represents a Puter team +- **[TeamDirectoryEntry](/Objects/teamdirectoryentry/)** - Represents a member of a team - **[TTSEngine](/Objects/ttsengine/)** - Represents an available text-to-speech engine/model - **[TTSVoice](/Objects/ttsvoice/)** - Represents an available text-to-speech voice - **[ToolCall](/Objects/toolcall/)** - Represents a tool invocation request diff --git a/src/docs/src/Objects/team.md b/src/docs/src/Objects/team.md new file mode 100644 index 000000000..3412d43cc --- /dev/null +++ b/src/docs/src/Objects/team.md @@ -0,0 +1,32 @@ +--- +title: Team +description: The Team object containing information about a Puter team. +--- + +The `Team` object contains information about a Puter team, returned by [`puter.teams.list()`](/Teams/list/). + +## Attributes + +#### `uid` (String) + +The team's unique identifier. Pass this to [`puter.teams.listDirectory()`](/Teams/listDirectory/). + +#### `name` (String) + +The team's display name, or `null` if it has none. + +#### `handle` (String) + +The team's short handle, or `null` if it has none. The handle can change, so store the `uid` instead. + +#### `isOwner` (Boolean) + +Whether the current user is the team owner. + +#### `directoryEnabled` (Boolean) + +Whether the team owner has enabled the directory setting, which lets apps use the Teams API for this team. + +#### `createdAt` (String) + +When the team was created, in `YYYY-MM-DDTHH:MM:SSZ` format. diff --git a/src/docs/src/Objects/teamdirectoryentry.md b/src/docs/src/Objects/teamdirectoryentry.md new file mode 100644 index 000000000..deb1356c0 --- /dev/null +++ b/src/docs/src/Objects/teamdirectoryentry.md @@ -0,0 +1,16 @@ +--- +title: TeamDirectoryEntry +description: The TeamDirectoryEntry object containing information about a team member. +--- + +The `TeamDirectoryEntry` object contains information about a team member, returned by [`puter.teams.listDirectory()`](/Teams/listDirectory/). + +## Attributes + +#### `username` (String) + +The member's Puter username. + +#### `uuid` (String) + +The member's unique account identifier. Unlike the username, it never changes. diff --git a/src/docs/src/Teams.md b/src/docs/src/Teams.md index 8e06af99e..e251a4394 100644 --- a/src/docs/src/Teams.md +++ b/src/docs/src/Teams.md @@ -1,109 +1,111 @@ --- title: Teams -description: Detect team context and look up a user's colleagues with the Teams API +description: Get the user's team and list its members with the Puter.js Teams API platforms: [websites, apps, nodejs, workers] ---
The Teams API is in beta. Method shapes, limits, and behavior may change between releases.
-The Puter.js Teams feature lets your app see the team context around the user in front of it: whether they belong to one, and who their colleagues are. +The Teams API lets your app interact with the Puter Teams feature. -A team is a Puter account that pays for other accounts. Members are ordinary Puter accounts — your app talks to them like any other user, and the team never gains access to a member's files. Team *administration* (creating teams, provisioning accounts, suspending them) happens in the account console rather than through apps, so what `puter.teams` offers is read-only. +Puter Teams lets an organization bring its members together. Any user can create a team and invite members. Each member keeps their own Puter account, but belongs to the team, so the owner can manage them and cover their paid plan in one place. + +With this API, your app can get the team the user currently belongs to and list the members of that team. To use the Teams API, the team owner must enable the directory setting in the [Teams dashboard](https://puter.com/#teams). ## Features -**Team context.** `list()` tells you whether the signed-in user belongs to a team, and is also how you detect whether the deployment has Teams at all: it rejects with `not_found` where the feature is off, and resolves to an empty array where it is on and the user has no team. Called from an app, it only includes teams whose owner opened the directory to apps (see below); others are simply omitted. +
+
Get Team
+
List Members
+
-**Member and colleague lookup.** `listDirectory()` returns the team's active members so your app can suggest people by name instead of asking users to type usernames. It is opt-in per team: until an owner opens the directory to apps, an app gets `team_not_found`, which is indistinguishable from having no team. +
-**Sharing with a team.** Anything shared with a team reaches every member with one grant, including anyone added later. Pass the team's `uid` as the recipient of [`puter.fs.share()`](/FS/share/); there is no string form, since a bare string is always read as an email or username. +#### Get the current user's team information -**Stable identifiers.** A team has a `uid` and an optional `handle`. Only the `uid` is stable — a handle is a mutable label, and deleting the team releases it for anyone else to take. Display the `name` and `handle`; pass the `uid`. +```html;teams-list + + + + + + +``` + +
+ +
+ +#### List the members of the user's team + +```html;teams-directory + + + + + + + + +``` + +
## Functions -- **[`puter.teams.list()`](/Teams/list/)** - List the teams the signed-in user belongs to, and detect whether Teams is available at all -- **[`puter.teams.listDirectory()`](/Teams/listDirectory/)** - List a team's members, where the owner has opened the directory to apps - -Both are keyset-paginated and take the same options; see each method page for the paging forms and the full error list. +- **[`puter.teams.list()`](/Teams/list/)** - Get the current user's team information +- **[`puter.teams.listDirectory()`](/Teams/listDirectory/)** - List the members of a team ## Examples -Detect whether the user is on a team +You can see various Puter.js Teams features in action from the following examples: -```html - - - - - - -``` - -Suggest a colleague to share with - -```html - - - - - - -``` - -Share a file with the whole team - -```html - - - - - - -``` - -## What is deliberately absent - -- **No sharing-policy controls.** A team cannot restrict who its members share - with: there is no external-sharing policy, no domain allowlist, and no control - over public links. A member shares exactly as any other Puter user does, with - anyone. This is the assumption most teams bring the other way round, so it is - worth stating plainly before you rely on it. -- **No administration from apps.** Provisioning, suspension, credentials and - audit belong to the account console; apps and API tokens are refused there. +- [Find the user's team](/playground/teams-list/) +- [Offer colleagues in a picker](/playground/teams-directory/) diff --git a/src/docs/src/Teams/list.md b/src/docs/src/Teams/list.md index c979fd558..b4b73e391 100644 --- a/src/docs/src/Teams/list.md +++ b/src/docs/src/Teams/list.md @@ -1,16 +1,14 @@ --- title: puter.teams.list() -description: List the teams you belong to. +description: Get the current user's team information. platforms: [websites, apps, nodejs, workers] ---
The Teams API is in beta. Method shapes, limits, and behavior may change between releases.
-Returns the teams the caller belongs to — both those they own and those they were provisioned into. +Get the current user's team information. If the user is not on a team, it resolves to an empty array. -This is also how an app discovers whether teams exist on this deployment at all: it rejects with `not_found` where the feature is off, and resolves to an empty array where it is on and the caller has no team. - -Called from an app acting for the user, the list only includes teams whose owner opened the directory to apps (see [`listDirectory()`](/Teams/listDirectory/)); a team that has not is simply left out, the same way a team the caller isn't in would be. +The team owner must enable the directory setting in the [Teams dashboard](https://puter.com/#teams) for the team to be returned to your app. ## Syntax @@ -23,69 +21,55 @@ puter.teams.list(options) #### `options` (Object) (optional) -The standard list options. All four are optional, and they decide the shape of what resolves: +An object with the following optional properties: -| Call | Resolves to | -| -- | -- | -| No options | The whole set as an array, fetched page by page under the hood | -| `{ limit }` | An array, capped at one page | -| `{ cursor }` or `{ includeTotal: true }` | One `{ items, cursor? }` page. `cursor` is absent on the last page | -| `{ stream: true }` | An async iterator of `{ items, cursor? }` pages | - -This route is keyset-paginated, so `offset` is not accepted — passing it throws `invalid_request`. Pass `cursor` to resume from a position. +- `limit` (Number): Maximum number of items to return in a single call. +- `cursor` (String): A pagination cursor from a previous call. Pass the `cursor` value returned by the previous page to fetch the next one. +- `includeTotal` (Boolean): If `true`, the result includes a `total` count of every item across all pages. +- `stream` (Boolean): If `true`, the method returns an async iterator of pages instead of a promise, for use with `for await ... of`. ## Return value -A `Promise` that resolves to an array of `Team` objects, or to a `{ items, cursor? }` page when a pagination option is given. With `stream: true` it returns an async iterator of pages instead. +A `Promise` that resolves to either: -#### `Team` +- An array of [`Team`](/Objects/team/) objects, or +- A page object `{ items, cursor, total }` when using `cursor` or `includeTotal` in `options`. `items` is an array of [`Team`](/Objects/team/) objects, `cursor` is present only when there are more pages, and `total` is present only when `includeTotal` is `true`. -| Field | Type | Description | -| -- | -- | -- | -| `uid` | `string` | The team's stable identifier — pass this, not the handle. | -| `name` | `string \| null` | Its display name. | -| `handle` | `string \| null` | Its short handle, unique while the team exists. | -| `isOwner` | `boolean` | Whether the caller is the owner account. | -| `directoryEnabled` | `boolean` | Whether the owner has opened the member directory to apps. | -| `createdAt` | `string` | When it was created. | +With `stream: true`, the method returns an async iterator of page objects instead. ## Errors -A rejection carries an `Error` with a stable `code`: +A rejection carries an `Error` with a `code`: | Code | Meaning | | -- | -- | -| `invalid_request` | Refused before reaching the server — a blank `uid`, or an `offset` on a keyset list. | -| `token_missing` | No authentication token was presented. | -| `token_auth_failed` | The token presented did not authenticate. | +| `invalid_request` | `options` contains `offset`, which is not supported. Use `cursor` instead. | +| `token_missing` | The user is not signed in. | +| `token_auth_failed` | The user's session is invalid. | | `forbidden` | Called with a scoped access token. | -| `account_is_not_verified` | The caller's email has not been confirmed. | -| `not_found` | Teams are turned off on this deployment. | +| `account_is_not_verified` | The user's email has not been confirmed. | | `too_many_requests` | The rate limit was exceeded. See [Rate Limits & Quotas](/rate-limits-and-quotas/). | -## Examples +## Example -Show the caller's teams, or nothing where the feature is off - -```html +```html;teams-list diff --git a/src/docs/src/Teams/listDirectory.md b/src/docs/src/Teams/listDirectory.md index 6888ceb3c..3c6d4d996 100644 --- a/src/docs/src/Teams/listDirectory.md +++ b/src/docs/src/Teams/listDirectory.md @@ -1,19 +1,14 @@ --- title: puter.teams.listDirectory() -description: Look up the user's colleagues, where the team has opened its directory to apps. +description: Get the list of members of a team. platforms: [websites, apps, nodejs, workers] ---
The Teams API is in beta. Method shapes, limits, and behavior may change between releases.
-Returns the team's member directory — the colleagues of the user your app is -running for, active accounts only. It is consent-gated: the team's owner has to -open the directory to apps, and until they do it rejects with `team_not_found` -for every caller, indistinguishable from the team not existing. The same -consent decides whether an app sees the team in [`list()`](/Teams/list/). +Get the list of members of a team. -The membership is always the signed-in user's, never the app's: an app can only -see the directory of a team its user belongs to. +The current user must be a member of the team, and the team owner must enable the directory setting in the [Teams dashboard](https://puter.com/#teams). ## Syntax @@ -26,77 +21,79 @@ puter.teams.listDirectory(uid, options) #### `uid` (String) (required) -The team's `uid`, from [`list()`](/Teams/list/). +The team's `uid`, from [`puter.teams.list()`](/Teams/list/). #### `options` (Object) (optional) -The standard list options. All four are optional, and they decide the shape of what resolves: +An object with the following optional properties: -| Call | Resolves to | -| -- | -- | -| No options | The whole set as an array, fetched page by page under the hood | -| `{ limit }` | An array, capped at one page | -| `{ cursor }` or `{ includeTotal: true }` | One `{ items, cursor? }` page. `cursor` is absent on the last page | -| `{ stream: true }` | An async iterator of `{ items, cursor? }` pages | - -This route is keyset-paginated, so `offset` is not accepted — passing it throws `invalid_request`. Pass `cursor` to resume from a position. +- `limit` (Number): Maximum number of items to return in a single call. +- `cursor` (String): A pagination cursor from a previous call. Pass the `cursor` value returned by the previous page to fetch the next one. +- `includeTotal` (Boolean): If `true`, the result includes a `total` count of every item across all pages. +- `stream` (Boolean): If `true`, the method returns an async iterator of pages instead of a promise, for use with `for await ... of`. ## Return value -A `Promise` that resolves to an array of -`TeamDirectoryEntry` objects, or to a -`{ items, cursor? }` page when a pagination option is given. With -`stream: true` it returns an async iterator of pages instead. +A `Promise` that resolves to either: -#### `TeamDirectoryEntry` +- An array of [`TeamDirectoryEntry`](/Objects/teamdirectoryentry/) objects, or +- A page object `{ items, cursor, total }` when using `cursor` or `includeTotal` in `options`. `items` is an array of [`TeamDirectoryEntry`](/Objects/teamdirectoryentry/) objects, `cursor` is present only when there are more pages, and `total` is present only when `includeTotal` is `true`. -| Field | Type | Description | -| -- | -- | -- | -| `username` | `string` | A colleague's Puter username. | -| `uuid` | `string` | Their stable account identifier. | +With `stream: true`, the method returns an async iterator of page objects instead. ## Errors -A rejection carries an `Error` with a stable `code`: +A rejection carries an `Error` with a `code`: | Code | Meaning | | -- | -- | -| `invalid_request` | Refused before reaching the server — a blank `uid`, or an `offset` on a keyset list. | -| `token_missing` | No authentication token was presented. | -| `token_auth_failed` | The token presented did not authenticate. | +| `invalid_request` | `uid` is empty, or `options` contains `offset`, which is not supported. Use `cursor` instead. | +| `token_missing` | The user is not signed in. | +| `token_auth_failed` | The user's session is invalid. | | `forbidden` | Called with a scoped access token. | -| `account_is_not_verified` | The caller's email has not been confirmed. | -| `not_found` | Teams are turned off on this deployment. | -| `team_not_found` | No such team, the caller is not a member of it, or the owner has not opened the directory to apps. | +| `account_is_not_verified` | The user's email has not been confirmed. | +| `team_not_found` | The team does not exist, the user is not a member, or the owner has not enabled the directory setting. | | `too_many_requests` | The rate limit was exceeded. See [Rate Limits & Quotas](/rate-limits-and-quotas/). | -## Examples +## Example -Suggest colleagues to share with - -```html +```html;teams-directory + + diff --git a/src/docs/src/playground/examples/teams-directory.html b/src/docs/src/playground/examples/teams-directory.html index 8d84285d6..97f358c7e 100644 --- a/src/docs/src/playground/examples/teams-directory.html +++ b/src/docs/src/playground/examples/teams-directory.html @@ -7,16 +7,7 @@ const select = document.getElementById('colleagues'); (async () => { - let teams; - try { - teams = await puter.teams.list(); - } catch (e) { - if (e.code !== 'not_found') throw e; - select.innerHTML = ''; - return; - } - - const team = teams[0]; + const [team] = await puter.teams.list(); if (!team) { select.innerHTML = ''; return; diff --git a/src/docs/src/playground/examples/teams-list.html b/src/docs/src/playground/examples/teams-list.html index a4df777ec..12efd3b24 100644 --- a/src/docs/src/playground/examples/teams-list.html +++ b/src/docs/src/playground/examples/teams-list.html @@ -3,16 +3,7 @@