---
title: "Enable an authored skill in this workspace"
method: POST
path: "/v1/authored-skills/{skill_id}/install"
tags: ["authored-skills"]
---

# Enable an authored skill in this workspace

`POST /v1/authored-skills/{skill_id}/install`

## Request

- Base URL: `https://api.patricia.app` (the document declares no server; this is the origin it was published from)
- URL: `https://api.patricia.app/v1/authored-skills/{skill_id}/install`
- Auth: HTTP bearer

Enable the tenant skill's active (approved) version. An INSTRUCTION skill is enabled immediately (its guidance reaches Patricia through the prompt, so there is nothing to schedule); the response lists any recommended integrations the workspace hasn't connected yet, to nudge the teammate. A SANS (scheduled) skill is born PAUSED + a default config, exactly like a catalog skill: the admin completes settings + runs activation review before delivery.

The optional body chooses how widely to enable it. Omit it (or send `team`) for the whole workspace, exactly as before, when the caller holds skills:install. Without skills:install, an absent body enables it for yourself alone instead; an EXPLICIT `team` is refused (403) rather than silently narrowed. Send `personal` to enable it for yourself alone: only your conversations carry the skill's guidance. Personal needs an INSTRUCTION skill (400 otherwise) and a caller whose membership links to a chat profile (400 otherwise, so an API key can never own one). Either way the workspace holds ONE install of a skill, so a second Enable is a 409.

## Path parameters

- `skill_id` string, uuid, required

## Headers

- `X-Tenant-Id` string, nullable

## Request body

- InstallAuthoredSkillRequest: How widely to enable an authored skill. Optional: stating nothing takes the widest scope you may create, which is TEAM as it always has been for a caller who may share with the team. ``personal`` enables it for the caller alone: their prompts carry the skill's guidance and nobody else's do. Only an ``instructions`` skill can be born personal today (it is the one authored kind whose runtime read filters by owner), and only a caller their workspace membership links to a chat profile can own one. ``visibility`` is unset rather than TEAM by default so that "I did not say" is ONE state: an absent body and an empty one are the same preference gap, and the server fills both the same way. Only a stated ``team`` is read as an explicit ask, which is what gets refused without ``skills:install``.
  - `visibility` 'team' | 'personal': Who, within a tenant, an installed skill is visible to (``skill_installation.visibility_code``). The skill-side counterpart of ``IntegrationVisibility``: ``TEAM`` is any actor in the tenant, ``PERSONAL`` is only the actor who installed it (``owner_actor_id``). A TEAM install is safe to surface in the shared, per-tenant prompt cache; a PERSONAL install is surfaced only through the per-run, actor-scoped progressive-disclosure loader (never the shared prompt, which would leak it across actors).

## Response `201`

Successful Response

- AuthoredInstallResponse: An authored-skill install result + the recommended integrations the workspace still needs to connect. ``recommended_to_connect`` is the subset of the skill's recommended app slugs the tenant has NOT connected yet, and the dashboard shows a friendly nudge (with the existing connect flow) for each.
  - `id` string, uuid, required
  - `skill_id` string, uuid, required
  - `manifest_version_id` string, uuid, required
  - `status_code` string, required
  - `config_version` integer, required
  - `projection_generation` integer, required
  - `activated_at` string, date-time, nullable, required
  - `first_live_at` string, date-time, nullable, required
  - `killed_at` string, date-time, nullable, required
  - `kill_reason` string, nullable, required
  - `title` string, nullable
  - `slug` string, nullable
  - `description` string, nullable
  - `listing_state` 'unlisted' | 'testing' | 'listed': Who can discover and install a global skill.
  - `is_portable` boolean, nullable
  - `archetype_code` string, nullable
  - `can_edit_definition` boolean, nullable
  - `removable` boolean: Whether this installation may be removed from the workspace.
  - `visibility_code` 'team' | 'personal', required: Who, within a tenant, an installed skill is visible to (``skill_installation.visibility_code``). The skill-side counterpart of ``IntegrationVisibility``: ``TEAM`` is any actor in the tenant, ``PERSONAL`` is only the actor who installed it (``owner_actor_id``). A TEAM install is safe to surface in the shared, per-tenant prompt cache; a PERSONAL install is surfaced only through the per-run, actor-scoped progressive-disclosure loader (never the shared prompt, which would leak it across actors).
  - `is_mine` boolean, nullable
  - `can_change_visibility` boolean
  - `has_settings` boolean, nullable
  - `is_catalog_default` boolean
  - `effectively_enabled` boolean
  - `update_available` boolean
  - `available_version_id` string, uuid, nullable
  - `outdated_config` boolean
  - `outdated_config_reasons` string[]
  - `about` SkillAboutSummary: What a skill IS, projected from its pinned manifest: the Hub's plain-language overview. The Hub renders a skill's feature from its compiled ``feature_surface`` rows, but a skill whose manifest declares no surfaces (every ``skill``/``instructions`` archetype today) compiles none, so that path alone leaves the page blank. The manifest still describes the skill perfectly well (what it is for, what to say to reach it, what it wants connected), and this projects exactly that, generically: no per-skill branching, one shape for every archetype. Display labels only, never a personal fact (G4). Every field is optional-or-empty so a manifest that omits one simply renders one section fewer.
    - `category` string, nullable
    - `taxonomy` string, nullable
    - `example_phrases` string[]
    - `requires_connected` string[]
    - `works_with` string[]
    - `needs_skills` string[]
    - `credits_per_run` integer, nullable
    - `instructions_body` string, nullable
    - `has_settings` boolean
  - `recommended_to_connect` string[]

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden. Inspect response body `code` for the specific reason (for example: PERMISSION_DENIED, EMAIL_NOT_VERIFIED, TENANT_ACCESS_REQUIRED, ROLE_UNRESOLVED)
- `409`: Conflict
- `422`: Validation error

---

[API](https://skmtc.dev/patricia/apis/patricia-api.md) · [All operations](https://skmtc.dev/patricia/apis/patricia-api/llms.txt) · [OpenAPI document](https://skmtc.dev/patricia/apis/patricia-api/revisions/0dfb44495ea1?raw)
