Claude
Cursor
GitHub Copilot
Skill
preset-admin
Manage Preset teams, workspaces, memberships, invites, role identifiers, seat checks, and audit logs through direct Management API calls. Use only for direct API workflows; Do not use for MCP-only work.
Virus-scanned
Reviewed automatically before listing.
Download
preset-io-agent-skills-plugins_preset-api-skills_skills_preset-admin-73d2674.zip · 13 KB
Install
skills CLI
npx skills add https://github.com/preset-io/agent-skills/tree/master/plugins/preset-api-skills/skills/preset-admin
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install preset-io-agent-skills@llmmart
Git
git clone https://github.com/preset-io/agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole preset-io/agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
preset-admin
Use for Preset Management API administration beyond read-only workspace discovery.
Always
- Auth and conventions come from
preset-api(JWT exchange, base URLs, Rison); resolve the workspace hostname through the Management API when it is not already known. - Default to read-only preflight and ID/role lookup.
- Resolve team, workspace, user, invite, and role identifiers from API responses before mutations.
- Get explicit confirmation before role changes, invites, member removals, workspace lifecycle actions, audit downloads, or any write.
- Use
preset-roles-permissionsfor permission-sensitive role/access changes.
Decision Rules
- Classify membership role and invite changes as approval-gated mutations.
- Use metadata reads for membership, role, invite, and audit inspection.
- Require target and effect summary before any access change.
- Avoid applying invite or member role changes until approval is explicit.
Workflow Order
- Identify team, workspace, user, invite, member, and role identifiers.
- Inspect membership, role, invite, and audit metadata.
- Prepare approval summary with target and expected effect.
- Stop before invite, member role, removal, or workspace change.
Retrieve
- Team membership routing: references/team-memberships.md
- Team member lookup, seat checks, response fields: references/team-member-lookup.md
- Team role changes and removals: references/team-member-access-changes.md
- Workspace create/update/delete/un-hibernate: references/workspace-management.md
- Invites, cancellation, resend, bulk invite: references/invites.md
- Audit log lookup and downloads: references/audit-logs.md
- Role identifiers: references/role-identifiers.md
- Unsupported or adjacent Manager endpoints: references/deferrals.md
- Approval policy before writes/downloads: load
preset-apiand thenreferences/safety-policy.md.
Do Not
- Do not use internal
/api-internal/*, billing/payment, SCIM, API key CRUD, permission/DAR/RLS, or database connection mutation routes from this skill.
Files (agent-skills)
-
examples
-
team_memberships.py 1.1 KB
from urllib.parse import urlencode def list_team_members(client, team_name, email=None, user_type=None): params = {"page_number": 1, "page_size": 100} if email: params["user_name_or_email"] = email if user_type: params["user_type"] = user_type return client.mgmt( "GET", f"/teams/{team_name}/memberships/?{urlencode(params)}", )["payload"] def get_team_member(client, team_name, user_id): return client.mgmt( "GET", f"/teams/{team_name}/memberships/{user_id}/", )["payload"] def has_seats_remaining(client, team_name): return client.mgmt( "GET", f"/teams/{team_name}/has-seats-remaining/", )["payload"] def update_team_role_after_confirmation(client, team_name, user_id, team_role_id): return client.mgmt( "PATCH", f"/teams/{team_name}/memberships/{user_id}/", json={"team_role_id": team_role_id}, )["payload"] def remove_team_member_after_confirmation(client, team_name, user_id): client.mgmt("DELETE", f"/teams/{team_name}/memberships/{user_id}/")
-
-
references
-
audit-logs.md 5.1 KB
# Audit Logs Reference Audit log APIs are Manager v2 routes. In Manager source they are mounted under `/api/v2/audit/teams/{team_name}/logs`. The public API base already includes the API prefix, so production examples use `https://api.app.preset.io/v2/audit/teams/{team_name}/logs`. Set `PRESET_API_BASE_V2` for non-production environments. Audit queries are read-only. Audit downloads can expose sensitive activity data and may send email or create a retrievable download token, so require explicit confirmation before `POST /downloads/`. ## Query Audit Logs ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v2/audit/teams/{team_name}/logs/?page_number=1&page_size=100" \ | jq '.payload' ``` ```python logs = client.mgmt_v2( "GET", f"/audit/teams/{team_name}/logs/?page_number=1&page_size=100", )["payload"] ``` Supported filters: | Query parameter | Description | |---|---| | `entity_type` | Entity type such as `user`, `dataset`, or `database` | | `entity_name` | Entity display name | | `entity_id` | Entity immutable ID | | `action` | One or more friendly action names or action URNs | | `user` | One or more usernames or emails | | `workspace_name` | One or more workspace names | | `start_time` | Inclusive ISO 8601 start datetime | | `end_time` | Inclusive ISO 8601 end datetime | | `page_number` | 1-based page number | | `page_size` | Page size, max 500 | | `direction` | `asc` or `desc` | | `order_by` | Sort field | Use repeated query params for list filters: ```python logs = client.mgmt_v2( "GET", f"/audit/teams/{team_name}/logs/" "?page_number=1&page_size=100" "&action=user:workspace_role_update" "&action=invite:create" "&user=admin@example.com" "&workspace_name=abcd1234", )["payload"] ``` Allowed `order_by` values: ```text action, user, entity_id, entity_name, entity_type, timestamp, workspace_name, is_mcp ``` Responses include `meta.count`. ## List Audit Actions ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v2/audit/teams/{team_name}/logs/actions/" \ | jq '.payload' ``` ```python actions = client.mgmt_v2( "GET", f"/audit/teams/{team_name}/logs/actions/", )["payload"] ``` The response contains friendly `action_name` values and full `action_urn` values. Query accepts friendly names and maps them to URNs in Manager. Useful Phase 3 actions include: | Friendly action | Meaning | |---|---| | `user:team_role_update` | Team role changed | | `user:workspace_role_update` | Workspace role changed | | `invite:create` | Invite created | | `invite:accept` | Invite accepted | | `auditlog:download_request` | Audit log download requested | | `auditlog:download_retrieve` | Audit log download retrieved | ## Request Audit Log Download Confirmation summary should include the team, filters, `via_email` value, approximate scope if known, and whether a CSV or email-delivered download is expected. For email-delivered downloads, use `client.mgmt_v2()` because Manager returns JSON: ```python download = client.mgmt_v2( "POST", f"/audit/teams/{team_name}/logs/downloads/", json={ "via_email": True, "action": ["user:workspace_role_update", "invite:create"], "workspace_name": ["abcd1234"], "page_number": 1, "page_size": 500, "order_by": "timestamp", "direction": "desc", }, ) token = download["payload"]["token"] ``` When `via_email` is true, Manager returns `201` with a token and sends the actual download link later. For immediate CSV downloads, use `client.mgmt_v2_response()` because the response body is CSV, not JSON: ```python resp = client.mgmt_v2_response( "POST", f"/audit/teams/{team_name}/logs/downloads/", json={ "via_email": False, "action": ["user:workspace_role_update", "invite:create"], "workspace_name": ["abcd1234"], "page_number": 1, "page_size": 500, "order_by": "timestamp", "direction": "desc", }, ) csv_bytes = resp.content ``` ## Retrieve Audit Log Download ```bash DOWNLOAD_URL="$( curl -sS -D - -o /dev/null \ -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v2/audit/teams/{team_name}/logs/downloads/?token={token}" \ | awk 'tolower($1) == "location:" {print $2}' \ | tr -d '\r' )" curl -sS "$DOWNLOAD_URL" ``` ```python import requests redirect_resp = client.mgmt_v2_response( "GET", f"/audit/teams/{team_name}/logs/downloads/?token={token}", allow_redirects=False, ) download_url = redirect_resp.headers["Location"] csv_resp = requests.get(download_url, timeout=60) csv_resp.raise_for_status() csv_bytes = csv_resp.content ``` Download tokens are sensitive. They appear in URL query strings, so do not print them in logs, PR comments, CI output, shell history, proxy/access logs, or handoff notes. ## Common Failures | Status | Likely cause | |---|---| | `400` | invalid date range, unsupported `order_by`, page size over 500, too-large download, or invalid workspace filter | | `403` | API key owner is not a team admin | | `404` | audit logs feature flag is disabled, download token not found, or team not found | -
deferrals.md 2.2 KB
# Deferred Admin Surfaces Phase 3 covers teams, workspaces, workspace memberships, invites, role identifiers, and audit logs. The following Manager surfaces are important but intentionally out of scope for this skill. ## Defer To Future Skills | Surface | Why deferred | |---|---| | User groups and SCIM provisioning | Group-derived roles can override direct user roles, and SCIM has separate auth and provisioning semantics. | | Permission, DAR, and RLS APIs | These are high-impact access-control APIs guarded by `PERMISSION_API_ENABLED`; route role/permission review through `preset-roles-permissions` and keep unsupported APIs deferred until separately reviewed. | | Database connection creation, update, and tests | These affect credentials, network access, and workspace data plane behavior; use `preset-database-connections` for documented workspace API flows. | | Embedded guest tokens and access-token keys | These issue or manage embeddable access credentials; use `preset-guest-tokens` for guest-token creation and keep access-token key lifecycle deferred until separately reviewed. | | Trusted domains | These affect embedding and external origins; use a separate embedded/admin workflow. | | Homepage settings | User-specific workspace UI state, not core team/workspace administration. | | API key and SCIM token CRUD | Credential lifecycle management needs separate secret-handling rules. | | Billing, payment, downgrade, and subscription routes | Business-critical billing workflows need separate review and approvals. | | Internal admin routes under `/api-internal/*` | Internal-only routes are not public Management API examples and often require internal roles. | | Workspace clone, hibernation recovery, deployment assignment, secret rotation, and health checks | Operational control-plane workflows require environment-specific runbooks. | ## How To Respond When a user asks for a deferred workflow: 1. Explain that the current Preset admin skill does not document that operation. 2. Name the likely API area if known. 3. Ask for confirmation to analyze Manager source and create a separate reviewed workflow before making any mutation. Do not improvise mutation examples for deferred surfaces from Manager source without a separate review. -
invites.md 4.9 KB
# Invites Reference Invite list, create, bulk-create, and cancel endpoints require team-admin permissions. Resending is API-key allowed in Manager, but still affects email delivery. Load the safety policy and get explicit confirmation before mutating invites. Invite payloads use both numeric `team_role_id` and string workspace role identifiers. See [role-identifiers.md](role-identifiers.md) before selecting roles. ## List Pending Invites ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v1/teams/{team_name}/invites/" | jq '.payload' ``` ```python invites = client.mgmt("GET", f"/teams/{team_name}/invites/")["payload"] ``` Common response fields: | Field | Description | |---|---| | `id` | Numeric invite ID for cancel/resend | | `email` | Invitee email | | `team_role_id` | Numeric team role ID | | `workspace_ids` | Workspace IDs included in the invite | | `workspace_role_identifier` | Workspace role applied by the invite | ## Create One Invite Preflight seat limits first: ```python seat_check = client.mgmt("GET", f"/teams/{team_name}/has-seats-remaining/")["payload"] if not seat_check.get("has_seats_remaining"): raise RuntimeError("Team has no seats remaining") ``` Confirmation summary should include: - invitee email - team name - numeric `team_role_id` and role name - workspace IDs and titles, if any - `workspace_role_identifier` - API-key-safe seat preflight result ```bash TEAM_ROLE_ID="${PRESET_TEAM_ROLE_ID:?set PRESET_TEAM_ROLE_ID to the verified team role ID}" curl -s -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ "https://api.app.preset.io/v1/teams/{team_name}/invites/" \ -d "{\"email\":\"jdoe@example.com\",\"team_role_id\":$TEAM_ROLE_ID,\"workspace_ids\":[123],\"workspace_role_identifier\":\"PresetGamma\"}" ``` ```python invite = client.mgmt( "POST", f"/teams/{team_name}/invites/", json={ "email": "jdoe@example.com", "team_role_id": team_role_id, "workspace_ids": [workspace_id], "workspace_role_identifier": "PresetGamma", }, )["payload"] ``` Use `workspace_ids` and `workspace_role_identifier` when granting workspace access as part of the invite. Omit them for a team-only invite. ## Create Many Invites Bulk invite requests use `POST /teams/{team_name}/invites/many/`. Do not exceed the Manager bulk invite limit; Manager currently accepts up to 50 invite entries per request. ```python payload = { "invites": [ { "email": "analyst@example.com", "team_role_id": team_role_id, "workspace_ids": [workspace_id], "workspace_role_identifier": "PresetReportsOnly", }, { "email": "creator@example.com", "team_role_id": team_role_id, "workspace_ids": [workspace_id], "workspace_role_identifier": "PresetGamma", }, ] } response_payload = client.mgmt( "POST", f"/teams/{team_name}/invites/many/", json=payload, )["payload"] created_invites = response_payload["invites"] ``` Bulk invite responses wrap created invite records in `payload["invites"]`, unlike the single-invite endpoint where `payload` is the invite record. Confirmation for bulk invites should summarize every email and role. Do not hide invite details behind "same as above" when asking for approval. ## Cancel A Pending Invite ```bash curl -s -X DELETE \ -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v1/teams/{team_name}/invites/{invite_id}/" ``` ```python client.mgmt("DELETE", f"/teams/{team_name}/invites/{invite_id}/") ``` Confirm the invite ID, email, team, and expected cancellation effect before deleting. ## Resend A Pending Invite Resolve `invite_id` from the team's pending invite list before resending. Confirmation should include the invite ID, email, team, and expected email delivery effect. ```bash curl -s -X POST \ -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v1/teams/{team_name}/invites/resend/{invite_id}/" ``` ```python client.mgmt("POST", f"/teams/{team_name}/invites/resend/{invite_id}/") ``` ## Accept Invite Codes Accepting an invite is an end-user/session flow, not an admin API-key workflow. Manager exposes an unauthenticated invite lookup and a session-user `POST /teams/{team_name}/invites/accept/{code}/` route, but agents using this skill should not accept invites on a user's behalf with API credentials. When a user asks to accept an invite, direct them to the Preset UI or ask for confirmation to design a separate browser/session-backed workflow. Do not improvise an API-key accept example. ## Common Failures | Status | Likely cause | |---|---| | `400` | duplicate invite, user already accepted, workspace outside team, invalid workspace role, invite pending limit reached, or seat limit reached | | `403` | API key owner is not a team admin or invite email domain is not allowed | | `404` | invite, team, or workspace not found | -
role-identifiers.md 3.5 KB
# Role Identifiers Reference Preset has two different role concepts in the Management API. | Concept | Field | Shape | Used by | |---|---|---|---| | Team role | `team_role_id` | Numeric ID | Team invites and team membership role changes | | Workspace role | `workspace_role_identifier` or `role_identifier` | String identifier | Workspace membership and invite workspace access | Do not mix these values. ## Resolve Roles Dynamically Prefer the current team payload before using any static workspace role table: ```python team = client.mgmt("GET", f"/teams/{team_name}/")["payload"] workspace_roles = team.get("workspace_roles", []) for role in workspace_roles: print(role["role_identifier"], role.get("name") or role.get("role_name")) ``` The `workspace_roles` field is produced by Manager from default roles, feature flags, and team custom roles. It is the safest source for a specific team because roles such as `PresetLimitedAdmin`, `PresetDelta`, `PresetEpsilon`, or custom role identifiers may or may not be enabled. The `name` and `role_name` fields vary by source serializer, so examples use `role.get("name") or role.get("role_name")` when printing a friendly label. ## Default Workspace Role Identifiers These defaults come from Manager's `DefaultWorkspaceRolesEnum`. | Identifier | Friendly role | Notes | |---|---|---| | `Admin` | Workspace Admin | Creator-level admin access | | `PresetLimitedAdmin` | Limited Admin | Feature-gated | | `PresetAlpha` | Primary Creator | Creator role | | `PresetBeta` | Secondary Creator | Creator role | | `PresetGamma` | Limited Creator | Creator role | | `PresetDelta` | Visualization Creator | Feature-gated creator role | | `PresetReportsOnly` | Viewer | Viewer role | | `PresetDashboardsOnly` | Dashboard Viewer | Viewer role | | `PresetEpsilon` | Dashboard Interactor | Feature-gated viewer role | | `PresetNoAccess` | No Access | Removes direct workspace access | | `PresetMachineRole` | Machine | Machine-only; do not assign to normal human users | Creator roles can consume creator seats on Enterprise teams. Before invite creation or role upgrades to a creator role, preflight seat limits in [team-memberships.md](team-memberships.md). ## Team Role IDs Invite and team membership APIs require numeric `team_role_id` values. `GET /team-roles/` exists in Manager but is not marked `user_api_key_allowed`, so do not assume an API-key JWT can call it. Use one of these sources instead: - the team membership response for an existing member with the intended role - an approved admin-provided value from a ticket, environment configuration, or runbook - a browser/session-backed admin context outside these API-key examples If no existing member has the intended role and no approved numeric ID is available, stop and ask a team admin for the numeric `team_role_id`. Do not guess from role names or attempt to discover team role IDs with the API-key JWT. Always state the numeric `team_role_id`, intended role name, target team, and target user before making a team role mutation. ## Custom Workspace Roles Custom workspace roles are valid only on endpoints that accept the team's dynamic workspace role list. Do not invent custom role identifiers. If the requested custom role is not present in the team payload, stop and ask the user for the correct role or admin context. `PUT /teams/{team_name}/workspaces/{workspace_id}/membership` currently validates default workspace role identifiers, so do not use custom workspace role identifiers for direct workspace member role updates unless Manager's request schema changes. -
team-member-access-changes.md 1.3 KB
# Team Member Access Changes Use this reference for approval-gated team role updates and member removals. ## Mutating Endpoints | Goal | Method and path | |---|---| | Update team role | `PATCH /teams/{team_name}/memberships/{user_id}/` with `{"team_role_id": <id>}` | | Remove team member | `DELETE /teams/{team_name}/memberships/{user_id}/` | Role updates require a numeric `team_role_id`; resolve it with [role-identifiers.md](role-identifiers.md). Use [team-member-lookup.md](team-member-lookup.md) first to identify the target `user.id`, email, current role, and inherited-role status. ## Required Confirmation Before a team role update, summarize: - team name - user ID and email - `current_role`: current team role ID and role name - `new_role`: new numeric `team_role_id` and role name - whether the current role came from a group - expected access effect Before member removal, summarize the team name, user ID, email, current team role, and expected access removal. Wait for explicit confirmation before `PATCH` or `DELETE`. Manager rejects changes that would remove the last team admin, and rejects removing yourself from a team. Group role assignment endpoints exist, but group and SCIM provisioning are intentionally deferred from this skill. See [deferrals.md](deferrals.md). -
team-member-lookup.md 1.6 KB
# Team Member Lookup Use this reference for read-only team membership listing, filtering, member lookup, and seat checks. ## API-Key-Safe Reads | Goal | Method and path | |---|---| | List or filter team members | `GET /teams/{team_name}/memberships/?page_number=1&page_size=100` | | Get one member | `GET /teams/{team_name}/memberships/{user_id}/` | | Check seats remaining | `GET /teams/{team_name}/has-seats-remaining/` | Use pagination and filter by `user_name_or_email` or `user_type` when the request targets a specific user or creator/viewer class. The response includes `meta.count` when paginated. Common response fields: | Field | Description | |---|---| | `user.id` | Numeric user ID for role updates or removal | | `user.email` | Member email address | | `team_role.id` | Numeric team role ID | | `team_role.name` | Team role display name | | `is_role_from_group` | Whether the team role comes from a group | | `user_type` | Enterprise creator/viewer classification when present | | `creator_on_workspaces` | Workspaces where the user has creator access | | `viewer_on_workspaces` | Workspaces where the user has viewer access | Use the numeric `user.id` from list results for single-member lookup, role update, or removal. Before creating invites or upgrading a viewer to a creator role, call the API-key-safe seat check. Enterprise teams split viewer and creator capacity; if no seats remain, treat that as a blocker. `GET /teams/{team_name}/user-limit/` and `GET /teams/{team_name}/memberships/{user_id}/groups/` are not API-key allowed in live validation. Use browser/session-backed admin context or defer group analysis outside this API-key workflow. -
team-memberships.md 1.1 KB
# Team Memberships Reference Team membership endpoints require team-admin permissions. API-key JWTs are accepted on documented membership endpoints, but requests return `403` if the key owner lacks the required team permissions. Use this file to route to the narrow membership reference: - Read/list members, identify `user.id`, check seats, or inspect response fields: [team-member-lookup.md](team-member-lookup.md) - Change a team role or remove a member: [team-member-access-changes.md](team-member-access-changes.md) - Resolve numeric role IDs before access changes: [role-identifiers.md](role-identifiers.md) - Check unsupported group/SCIM/session-only adjacent routes: [deferrals.md](deferrals.md) Changing a team role or removing a user changes access. Load `preset-api` and then `references/safety-policy.md`; get explicit confirmation before making `PATCH` or `DELETE` requests. Lookup anchors: `user_name_or_email`, `/teams/{team_name}/has-seats-remaining/`. Reusable Python snippets live in `examples/team_memberships.py`; load that file only when implementation detail is needed. -
workspace-management.md 7.8 KB
# Workspace Management Reference Use `preset-workspaces` for read-only discovery and hostname resolution. Use this reference when the task involves workspace lifecycle management or membership edge cases. Workspace create, update, delete, un-hibernate, and membership role changes are sensitive administration workflows. Load the safety policy and get explicit confirmation before mutating. Existing-member role changes are covered in [Update Workspace Member Role](#update-workspace-member-role). ## Create A Workspace Confirmation summary should include the target `team_name`, workspace title, region or cluster selection, whether example data will be loaded, and any public dashboard or embedding-related setting. ```bash curl -s -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ "https://api.app.preset.io/v1/teams/{team_name}/workspaces/" \ -d '{"title":"Analytics","region":"us-east-1","load_examples":true}' ``` ```python workspace = client.mgmt( "POST", f"/teams/{team_name}/workspaces/", json={ "title": "Analytics", "region": "us-east-1", "load_examples": True, }, )["payload"] ``` Common request fields: | Field | Description | |---|---| | `title` | Required workspace title | | `region` | Region for workspace placement | | `cluster_id` | Explicit cluster ID, when approved | | `load_examples` | Whether to load examples; defaults true in Manager | | `icon`, `color`, `descr` | Workspace display metadata | | `allow_public_dashboards` | Public dashboard setting | ## Update A Workspace `PUT` updates the workspace resource, but Manager does not treat every omitted field as preserved. Some omitted optional fields are passed to the manager as `None` or `False`, which can clear display metadata or disable boolean settings. Always read the current workspace first and send the full desired state for fields managed by this endpoint. Confirmation summary should include the target `team_name`, workspace ID, current title, current hostname, every field being preserved, every field being changed, and the expected effect. ```bash CURRENT_WORKSPACE="$(curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v1/teams/{team_name}/workspaces/{workspace_id}/")" CURRENT_WORKSPACE="$CURRENT_WORKSPACE" jq -n '{ title: "Analytics", descr: (env.CURRENT_WORKSPACE | fromjson | .payload.descr), color: (env.CURRENT_WORKSPACE | fromjson | .payload.color), icon: (env.CURRENT_WORKSPACE | fromjson | .payload.icon), allow_public_dashboards: (env.CURRENT_WORKSPACE | fromjson | .payload.allow_public_dashboards) }' \ | curl -s -X PUT \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ "https://api.app.preset.io/v1/teams/{team_name}/workspaces/{workspace_id}/" \ -d @- ``` ```python current = client.mgmt( "GET", f"/teams/{team_name}/workspaces/{workspace_id}/", )["payload"] payload = { "title": "Analytics", "descr": current.get("descr"), "color": current.get("color"), "icon": current.get("icon"), "allow_public_dashboards": current.get("allow_public_dashboards", False), } updated = client.mgmt( "PUT", f"/teams/{team_name}/workspaces/{workspace_id}/", json=payload, )["payload"] ``` Do not include secrets such as `slack_token` unless the user explicitly asks and provides an approved secret-handling path. Treat AI Assist, embedding, MCP, and Copilot settings as separate sensitive configuration changes, not incidental workspace metadata updates. ## Delete A Workspace Deleting a workspace is destructive. Confirmation must name the team, workspace ID, workspace title, hostname, and expected deletion effect. ```bash curl -s -X DELETE \ -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v1/teams/{team_name}/workspaces/{workspace_id}/" ``` ```python client.mgmt("DELETE", f"/teams/{team_name}/workspaces/{workspace_id}/") ``` ## Un-Hibernate A Workspace Un-hibernating may resume compute and incur runtime cost. Confirm the target `team_name`, workspace title, stable workspace slug, hostname, and expected cost or availability effect before calling this endpoint. ```bash curl -s -X PATCH \ -H "Authorization: Bearer $TOKEN" \ "https://api.app.preset.io/v1/teams/{team_name}/workspaces/{workspace_slug}/un-hibernate/" ``` ```python workspace = client.mgmt( "PATCH", f"/teams/{team_name}/workspaces/{workspace_slug}/un-hibernate/", )["payload"] ``` Use the stable workspace slug from the workspace `name` field, not the display title. ## Workspace Membership Edge Cases The read-only workspace membership list lives in `preset-workspaces`. These details are important for admin work: - `GET /teams/{team_name}/workspaces/{workspace_id_or_name}/memberships/` accepts either a numeric workspace ID or `name-<workspace_name>`. - Use `user_name_or_email` to search members. - The response is paginated and returns `meta.count`. - `is_role_from_group` means the visible role is inherited from a group. Updating a direct user role may not change effective access. ```python members = client.mgmt( "GET", f"/teams/{team_name}/workspaces/name-{workspace_name}/memberships/" "?page_number=1&page_size=100" "&user_name_or_email=jdoe@example.com", )["payload"] ``` ## Update Workspace Member Role `PUT /teams/{team_name}/workspaces/{workspace_id}/membership` updates direct workspace access for an existing team member. This endpoint accepts a numeric workspace ID, not `name-<workspace_name>`, and currently validates default workspace role identifiers only. Before updating a member role: 1. Load [role-identifiers.md](role-identifiers.md) and choose a default `role_identifier`. 2. List the workspace memberships and resolve the target `user_id` from the API response. 3. Check `is_role_from_group`. If it is true, the effective role is inherited from a group and a direct role update may not change access. 4. For creator role upgrades on Enterprise teams, preflight seat availability with `GET /teams/{team_name}/has-seats-remaining/`. 5. Confirm the target team, workspace ID, workspace title, user ID, email, current role, new role, group-derived role status, and seat preflight result. ```python members = client.mgmt( "GET", f"/teams/{team_name}/workspaces/{workspace_id}/memberships/" "?page_number=1&page_size=100" "&user_name_or_email=jdoe@example.com", )["payload"] member = next( m for m in members if m["user"]["email"].lower() == "jdoe@example.com" ) if member.get("is_role_from_group"): raise RuntimeError("Role is inherited from a group; confirm before direct update") seat_check = client.mgmt("GET", f"/teams/{team_name}/has-seats-remaining/")["payload"] if not seat_check.get("has_seats_remaining"): raise RuntimeError("Team has no seats remaining") updated = client.mgmt( "PUT", f"/teams/{team_name}/workspaces/{workspace_id}/membership", json={ "user_id": member["user"]["id"], "role_identifier": "PresetGamma", }, )["payload"] ``` Use `PresetNoAccess` to remove direct workspace access. Do not use custom workspace role identifiers with this endpoint unless Manager's request schema changes to accept them. ## Workspace User Access `GET /teams/{team_name}/workspaces/{workspace_name}/user-access/` returns workspace access information suitable for non-admin displays, but Manager does not mark this route as `user_api_key_allowed`. Do not call it with the API-key JWT client from `preset-api`; use the workspace membership list for API-key examples or a browser/session-backed context outside this skill. ## Defer Adjacent Workspace Admin APIs Do not use this skill for database connection creation/update/test, embedded guest tokens, embedded access-token keys, trusted domains, homepage settings, workspace cloning, hibernation recovery, internal health checks, or internal `/api-internal/*` workspace operations. See [deferrals.md](deferrals.md).
-
-
SKILL.md 2.5 KB
--- name: preset-admin description: Manage Preset teams, workspaces, memberships, invites, role identifiers, seat checks, and audit logs through direct Management API calls. Use only for direct API workflows; Do not use for MCP-only work. --- # preset-admin Use for Preset Management API administration beyond read-only workspace discovery. ## Always - Auth and conventions come from `preset-api` (JWT exchange, base URLs, Rison); resolve the workspace hostname through the Management API when it is not already known. - Default to read-only preflight and ID/role lookup. - Resolve team, workspace, user, invite, and role identifiers from API responses before mutations. - Get explicit confirmation before role changes, invites, member removals, workspace lifecycle actions, audit downloads, or any write. - Use `preset-roles-permissions` for permission-sensitive role/access changes. ## Decision Rules - Classify membership role and invite changes as approval-gated mutations. - Use metadata reads for membership, role, invite, and audit inspection. - Require target and effect summary before any access change. - Avoid applying invite or member role changes until approval is explicit. ## Workflow Order 1. Identify team, workspace, user, invite, member, and role identifiers. 2. Inspect membership, role, invite, and audit metadata. 3. Prepare approval summary with target and expected effect. 4. Stop before invite, member role, removal, or workspace change. ## Retrieve - Team membership routing: [references/team-memberships.md](references/team-memberships.md) - Team member lookup, seat checks, response fields: [references/team-member-lookup.md](references/team-member-lookup.md) - Team role changes and removals: [references/team-member-access-changes.md](references/team-member-access-changes.md) - Workspace create/update/delete/un-hibernate: [references/workspace-management.md](references/workspace-management.md) - Invites, cancellation, resend, bulk invite: [references/invites.md](references/invites.md) - Audit log lookup and downloads: [references/audit-logs.md](references/audit-logs.md) - Role identifiers: [references/role-identifiers.md](references/role-identifiers.md) - Unsupported or adjacent Manager endpoints: [references/deferrals.md](references/deferrals.md) - Approval policy before writes/downloads: load `preset-api` and then `references/safety-policy.md`. ## Do Not - Do not use internal `/api-internal/*`, billing/payment, SCIM, API key CRUD, permission/DAR/RLS, or database connection mutation routes from this skill.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.