from line 34
3434| Resource | Endpoints |
3535| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3636| **Plugins:** list every plugin in the organization, upload a new one, look one up, choose the version members are served (roll back or promote), delete one | `GET /v1/organizations/plugins`<br />`POST /v1/organizations/plugins`<br />`GET /v1/organizations/plugins/{plugin_id}`<br />`POST /v1/organizations/plugins/{plugin_id}`<br />`DELETE /v1/organizations/plugins/{plugin_id}` |
37| **Plugin versions:** list a plugin's version history, upload a new version, look one up, download a version's files | `GET /v1/organizations/plugins/{plugin_id}/versions`<br />`POST /v1/organizations/plugins/{plugin_id}/versions`<br />`GET /v1/organizations/plugins/{plugin_id}/versions/{version}`<br />`GET /v1/organizations/plugins/{plugin_id}/versions/{version}/content` |
37| **Plugin versions:** list a plugin’s version history, upload a new version, look one up, download a version’s files | `GET /v1/organizations/plugins/{plugin_id}/versions`<br />`POST /v1/organizations/plugins/{plugin_id}/versions`<br />`GET /v1/organizations/plugins/{plugin_id}/versions/{version}`<br />`GET /v1/organizations/plugins/{plugin_id}/versions/{version}/content` |
3838| **Installation settings:** read who can use an organization-owned plugin, set it for the whole organization or for one group, remove either setting | `GET /v1/organizations/plugins/{plugin_id}/installation_settings`<br />`POST /v1/organizations/plugins/{plugin_id}/installation_settings/{target}`<br />`DELETE /v1/organizations/plugins/{plugin_id}/installation_settings/{target}` |
3939| **Shares:** read who a member has shared their own plugin with (read-only) | `GET /v1/organizations/plugins/{plugin_id}/shares` |
40| **Plugin marketplaces:** find a marketplace's ID, look one up, set the default installation setting for its plugins, check marketplace content before connecting it | `GET /v1/organizations/plugin_marketplaces`<br />`GET /v1/organizations/plugin_marketplaces/{marketplace_id}`<br />`POST /v1/organizations/plugin_marketplaces/{marketplace_id}`<br />`POST /v1/organizations/plugin_marketplaces/validate_repository`<br />`POST /v1/organizations/plugin_marketplaces/validate_archive` |
40| **Plugin marketplaces:** find a marketplace’s ID, look one up, set the default installation setting for its plugins, check marketplace content before connecting it | `GET /v1/organizations/plugin_marketplaces`<br />`GET /v1/organizations/plugin_marketplaces/{marketplace_id}`<br />`POST /v1/organizations/plugin_marketplaces/{marketplace_id}`<br />`POST /v1/organizations/plugin_marketplaces/validate_repository`<br />`POST /v1/organizations/plugin_marketplaces/validate_archive` |
4141
4242This release does not include standalone skills (skills a member writes in the skills editor or uploads as a single skill in claude.ai). They do not appear in the inventory and cannot be created here. Plugins that Anthropic publishes are not inventoried either; their usage is reported by the [Analytics APIs](https://platform.claude.com/docs/en/manage-claude/analytics-api). Marketplaces are created, connected to repositories, and deleted in claude.ai, not through this API.
4343
from line 53
5353
5454## Quick start
5555
56List the plugins in your organization's own marketplaces, newest first:
56List the plugins in your organization’s own marketplaces, newest first:
5757
5858<CodeGroup>
5959 ```bash cURL
from line 211
211211| Scope | Grants |
212212| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
213213| `read:plugins` | Every `GET` endpoint on this page, including archive downloads, plus marketplace validation. |
214| `write:plugins` | Every `POST` and `DELETE` endpoint on this page: create a plugin, create a version, change the served version, delete a plugin, set and remove installation settings, and set a marketplace's default, plus marketplace validation. It does not grant reads. |
214| `write:plugins` | Every `POST` and `DELETE` endpoint on this page: create a plugin, create a version, change the served version, delete a plugin, set and remove installation settings, and set a marketplace’s default, plus marketplace validation. It does not grant reads. |
215215| `read:org_audit` | A read-only scope for security-audit integrations: every `GET` endpoint on this page, including archive downloads, plus the [user management](https://platform.claude.com/docs/en/manage-claude/user-management) and [Compliance API](https://platform.claude.com/docs/en/manage-claude/compliance-api) read endpoints. It does not grant marketplace validation or any write. |
216| `read:compliance_org_data` | The Compliance API's scope for organization metadata (names, types, roles, and groups) and effective settings. Grants every `GET` endpoint on this page, exactly as `read:org_audit` does, so a Compliance Access Key can read plugins without a second key. It does not grant marketplace validation or any write. |
216| `read:compliance_org_data` | The Compliance API’s scope for organization metadata (names, types, roles, and groups) and effective settings. Grants every `GET` endpoint on this page, exactly as `read:org_audit` does, so a Compliance Access Key can read plugins without a second key. It does not grant marketplace validation or any write. |
217217
218218A key can carry several scopes. An integration that uploads a plugin and then reads it back needs both `read:plugins` and `write:plugins`. Wherever this page says an endpoint requires the `read:plugins` scope, a key with `read:org_audit` or `read:compliance_org_data` works too.
219219
220### Access to members' plugin files
220### Access to members’ plugin files
221221
222Each of these read scopes (`read:plugins`, `read:org_audit`, and `read:compliance_org_data`) can download the files of plugins in members' personal marketplaces, including files that claude.ai's admin settings do not show, and a `read:org_audit` or `read:compliance_org_data` key bound to your parent organization can do this in any organization under it that has access to this API, by passing `organization_id` (see [Reading another organization under the same parent](https://platform.claude.com/docs/en/manage-claude/plugins-api#reading-another-organization-under-the-same-parent)). Each such download records a `claude_plugin_archive_accessed` event on the [Compliance API Activity Feed](https://platform.claude.com/docs/en/manage-claude/compliance-activity-feed), identifying the key, the plugin, the version, and the member (see [Activity Feed events](https://platform.claude.com/docs/en/manage-claude/plugins-api#activity-feed-events)). Downloads of organization-owned plugins are not recorded.
222Each of these read scopes (`read:plugins`, `read:org_audit`, and `read:compliance_org_data`) can download the files of plugins in members’ personal marketplaces, including files that claude.ai’s admin settings do not show, and a `read:org_audit` or `read:compliance_org_data` key bound to your parent organization can do this in any organization under it that has access to this API, by passing `organization_id` (see [Reading another organization under the same parent](https://platform.claude.com/docs/en/manage-claude/plugins-api#reading-another-organization-under-the-same-parent)). Each such download records a `claude_plugin_archive_accessed` event on the [Compliance API Activity Feed](https://platform.claude.com/docs/en/manage-claude/compliance-activity-feed), identifying the key, the plugin, the version, and the member (see [Activity Feed events](https://platform.claude.com/docs/en/manage-claude/plugins-api#activity-feed-events)). Downloads of organization-owned plugins are not recorded.
223223
224224### Reading another organization under the same parent
225225
226`read:plugins` and `write:plugins` keys read and write only the organization they were created in. If your company has several Claude organizations linked under one parent organization, the parent's primary owner can create a `read:org_audit` or `read:compliance_org_data` key for all linked organizations (see [Create a key for a Claude Enterprise organization](https://platform.claude.com/docs/en/manage-claude/admin-api-keys#create-a-key-for-a-claude-enterprise-organization)). That key can also read any linked organization that has access to this API. To read one, pass its ID in the `organization_id` query parameter on any `GET` endpoint on this page. The ID is the organization UUID shown in claude.ai's settings (its `org_`-prefixed form is accepted too). Without the parameter, the key reads the organization it was created in. A `404` means the named organization is not under the key's parent or the API is not available to it; a value that is not a UUID or `org_` ID returns `400`. Any other key that names an organization other than its own gets `404`. Writes do not accept `organization_id`.
226`read:plugins` and `write:plugins` keys read and write only the organization they were created in. If your company has several Claude organizations linked under one parent organization, the parent’s primary owner can create a `read:org_audit` or `read:compliance_org_data` key for all linked organizations (see [Create a key for a Claude Enterprise organization](https://platform.claude.com/docs/en/manage-claude/admin-api-keys#create-a-key-for-a-claude-enterprise-organization)). That key can also read any linked organization that has access to this API. To read one, pass its ID in the `organization_id` query parameter on any `GET` endpoint on this page. The ID is the organization UUID shown in claude.ai’s settings (its `org_`-prefixed form is accepted too). Without the parameter, the key reads the organization it was created in. A `404` means the named organization is not under the key’s parent or the API is not available to it; a value that is not a UUID or `org_` ID returns `400`. Any other key that names an organization other than its own gets `404`. Writes do not accept `organization_id`.
227227
228228## Key concepts
229229
230230### Plugins and components
231231
232A **plugin** is a package that extends Claude for your organization's members. It contains any combination of these components:
232A **plugin** is a package that extends Claude for your organization’s members. It contains any combination of these components:
233233
234234| Component | What it is |
235235| ---------- | ------------------------------------------------------------------------------------------------------- |
236236| Skill | Instructions and files that Claude loads when a task calls for it. |
237| Command | A saved prompt a member runs by typing `/` followed by the command's name. |
237| Command | A saved prompt a member runs by typing `/` followed by the command’s name. |
238238| Agent | A helper assistant with its own instructions, to which Claude can hand part of a task. |
239239| Hook | A command that runs automatically when an event occurs in a session, such as before Claude uses a tool. |
240240| MCP server | A connection from Claude to tools and data in another system (Model Context Protocol). |
241241| CLI | A command-line program that the plugin lets Claude run. |
242242
243Every plugin has a manifest at `.claude-plugin/plugin.json`. The manifest's `name` becomes the plugin's `name`: a lowercase identifier that is unique within its marketplace.
243Every plugin has a manifest at `.claude-plugin/plugin.json`. The manifest’s `name` becomes the plugin’s `name`: a lowercase identifier that is unique within its marketplace.
244244
245245### Marketplaces
246246
from line 249
249249* **Owner.** The organization owns its marketplaces. Each member can also have personal marketplaces.
250250* **Source.** `manual` means plugins are uploaded, in claude.ai or, for an organization marketplace, through this API. `github`, `gitlab`, and `public_git` mean plugins are synchronized from a Git repository the owner connected. Nothing can be uploaded to a synchronized marketplace, and this API cannot delete its plugins, because the next synchronization would undo either change. Change the repository instead.
251251
252Your organization's **library marketplace** is the organization-owned `manual` marketplace that uploads go to when you do not name a marketplace. It is created the first time something is uploaded to it.
252Your organization’s **library marketplace** is the organization-owned `manual` marketplace that uploads go to when you do not name a marketplace. It is created the first time something is uploaded to it.
253253
254254### Organization-owned and member-owned plugins
255255
256A plugin's `owner.type` says whose marketplace it lives in:
256A plugin’s `owner.type` says whose marketplace it lives in:
257257
258258* `organization`: you can manage it through this API, except that a plugin in a marketplace synchronized from Git cannot receive uploads or be deleted here.
259* `user`: it lives in one member's personal marketplace. You can read its details and download its files, and delete it if its marketplace is `manual`. Uploading versions and choosing the served version return `403`. Sharing is managed only by the member, in claude.ai.
259* `user`: it lives in one member’s personal marketplace. You can read its details and download its files, and delete it if its marketplace is `manual`. Uploading versions and choosing the served version return `403`. Sharing is managed only by the member, in claude.ai.
260260
261Removing a member from the organization does not remove their plugins. They stay in the inventory under the member's `user_id`, and the `owner_user_id` filter still finds them, so you can review and remove a departed member's content. They are removed from the inventory when the member's account is deleted.
261Removing a member from the organization does not remove their plugins. They stay in the inventory under the member’s `user_id`, and the `owner_user_id` filter still finds them, so you can review and remove a departed member’s content. They are removed from the inventory when the member’s account is deleted.
262262
263263### Versions and the served version
264264
from line 267
267267* `latest_version_id`: the newest version
268268* `served_version_id`: the version members are served
269269
270By default `served_version_pinned` is `false`: the served version normally follows the newest one. When content scanning is on, a version saved in claude.ai might **wait** for its content scan: it is stored but not served at once. This page uses "wait" only for a version saved in claude.ai, not for one that a member or an administrator publishes into a plugin. While scanning is on, a waiting version is served only when its `content_scan` reads `completed` with `pass` or `warn`. While a version waits, `served_version_id` names an earlier version than `latest_version_id`. Do not poll until the two IDs match: they might stay different, for example when the scan fails.
270By default `served_version_pinned` is `false`: the served version normally follows the newest one. When content scanning is on, a version saved in claude.ai might **wait** for its content scan: it is stored but not served at once. This page uses “wait” only for a version saved in claude.ai, not for one that a member or an administrator publishes into a plugin. While scanning is on, a waiting version is served only when its `content_scan` reads `completed` with `pass` or `warn`. While a version waits, `served_version_id` names an earlier version than `latest_version_id`. Do not poll until the two IDs match: they might stay different, for example when the scan fails.
271271
272Choosing a version with `POST /v1/organizations/plugins/{plugin_id}` **pins** the plugin (`served_version_pinned: true`). So does an administrator choosing a version in claude.ai, or accepting a member's request to publish into the plugin. From then on, new uploads are stored and advance `latest_version_id`, but members keep the pinned version until you point `served_version_id` at another one. A plugin whose two pointers differ has a stored version that is not being served.
272Choosing a version with `POST /v1/organizations/plugins/{plugin_id}` **pins** the plugin (`served_version_pinned: true`). So does an administrator choosing a version in claude.ai, or accepting a member’s request to publish into the plugin. From then on, new uploads are stored and advance `latest_version_id`, but members keep the pinned version until you point `served_version_id` at another one. A plugin whose two pointers differ has a stored version that is not being served.
273273
274This lets a release pipeline upload each build, test it, and then promote it. To have your pipeline determine when each build is served, pin the plugin once by setting `served_version_id` to its current version; from then on, promote each build you want served. With content scanning on, that first pin returns `409 scan_pending` while the current version's `content_scan` reads `processing`, and `400 scan_failed` if the scan reads `fail`, `unknown`, or `errored` (`warn` is accepted). A pinned plugin cannot be unpinned, here or in claude.ai.
274This lets a release pipeline upload each build, test it, and then promote it. To have your pipeline determine when each build is served, pin the plugin once by setting `served_version_id` to its current version; from then on, promote each build you want served. With content scanning on, that first pin returns `409 scan_pending` while the current version’s `content_scan` reads `processing`, and `400 scan_failed` if the scan reads `fail`, `unknown`, or `errored` (`warn` is accepted). A pinned plugin cannot be unpinned, here or in claude.ai.
275275
276276To roll back, set `served_version_id` to an earlier version. Roll forward the same way.
277277
278These rules describe organization-owned plugins. A member-owned plugin's served version is controlled by its owner in claude.ai.
278These rules describe organization-owned plugins. A member-owned plugin’s served version is controlled by its owner in claude.ai.
279279
280280### Installation settings
281281
from line 290
290290
291291A plugin can hold an organization-wide setting and one setting for each group (the role-based access control [groups managed in User management](https://platform.claude.com/docs/en/manage-claude/user-management#groups)). A member gets a value by these rules:
292292
2931. The organization-wide value is the plugin's own organization-wide setting if it has one, otherwise its marketplace's default, otherwise `not_available`. The plugin reports this value in `organization_installation_preference`, with `organization_installation_preference_inherited: true` while it comes from the marketplace default.
2931. The organization-wide value is the plugin’s own organization-wide setting if it has one, otherwise its marketplace’s default, otherwise `not_available`. The plugin reports this value in `organization_installation_preference`, with `organization_installation_preference_inherited: true` while it comes from the marketplace default.
2942942. A member who belongs to no group holding a setting for the plugin gets the organization-wide value.
2953. A member who belongs to one or more groups holding a setting gets the most permissive of those groups' settings instead, ranked `required`, `auto_install`, `available`, `not_available`.
2953. A member who belongs to one or more groups holding a setting gets the most permissive of those groups’ settings instead, ranked `required`, `auto_install`, `available`, `not_available`.
296296
297A group's setting replaces the organization-wide value for its members; it does not add to it. For example, if the organization-wide value is `required` and the Pilot group holds `available`, Pilot members get `available`. When you move a plugin from a pilot group to the whole organization, set the organization-wide value and then remove the group's setting. Setting the organization-wide value stops the plugin from inheriting its marketplace default; [removing the organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting) returns the plugin to that default.
297A group’s setting replaces the organization-wide value for its members; it does not add to it. For example, if the organization-wide value is `required` and the Pilot group holds `available`, Pilot members get `available`. When you move a plugin from a pilot group to the whole organization, set the organization-wide value and then remove the group’s setting. Setting the organization-wide value stops the plugin from inheriting its marketplace default; [removing the organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting) returns the plugin to that default.
298298
299A plugin created through this API starts with no settings of its own, so it inherits its marketplace's default: `not_available` unless someone has set a default. Deleting a group removes its settings from every plugin.
299A plugin created through this API starts with no settings of its own, so it inherits its marketplace’s default: `not_available` unless someone has set a default. Deleting a group removes its settings from every plugin.
300300
301301### Shares
302302
from line 308
308308
309309Content scanning is an organization setting in claude.ai. When it is on, newly stored versions are scanned (claude.ai exempts a few) and the result is reported in `content_scan`; a version that was not scanned, for example one stored before scanning was turned on, has `content_scan: null`. Scanning is not offered to organizations that use customer-managed encryption keys or zero data retention.
310310
311While scanning is on, members are served a plugin only when its served version's scan is `completed` with `pass` or `warn`. While the scan is `processing` or `errored`, or reads `fail` or `unknown`, the plugin is withheld from members, and an earlier version is not served in its place. A version that was never scanned (`content_scan: null`) is served normally.
311While scanning is on, members are served a plugin only when its served version’s scan is `completed` with `pass` or `warn`. While the scan is `processing` or `errored`, or reads `fail` or `unknown`, the plugin is withheld from members, and an earlier version is not served in its place. A version that was never scanned (`content_scan: null`) is served normally.
312312
313313A version you upload through this API never waits for its content scan. When it becomes the served version, members lose the plugin until its `content_scan` reads `completed` with `pass` or `warn`. If members should keep the current version while a new one is scanned, pin the plugin first (see [Versions and the served version](https://platform.claude.com/docs/en/manage-claude/plugins-api#versions-and-the-served-version)).
314314
315While a version saved in claude.ai waits for its scan, the served version does not change. When your API upload becomes the served version, any wait ends: the waiting version is served only if someone chooses it. To see a waiting version's scan, read the version (see [List a plugin's versions](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-a-plugins-versions)).
315While a version saved in claude.ai waits for its scan, the served version does not change. When your API upload becomes the served version, any wait ends: the waiting version is served only if someone chooses it. To see a waiting version’s scan, read the version (see [List a plugin’s versions](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-a-plugins-versions)).
316316
317After an upload, `content_scan.status` is `processing` and the verdict arrives asynchronously. Read the version to see it; the plugin object shows only its served version's scan. While scanning is on, changing the served version to a version whose `content_scan` reads `processing` returns `409 scan_pending`, and changing it to one whose scan reads `fail`, `unknown`, or `errored` returns `400 scan_failed`.
317After an upload, `content_scan.status` is `processing` and the verdict arrives asynchronously. Read the version to see it; the plugin object shows only its served version’s scan. While scanning is on, changing the served version to a version whose `content_scan` reads `processing` returns `409 scan_pending`, and changing it to one whose scan reads `fail`, `unknown`, or `errored` returns `400 scan_failed`.
318318
319319#### Failed scans allowed in claude.ai
320320
321In claude.ai, organization owners can allow a version whose scan failed. So can members whose [custom role](https://platform.claude.com/docs/en/manage-claude/user-management#custom-roles) can manage **Libraries**. This API can then report that scan as `completed` with `warn` and the failed scan's `reason`. While it does, the rules for `warn` apply: the version can be served to members, and you can make it the served version. Nothing in the response shows that it was allowed, so a `warn` is either a warning or a failed scan that was allowed. Allowing a version whose scan reads `warn` does not change its `content_scan`. When a version stops being allowed, this API reports the verdict of its newest completed scan. A served version whose scan then reads `fail` or `unknown` is withheld from members.
321In claude.ai, organization owners can allow a version whose scan failed. So can members whose [custom role](https://platform.claude.com/docs/en/manage-claude/user-management#custom-roles) can manage **Libraries**. This API can then report that scan as `completed` with `warn` and the failed scan’s `reason`. While it does, the rules for `warn` apply: the version can be served to members, and you can make it the served version. Nothing in the response shows that it was allowed, so a `warn` is either a warning or a failed scan that was allowed. Allowing a version whose scan reads `warn` does not change its `content_scan`. When a version stops being allowed, this API reports the verdict of its newest completed scan. A served version whose scan then reads `fail` or `unknown` is withheld from members.
322322
323323### Reach
324324
325`reach` summarizes, in one value, how far a version reaches on members' machines and beyond:
325`reach` summarizes, in one value, how far a version reaches on members’ machines and beyond:
326326
327327| Value | Meaning |
328328| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
329329| `remote` | Declares an MCP server or a CLI, whatever else it declares. |
330| `privileged` | Declares no MCP server or CLI, but declares a hook, a monitor (a background command that keeps running during a session), an LSP (Language Server Protocol) server, or settings the plugin applies to the member's app, or contains a skill, command, or any other Markdown file whose frontmatter pre-approves tools (`allowed-tools`). These run, or take effect, on the member's own computer. |
330| `privileged` | Declares no MCP server or CLI, but declares a hook, a monitor (a background command that keeps running during a session), an LSP (Language Server Protocol) server, or settings the plugin applies to the member’s app, or contains a skill, command, or any other Markdown file whose frontmatter pre-approves tools (`allowed-tools`). These run, or take effect, on the member’s own computer. |
331331| `contained` | Declares no MCP server, CLI, hook, monitor, LSP server, or app settings, and none of its Markdown files pre-approves tools (for example, a plugin that holds only skills, commands, and agents, none with `allowed-tools`). |
332332
333333`reach` counts everything the version declares, including monitors, LSP servers, and app settings, which `components` does not list, so a version with an empty `components` list can still be `privileged`. It is `null` for a version stored before components were recorded, and for a version whose reach could not be determined because the `SKILL.md` of one of its skills, or one of its command files, could not be read; treat `null` as unclassified.
from line 356
356356Upload every tagged build from CI, and let the pipeline determine when a build is served.
357357
3583581. Find the marketplace to upload into with `GET /v1/organizations/plugin_marketplaces?owner_type=organization`, or omit `marketplace_id` to use the library marketplace.
3592. On the first release, [create the plugin](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-plugin) with `POST /v1/organizations/plugins`. On every later release, record the plugin's `latest_version_id`, then [upload a version](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-version) with `POST /v1/organizations/plugins/{plugin_id}/versions`. If the upload's response is lost, read the plugin and retry only if `latest_version_id` is unchanged (see [Retrying uploads](https://platform.claude.com/docs/en/manage-claude/plugins-api#retrying-uploads)).
3592. On the first release, [create the plugin](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-plugin) with `POST /v1/organizations/plugins`. On every later release, record the plugin’s `latest_version_id`, then [upload a version](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-version) with `POST /v1/organizations/plugins/{plugin_id}/versions`. If the upload’s response is lost, read the plugin and retry only if `latest_version_id` is unchanged (see [Retrying uploads](https://platform.claude.com/docs/en/manage-claude/plugins-api#retrying-uploads)).
3603603. To keep members on the current version while each new build is checked, pin the plugin once by setting `served_version_id` to its current version. From then on each upload is stored without being served, and the pin cannot be undone: every build you want served needs step 5.
3613614. When content scanning is on, poll `GET /v1/organizations/plugins/{plugin_id}/versions/{version}` until `content_scan.status` is no longer `processing`, and promote only when it is `completed` with `pass` or `warn`.
3625. [Promote the build](https://platform.claude.com/docs/en/manage-claude/plugins-api#change-the-served-version) with `POST /v1/organizations/plugins/{plugin_id}` and `{"served_version_id": "<the new version's ID>"}`. To roll back, send the previous version's ID the same way.
3625. [Promote the build](https://platform.claude.com/docs/en/manage-claude/plugins-api#change-the-served-version) with `POST /v1/organizations/plugins/{plugin_id}` and `{"served_version_id": "<the new version's ID>"}`. To roll back, send the previous version’s ID the same way.
363363
364364### Roll a plugin out to a pilot group, then to everyone
365365
3661. Look up the pilot group's ID with `GET /v1/organizations/rbac_groups`. That call needs the `read:rbac_groups` scope, which requires a key created for all linked organizations (see [Groups in User management](https://platform.claude.com/docs/en/manage-claude/user-management#groups)). The next steps need `write:plugins`, which acts only on the organization its key was created in, so in an enterprise with several linked organizations, create this key in the organization that holds the plugin and give it both scopes, or use a second key created there for those steps.
3661. Look up the pilot group’s ID with `GET /v1/organizations/rbac_groups`. That call needs the `read:rbac_groups` scope, which requires a key created for all linked organizations (see [Groups in User management](https://platform.claude.com/docs/en/manage-claude/user-management#groups)). The next steps need `write:plugins`, which acts only on the organization its key was created in, so in an enterprise with several linked organizations, create this key in the organization that holds the plugin and give it both scopes, or use a second key created there for those steps.
367367
3682. [Give the group its own setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#set-an-installation-setting), for example `auto_install`, with `POST /v1/organizations/plugins/{plugin_id}/installation_settings/{target}`, where `{target}` is the group's `rbac_group_` ID, while the organization-wide value stays `not_available`. Only the group's members get the plugin.
3682. [Give the group its own setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#set-an-installation-setting), for example `auto_install`, with `POST /v1/organizations/plugins/{plugin_id}/installation_settings/{target}`, where `{target}` is the group’s `rbac_group_` ID, while the organization-wide value stays `not_available`. Only the group’s members get the plugin.
369369
3703. When the pilot ends, set the organization-wide value with the following call, then remove the group's setting so the group follows the organization again. Setting the organization-wide value stops the plugin from inheriting its marketplace default; [removing the organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting) later returns the plugin to that default.
3703. When the pilot ends, set the organization-wide value with the following call, then remove the group’s setting so the group follows the organization again. Setting the organization-wide value stops the plugin from inheriting its marketplace default; [removing the organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting) later returns the plugin to that default.
371371
372372 <CodeGroup>
373373 ```bash cURL
from line 500
500500 ```
501501 </CodeGroup>
502502
503 Then remove the group's setting with `DELETE /v1/organizations/plugins/{plugin_id}/installation_settings/{target}`, where `{target}` is the group's ID. A group's setting replaces the organization-wide value for its members rather than adding to it, so a leftover group setting of `available` would keep those members on `available`.
503 Then remove the group’s setting with `DELETE /v1/organizations/plugins/{plugin_id}/installation_settings/{target}`, where `{target}` is the group’s ID. A group’s setting replaces the organization-wide value for its members rather than adding to it, so a leftover group setting of `available` would keep those members on `available`.
504504
505505### Keep a security inventory in sync
506506
507Run a nightly job that flags plugins that reach outside the member's session or whose content scan reads `warn`, `fail`, or `unknown`.
507Run a nightly job that flags plugins that reach outside the member’s session or whose content scan reads `warn`, `fail`, or `unknown`.
508508
5091. Page through [`GET /v1/organizations/plugins?limit=100`](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-plugins) until `next_page` is `null` (see [Pagination](https://platform.claude.com/docs/en/manage-claude/plugins-api#pagination)). Read each plugin's `reach` and `content_scan` from that list on every run: a change to `content_scan` alone, such as a verdict that arrives later, does not move `updated_at`. `updated_at` normally tells you which plugins have new content or a new served version since the last run (worth a fresh archive download). A waiting version (see [Versions and the served version](https://platform.claude.com/docs/en/manage-claude/plugins-api#versions-and-the-served-version)) moves `updated_at` when it becomes the served version, not necessarily when it is stored. To notice one, compare `latest_version_id` with the value from your last run. The full re-list is also what catches removals, because a plugin removed by a Git synchronization or an account deletion disappears without an event.
5102. Flag each plugin whose `reach` is `remote` (it declares an MCP server or a CLI), or whose `content_scan.assessment` is `warn`, `fail`, or `unknown`. A `warn` can also be a [failed scan that was allowed in claude.ai](https://platform.claude.com/docs/en/manage-claude/plugins-api#failed-scans-allowed-in-claude-ai). Leave `warn` out only if you accept every failed scan that is allowed there. These fields describe the served version. To also check a newer version that is not served, list the plugin's versions when `latest_version_id` differs from `served_version_id` (see [List a plugin's versions](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-a-plugins-versions)).
5113. For each flagged plugin, download the served version's archive for review with `GET /v1/organizations/plugins/{plugin_id}/versions/{served_version_id}/content` (see [Download a version's files](https://platform.claude.com/docs/en/manage-claude/plugins-api#download-a-versions-files)). Also download any unserved version that meets step 2's test, by its ID.
5091. Page through [`GET /v1/organizations/plugins?limit=100`](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-plugins) until `next_page` is `null` (see [Pagination](https://platform.claude.com/docs/en/manage-claude/plugins-api#pagination)). Read each plugin’s `reach` and `content_scan` from that list on every run: a change to `content_scan` alone, such as a verdict that arrives later, does not move `updated_at`. `updated_at` normally tells you which plugins have new content or a new served version since the last run (worth a fresh archive download). A waiting version (see [Versions and the served version](https://platform.claude.com/docs/en/manage-claude/plugins-api#versions-and-the-served-version)) moves `updated_at` when it becomes the served version, not necessarily when it is stored. To notice one, compare `latest_version_id` with the value from your last run. The full re-list is also what catches removals, because a plugin removed by a Git synchronization or an account deletion disappears without an event.
5102. Flag each plugin whose `reach` is `remote` (it declares an MCP server or a CLI), or whose `content_scan.assessment` is `warn`, `fail`, or `unknown`. A `warn` can also be a [failed scan that was allowed in claude.ai](https://platform.claude.com/docs/en/manage-claude/plugins-api#failed-scans-allowed-in-claude-ai). Leave `warn` out only if you accept every failed scan that is allowed there. These fields describe the served version. To also check a newer version that is not served, list the plugin’s versions when `latest_version_id` differs from `served_version_id` (see [List a plugin’s versions](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-a-plugins-versions)).
5113. For each flagged plugin, download the served version’s archive for review with `GET /v1/organizations/plugins/{plugin_id}/versions/{served_version_id}/content` (see [Download a version’s files](https://platform.claude.com/docs/en/manage-claude/plugins-api#download-a-versions-files)). Also download any unserved version that meets step 2’s test, by its ID.
5125124. To take a plugin away from members while you review it, see [Delete a plugin](https://platform.claude.com/docs/en/manage-claude/plugins-api#delete-a-plugin) for the reversible (organization-owned) and permanent options.
513513
514514## Plugins
515515
516The plugin object describes a plugin in one of your organization's marketplaces or in a member's personal marketplace (the [Quick start](https://platform.claude.com/docs/en/manage-claude/plugins-api#quick-start) response shows a complete one). Its `display_name`, `description`, `manifest_version`, `content_scan`, `components`, and `reach` describe its **served** version, so one list call shows what members are being served.
516The plugin object describes a plugin in one of your organization’s marketplaces or in a member’s personal marketplace (the [Quick start](https://platform.claude.com/docs/en/manage-claude/plugins-api#quick-start) response shows a complete one). Its `display_name`, `description`, `manifest_version`, `content_scan`, `components`, and `reach` describe its **served** version, so one list call shows what members are being served.
517517
518518| Field | Description |
519519| ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
520520| `id` | Prefixed `plugin_`. |
521521| `name` | From the manifest. Unique within its marketplace, not across the organization. Fixed for an organization-owned plugin; changes if a member renames their own plugin in claude.ai. |
522| `display_name`, `description`, `manifest_version` | The served version's manifest `displayName`, `description`, and `version`; each is `null` when the manifest declares none. `manifest_version` is normalized for display: one leading `v` or `V` is dropped, so a manifest `version` of `"v1.4.0"` is returned as `"1.4.0"`. It is also `null` for a value that does not look like a version number, such as `"latest"`, and for a plugin version created before claude.ai started recording this field in August 2026. An upload is never refused because of its `version`, and `manifest_version` is not unique. |
522| `display_name`, `description`, `manifest_version` | The served version’s manifest `displayName`, `description`, and `version`; each is `null` when the manifest declares none. `manifest_version` is normalized for display: one leading `v` or `V` is dropped, so a manifest `version` of `"v1.4.0"` is returned as `"1.4.0"`. It is also `null` for a value that does not look like a version number, such as `"latest"`, and for a plugin version created before claude.ai started recording this field in August 2026. An upload is never refused because of its `version`, and `manifest_version` is not unique. |
523523| `served_version_id`, `latest_version_id` | Prefixed `pluginver_`: the version members are served, and the newest version. See [Versions and the served version](https://platform.claude.com/docs/en/manage-claude/plugins-api#versions-and-the-served-version). |
524524| `served_version_pinned` | `false` until the plugin is pinned (see [Versions and the served version](https://platform.claude.com/docs/en/manage-claude/plugins-api#versions-and-the-served-version)); `true` from then on. |
525| `owner` | `{"type": "organization"}`, or `{"type": "user", "user_id": "user_..."}` for a member's personal marketplace. |
525| `owner` | `{"type": "organization"}`, or `{"type": "user", "user_id": "user_..."}` for a member’s personal marketplace. |
526526| `marketplace_id` | Prefixed `marketplace_`. |
527527| `created_by` | Who created the plugin: `{"type": "user_actor", "user_id": "user_...", "email_address": "..."}` for a person in claude.ai (`email_address` might be `null`), or `{"type": "api_actor", "api_key_id": "apikey_..."}` for an API key. Other actor types may appear. `null` when no creator is recorded, such as for plugins synchronized from Git. |
528| `organization_installation_preference`, `organization_installation_preference_inherited` | Organization-owned: the organization-wide value, and whether it comes from the marketplace's default (see [Installation settings](https://platform.claude.com/docs/en/manage-claude/plugins-api#installation-settings)). Member-owned: both `null`. |
529| `content_scan` | The served version's scan result, an object with `status`, `assessment`, and `reason` (described after this table). `null` when it was never scanned. |
530| `components` | The served version's components, each `{"type", "name", "description"}` with `type` one of `skill`, `mcp_server`, `command`, `agent`, `hook`, or `cli`, listed in that type order and then by name. For an MCP server, `name` is its key in the manifest; for a hook, the event it runs on; for a CLI, the executable's name. `description` is always `null` for MCP servers, hooks, and CLIs. `null` when not recorded. |
528| `organization_installation_preference`, `organization_installation_preference_inherited` | Organization-owned: the organization-wide value, and whether it comes from the marketplace’s default (see [Installation settings](https://platform.claude.com/docs/en/manage-claude/plugins-api#installation-settings)). Member-owned: both `null`. |
529| `content_scan` | The served version’s scan result, an object with `status`, `assessment`, and `reason` (described after this table). `null` when it was never scanned. |
530| `components` | The served version’s components, each `{"type", "name", "description"}` with `type` one of `skill`, `mcp_server`, `command`, `agent`, `hook`, or `cli`, listed in that type order and then by name. For an MCP server, `name` is its key in the manifest; for a hook, the event it runs on; for a CLI, the executable’s name. `description` is always `null` for MCP servers, hooks, and CLIs. `null` when not recorded. |
531531| `reach` | `contained`, `privileged`, or `remote`. See [Reach](https://platform.claude.com/docs/en/manage-claude/plugins-api#reach). |
532532| `updated_at` | Changes when the served version changes, when the plugin becomes pinned, and normally when a new version is stored. A waiting version (see [Versions and the served version](https://platform.claude.com/docs/en/manage-claude/plugins-api#versions-and-the-served-version)) changes it when it becomes the served version, not necessarily when it is stored. It does not change for installation settings, shares, or a change to `content_scan` alone. |
533533
from line 545
545545 | `covert-usage-telemetry` | Tells Claude to send information about the member or their usage to an outside address without telling them. |
546546 | `undisclosed-data-destination` | Sends files, emails, documents, or other content to a fixed outside destination the member is not shown and does not control. |
547547 | `remote-code-instruction-loader` | Tells Claude to download and run, or follow instructions from, outside content that can change after the plugin is installed. |
548 | `credential-exposure` | Contains live credentials, or collects credentials or tokens from the member's environment. |
549 | `guardrail-tampering` | Weakens the member's safeguards, for example by pre-approving every permission prompt. |
550 | `system-prompt-spoofing` | Imitates or attempts to replace Claude's system instructions. |
548 | `credential-exposure` | Contains live credentials, or collects credentials or tokens from the member’s environment. |
549 | `guardrail-tampering` | Weakens the member’s safeguards, for example by pre-approving every permission prompt. |
550 | `system-prompt-spoofing` | Imitates or attempts to replace Claude’s system instructions. |
551551 | `covert-record-tampering` | Quietly changes, hides, or deletes information the member would otherwise see. |
552 | `covert-behavior-override` | Changes Claude's behavior beyond the plugin's purpose and hides the change from the member. |
552 | `covert-behavior-override` | Changes Claude’s behavior beyond the plugin’s purpose and hides the change from the member. |
553553 | `hidden-code-execution` | Runs bundled code while telling Claude not to reveal what it does. |
554 | `undisclosed-promotion-injection` | Inserts undisclosed promotional content into Claude's output. |
554 | `undisclosed-promotion-injection` | Inserts undisclosed promotional content into Claude’s output. |
555555 | `hidden-identity-gate` | Changes or stops its behavior depending on which account runs it, without saying why. |
556 | `destructive-persistence` | Can delete or corrupt the member's files, or install programs that remain after the plugin. |
556 | `destructive-persistence` | Can delete or corrupt the member’s files, or install programs that remain after the plugin. |
557557 | `unanalyzable-binary` | Includes a compiled or unreadable program, so the scan could not verify what it does. |
558558 | `other` | Any other concern, including one newer than this list. |
559559</Accordion>
from line 562
562562
563563### List plugins
564564
565`GET /v1/organizations/plugins` lists every plugin in your organization, in the organization's marketplaces and in members' personal marketplaces, ordered by `created_at` descending. Filter by `owner_type` (`organization` or `user`), `owner_user_id` (an ID prefixed `user_`, which finds a member's plugins, including after the member leaves the organization), `marketplace_id`, and `created_at[gte]`, `created_at[gt]`, `created_at[lte]`, `created_at[lt]` (RFC 3339 timestamps). Filters combine with AND. A `marketplace_id` or `owner_user_id` that matches nothing in your organization returns an empty page, not an error. The response has the shape shown in [Quick start](https://platform.claude.com/docs/en/manage-claude/plugins-api#quick-start). Requires the `read:plugins` scope.
565`GET /v1/organizations/plugins` lists every plugin in your organization, in the organization’s marketplaces and in members’ personal marketplaces, ordered by `created_at` descending. Filter by `owner_type` (`organization` or `user`), `owner_user_id` (an ID prefixed `user_`, which finds a member’s plugins, including after the member leaves the organization), `marketplace_id`, and `created_at[gte]`, `created_at[gt]`, `created_at[lte]`, `created_at[lt]` (RFC 3339 timestamps). Filters combine with AND. A `marketplace_id` or `owner_user_id` that matches nothing in your organization returns an empty page, not an error. The response has the shape shown in [Quick start](https://platform.claude.com/docs/en/manage-claude/plugins-api#quick-start). Requires the `read:plugins` scope.
566566
567567<CodeGroup>
568568 ```bash cURL
from line 679
679679
680680### Create a plugin
681681
682`POST /v1/organizations/plugins` creates an organization-owned plugin and its first version in one call; the version becomes the served version. The body is `multipart/form-data`: `files[]` is either one `.zip` or `.plugin` archive, or one part for each file, where each part's file name is the file's path within the plugin (for example `.claude-plugin/plugin.json`). Optional fields are `marketplace_id` (an organization-owned `manual` marketplace, or by default your library marketplace, which is created on first use) and `release_notes` (up to 5,000 characters, shown in claude.ai's version history and returned on the version). The plugin's `name`, `display_name`, `description`, and `manifest_version` come from the uploaded manifest, and the upload must meet the [upload requirements](https://platform.claude.com/docs/en/manage-claude/plugins-api#upload-requirements). When content scanning is on, the response's `content_scan.status` is `processing` and the verdict arrives asynchronously. Returns the plugin. Requires the `write:plugins` scope.
682`POST /v1/organizations/plugins` creates an organization-owned plugin and its first version in one call; the version becomes the served version. The body is `multipart/form-data`: `files[]` is either one `.zip` or `.plugin` archive, or one part for each file, where each part’s file name is the file’s path within the plugin (for example `.claude-plugin/plugin.json`). Optional fields are `marketplace_id` (an organization-owned `manual` marketplace, or by default your library marketplace, which is created on first use) and `release_notes` (up to 5,000 characters, shown in claude.ai’s version history and returned on the version). The plugin’s `name`, `display_name`, `description`, and `manifest_version` come from the uploaded manifest, and the upload must meet the [upload requirements](https://platform.claude.com/docs/en/manage-claude/plugins-api#upload-requirements). When content scanning is on, the response’s `content_scan.status` is `processing` and the verdict arrives asynchronously. Returns the plugin. Requires the `write:plugins` scope.
683683
684684Upload an archive:
685685
from line 1041
10411041 ```
10421042</CodeGroup>
10431043
1044Besides a `400` for an upload that breaks the [upload requirements](https://platform.claude.com/docs/en/manage-claude/plugins-api#upload-requirements) (`413` for a request body over 200 MB) and the shared responses (a `403` when `marketplace_id` is a member's personal marketplace; see [Error responses](https://platform.claude.com/docs/en/manage-claude/plugins-api#error-responses)), a create can fail with:
1044Besides a `400` for an upload that breaks the [upload requirements](https://platform.claude.com/docs/en/manage-claude/plugins-api#upload-requirements) (`413` for a request body over 200 MB) and the shared responses (a `403` when `marketplace_id` is a member’s personal marketplace; see [Error responses](https://platform.claude.com/docs/en/manage-claude/plugins-api#error-responses)), a create can fail with:
10451045
10461046| Status | Cause | What to do |
10471047| -------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
from line 1048
10481048| 404 | `marketplace_id` is not a marketplace of your organization. | Take the ID from [List marketplaces](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-marketplaces). |
10491049| 400 | The marketplace is synchronized from Git, or already holds 500 plugins and skills. | Upload to a `manual` marketplace, or change the repository instead. |
10501050| 409 `plugin_name_taken` | The name is taken in that marketplace. | Upload a version to `details.plugin_id` only if it is the plugin you meant to update, because someone else might have made it. Otherwise, upload under another name (change the manifest `name`). |
1051| 409 `skill_name_taken` | The plugin is going into the library marketplace and one of its skills has an organization skill's name. | Rename the skill, or remove the organization skill in claude.ai. |
1051| 409 `skill_name_taken` | The plugin is going into the library marketplace and one of its skills has an organization skill’s name. | Rename the skill, or remove the organization skill in claude.ai. |
10521052| 409 (no `error_code`) | Another upload with the same name to the same marketplace is still in progress. | Retry shortly. |
10531053| 503 `registration_pending` | The plugin was created but its registration did not complete. | Do not resend; upload the same files as a version of `details.plugin_id` (see [Retrying uploads](https://platform.claude.com/docs/en/manage-claude/plugins-api#retrying-uploads)). |
10541054
from line 1304
13041304| Status | Cause | What to do |
13051305| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
13061306| 400 | The body omits `served_version_id`, sets it to `null`, or contains any other field; or the value lacks the `pluginver_` prefix or is `latest`. | Send exactly `{"served_version_id": "pluginver_..."}`. |
1307| 404 | `served_version_id` is not a version of this plugin. | Take the ID from [List a plugin's versions](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-a-plugins-versions). |
1307| 404 | `served_version_id` is not a version of this plugin. | Take the ID from [List a plugin’s versions](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-a-plugins-versions). |
13081308| 409 (no `error_code`) | An upload to this plugin or another served-version change is still in progress. | Retry shortly. |
13091309| 409 `skill_name_taken` | The plugin is in the library marketplace and the version has a skill whose name an organization skill now uses. | Choose another version, or rename one of the skills. |
13101310
13111311### Delete a plugin
13121312
1313`DELETE /v1/organizations/plugins/{plugin_id}` permanently deletes a plugin and every version it holds, just as an administrator's delete in claude.ai does. It works on any plugin in a `manual` marketplace, including a member's plugin, even if that member has since left the organization. When the delete returns, the plugin, its versions, and their files are gone from every read, and members are no longer served it. An organization-owned plugin's installation settings are removed with it; a member-owned plugin's shares are withdrawn, and it disappears for its owner too. A plugin in a marketplace synchronized from Git returns `400`: remove it from the repository, or remove the marketplace in claude.ai. Requires the `write:plugins` scope.
1313`DELETE /v1/organizations/plugins/{plugin_id}` permanently deletes a plugin and every version it holds, just as an administrator’s delete in claude.ai does. It works on any plugin in a `manual` marketplace, including a member’s plugin, even if that member has since left the organization. When the delete returns, the plugin, its versions, and their files are gone from every read, and members are no longer served it. An organization-owned plugin’s installation settings are removed with it; a member-owned plugin’s shares are withdrawn, and it disappears for its owner too. A plugin in a marketplace synchronized from Git returns `400`: remove it from the repository, or remove the marketplace in claude.ai. Requires the `write:plugins` scope.
13141314
1315Deletion cannot be undone, and there is no per-version delete. To withhold an organization-owned plugin reversibly instead, set its organization-wide installation setting to `not_available` (a plugin that was inheriting its marketplace's default gets a setting of its own, and [removing that organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting) later returns the plugin to the default), and remove (or set to `not_available`) every group setting that `GET /v1/organizations/plugins/{plugin_id}/installation_settings` lists, because a group's setting overrides the organization-wide value for its members. Send these writes one after another, not in parallel (see [Set an installation setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#set-an-installation-setting)). A member-owned plugin cannot be withheld through this API except by deleting it, and only if its marketplace is `manual`.
1315Deletion cannot be undone, and there is no per-version delete. To withhold an organization-owned plugin reversibly instead, set its organization-wide installation setting to `not_available` (a plugin that was inheriting its marketplace’s default gets a setting of its own, and [removing that organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting) later returns the plugin to the default), and remove (or set to `not_available`) every group setting that `GET /v1/organizations/plugins/{plugin_id}/installation_settings` lists, because a group’s setting overrides the organization-wide value for its members. Send these writes one after another, not in parallel (see [Set an installation setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#set-an-installation-setting)). A member-owned plugin cannot be withheld through this API except by deleting it, and only if its marketplace is `manual`.
13161316
13171317<CodeGroup>
13181318 ```bash cURL
from line 1406
14061406
14071407## Plugin versions
14081408
1409A plugin version is an immutable snapshot of a plugin's files from one upload (the [Create a version](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-version) response shows a complete object). Its fields mirror the plugin's served-version fields (`display_name`, `description`, `manifest_version`, `content_scan`, `components`, `reach`) for this version, plus `release_notes` (as supplied with the upload and shown in claude.ai's version history) and `created_by` (who uploaded it).
1409A plugin version is an immutable snapshot of a plugin’s files from one upload (the [Create a version](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-version) response shows a complete object). Its fields mirror the plugin’s served-version fields (`display_name`, `description`, `manifest_version`, `content_scan`, `components`, `reach`) for this version, plus `release_notes` (as supplied with the upload and shown in claude.ai’s version history) and `created_by` (who uploaded it).
14101410
14111411A `{version}` that lacks the `pluginver_` prefix returns `400` (except the literal `latest` where noted). One that carries the prefix but does not identify a version of that plugin returns `404`.
14121412
1413### List a plugin's versions
1413### List a plugin’s versions
14141414
1415`GET /v1/organizations/plugins/{plugin_id}/versions` lists a plugin's versions, ordered by `created_at` descending; the first item is the version `latest_version_id` identifies. `limit` is 1 to 1,000. Requires the `read:plugins` scope.
1415`GET /v1/organizations/plugins/{plugin_id}/versions` lists a plugin’s versions, ordered by `created_at` descending; the first item is the version `latest_version_id` identifies. `limit` is 1 to 1,000. Requires the `read:plugins` scope.
14161416
14171417<CodeGroup>
14181418 ```bash cURL
from line 1533
15331533
15341534### Create a version
15351535
1536`POST /v1/organizations/plugins/{plugin_id}/versions` adds a version to an organization-owned plugin in a `manual` marketplace. The body is `multipart/form-data`, with the same `files[]` and `release_notes` fields, [upload requirements](https://platform.claude.com/docs/en/manage-claude/plugins-api#upload-requirements), and file, manifest, archive, and size errors as [Create a plugin](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-plugin). The uploaded name (the manifest's `name`) must equal the plugin's `name`. If the plugin is not pinned, the new version is normally served as soon as it is stored; if it is pinned, the version is stored but not served until you change the served version to it. To check, compare the response's `id` with the plugin's `served_version_id`. Returns the version. Requires the `write:plugins` scope.
1536`POST /v1/organizations/plugins/{plugin_id}/versions` adds a version to an organization-owned plugin in a `manual` marketplace. The body is `multipart/form-data`, with the same `files[]` and `release_notes` fields, [upload requirements](https://platform.claude.com/docs/en/manage-claude/plugins-api#upload-requirements), and file, manifest, archive, and size errors as [Create a plugin](https://platform.claude.com/docs/en/manage-claude/plugins-api#create-a-plugin). The uploaded name (the manifest’s `name`) must equal the plugin’s `name`. If the plugin is not pinned, the new version is normally served as soon as it is stored; if it is pinned, the version is stored but not served until you change the served version to it. To check, compare the response’s `id` with the plugin’s `served_version_id`. Returns the version. Requires the `write:plugins` scope.
15371537
15381538<CodeGroup>
15391539 ```bash cURL
from line 1715
17151715
17161716| Status | Cause | What to do |
17171717| -------------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1718| 400 | The plugin is in a marketplace synchronized from Git, or the uploaded name differs from the plugin's. | Change the repository instead, or fix the manifest `name`. |
1718| 400 | The plugin is in a marketplace synchronized from Git, or the uploaded name differs from the plugin’s. | Change the repository instead, or fix the manifest `name`. |
17191719| 409 (no `error_code`) | Another upload to this plugin, or a served-version change, is still in progress. | Retry shortly. |
1720| 409 `skill_name_taken` | The plugin is in the library marketplace and the version adds a skill with an organization skill's name. | Rename the skill, or remove the organization skill in claude.ai. |
1720| 409 `skill_name_taken` | The plugin is in the library marketplace and the version adds a skill with an organization skill’s name. | Rename the skill, or remove the organization skill in claude.ai. |
17211721| 503 `registration_pending` | The version was stored but its registration did not complete. | Resend the same request when the response carries `x-should-retry: true` (see [Retrying uploads](https://platform.claude.com/docs/en/manage-claude/plugins-api#retrying-uploads)). |
17221722
17231723### Get a version
from line 1831
18311831 ```
18321832</CodeGroup>
18331833
1834### Download a version's files
1834### Download a version’s files
18351835
1836`GET /v1/organizations/plugins/{plugin_id}/versions/{version}/content` downloads a version's files as the stored `.zip` archive (`Content-Type: application/zip`). The archive is returned whatever its content-scan result, so you can inspect versions withheld from members. It is served exactly as stored, so for an organization-owned plugin in a `manual` marketplace you can re-upload it unchanged as a new version, provided it meets the current upload requirements. `{version}` must be a version ID, not `latest`: read the plugin's `served_version_id` or `latest_version_id` first, or resolve `latest` with `GET /v1/organizations/plugins/{plugin_id}/versions/latest`. The `Content-Disposition` file name is derived from the plugin's name and is not unique; name saved files by plugin and version ID. Requires the `read:plugins` scope.
1836`GET /v1/organizations/plugins/{plugin_id}/versions/{version}/content` downloads a version’s files as the stored `.zip` archive (`Content-Type: application/zip`). The archive is returned whatever its content-scan result, so you can inspect versions withheld from members. It is served exactly as stored, so for an organization-owned plugin in a `manual` marketplace you can re-upload it unchanged as a new version, provided it meets the current upload requirements. `{version}` must be a version ID, not `latest`: read the plugin’s `served_version_id` or `latest_version_id` first, or resolve `latest` with `GET /v1/organizations/plugins/{plugin_id}/versions/latest`. The `Content-Disposition` file name is derived from the plugin’s name and is not unique; name saved files by plugin and version ID. Requires the `read:plugins` scope.
18371837
1838Downloading a **member-owned** plugin's archive records a `claude_plugin_archive_accessed` event on the Compliance API Activity Feed, identifying the key (as `api_actor`), the plugin and its marketplace, the version, and the owning member by ID; it carries no names. Downloading an organization-owned plugin's archive records nothing.
1838Downloading a **member-owned** plugin’s archive records a `claude_plugin_archive_accessed` event on the Compliance API Activity Feed, identifying the key (as `api_actor`), the plugin and its marketplace, the version, and the owning member by ID; it carries no names. Downloading an organization-owned plugin’s archive records nothing.
18391839
18401840<CodeGroup>
18411841 ```bash cURL
from line 1977
19771977
19781978## Plugin installation settings
19791979
1980These endpoints apply to organization-owned plugins. They return `404` for a member-owned plugin, which has shares instead (see [Plugin shares](https://platform.claude.com/docs/en/manage-claude/plugins-api#plugin-shares)). `{target}` is the literal `organization` for the plugin's organization-wide setting, or a group's `rbac_group_` ID for that group's setting; any other value returns `400`. Group IDs come from `GET /v1/organizations/rbac_groups` (scope `read:rbac_groups`; see [Groups in User management](https://platform.claude.com/docs/en/manage-claude/user-management#groups)). A setting has no `id` of its own: it is addressed by `(plugin_id, target)`, and no actor is recorded on it (the actor is on its `plugin_installation_preference_updated` activity event).
1980These endpoints apply to organization-owned plugins. They return `404` for a member-owned plugin, which has shares instead (see [Plugin shares](https://platform.claude.com/docs/en/manage-claude/plugins-api#plugin-shares)). `{target}` is the literal `organization` for the plugin’s organization-wide setting, or a group’s `rbac_group_` ID for that group’s setting; any other value returns `400`. Group IDs come from `GET /v1/organizations/rbac_groups` (scope `read:rbac_groups`; see [Groups in User management](https://platform.claude.com/docs/en/manage-claude/user-management#groups)). A setting has no `id` of its own: it is addressed by `(plugin_id, target)`, and no actor is recorded on it (the actor is on its `plugin_installation_preference_updated` activity event).
19811981
1982### List a plugin's installation settings
1982### List a plugin’s installation settings
19831983
1984`GET /v1/organizations/plugins/{plugin_id}/installation_settings` lists the settings an organization-owned plugin holds, ordered by `created_at` descending: its own organization-wide setting (absent while it inherits its marketplace's default) and each group's setting. Filter by `target_type` (`organization` or `rbac_group`). Requires the `read:plugins` scope.
1984`GET /v1/organizations/plugins/{plugin_id}/installation_settings` lists the settings an organization-owned plugin holds, ordered by `created_at` descending: its own organization-wide setting (absent while it inherits its marketplace’s default) and each group’s setting. Filter by `target_type` (`organization` or `rbac_group`). Requires the `read:plugins` scope.
19851985
19861986<CodeGroup>
19871987 ```bash cURL
from line 2089
20892089
20902090### Set an installation setting
20912091
2092`POST /v1/organizations/plugins/{plugin_id}/installation_settings/{target}` sets one target's installation setting for an organization-owned plugin, creating it or changing the value it already holds. The body's only field is `installation_preference` (`required`, `auto_install`, `available`, or `not_available`), and it is required. Setting the value the target already holds changes nothing. Setting the `organization` target stops the plugin inheriting its marketplace's default (`organization_installation_preference_inherited` becomes `false`), even when the value is the same as the default, so the plugin no longer follows later changes to the marketplace default. To return the plugin to its marketplace's default, [remove the organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting); group settings are unaffected. A group target must be a group your organization can see in `GET /v1/organizations/rbac_groups`, or the request returns `404`. The change does not alter the plugin's `updated_at`; it is recorded on the Activity Feed. Returns the setting. Requires the `write:plugins` scope.
2092`POST /v1/organizations/plugins/{plugin_id}/installation_settings/{target}` sets one target’s installation setting for an organization-owned plugin, creating it or changing the value it already holds. The body’s only field is `installation_preference` (`required`, `auto_install`, `available`, or `not_available`), and it is required. Setting the value the target already holds changes nothing. Setting the `organization` target stops the plugin inheriting its marketplace’s default (`organization_installation_preference_inherited` becomes `false`), even when the value is the same as the default, so the plugin no longer follows later changes to the marketplace default. To return the plugin to its marketplace’s default, [remove the organization-wide setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#remove-an-installation-setting); group settings are unaffected. A group target must be a group your organization can see in `GET /v1/organizations/rbac_groups`, or the request returns `404`. The change does not alter the plugin’s `updated_at`; it is recorded on the Activity Feed. Returns the setting. Requires the `write:plugins` scope.
20932093
2094Send a plugin's installation-setting writes one at a time. If several writes for the same plugin arrive at the same time, the server handles them one after another and can answer some of them with `503` instead of applying them. That `503` carries `x-should-retry: true`, and the write is safe to repeat: wait a second or two, then send it again.
2094Send a plugin’s installation-setting writes one at a time. If several writes for the same plugin arrive at the same time, the server handles them one after another and can answer some of them with `503` instead of applying them. That `503` carries `x-should-retry: true`, and the write is safe to repeat: wait a second or two, then send it again.
20952095
20962096<CodeGroup>
20972097 ```bash cURL
from line 2238
22382238
22392239### Remove an installation setting
22402240
2241`DELETE /v1/organizations/plugins/{plugin_id}/installation_settings/{target}` removes one target's installation setting for an organization-owned plugin. With `{target}` of `organization` it removes the plugin's own organization-wide setting. The plugin then inherits its marketplace's default again (`organization_installation_preference_inherited` becomes `true`) and follows later changes to that default; group settings are unaffected. With a group's `rbac_group_` ID it removes that group's setting: the group's members fall back to the organization-wide value, or to another of their groups' settings. A target that holds no setting of its own returns `404`; that includes the `organization` target of a plugin that already inherits its marketplace's default. The change does not alter the plugin's `updated_at`; it is recorded on the Activity Feed. The response carries the composite key in place of an `id`; for the `organization` target its `target` is `{"type": "organization"}`. Requires the `write:plugins` scope.
2241`DELETE /v1/organizations/plugins/{plugin_id}/installation_settings/{target}` removes one target’s installation setting for an organization-owned plugin. With `{target}` of `organization` it removes the plugin’s own organization-wide setting. The plugin then inherits its marketplace’s default again (`organization_installation_preference_inherited` becomes `true`) and follows later changes to that default; group settings are unaffected. With a group’s `rbac_group_` ID it removes that group’s setting: the group’s members fall back to the organization-wide value, or to another of their groups’ settings. A target that holds no setting of its own returns `404`; that includes the `organization` target of a plugin that already inherits its marketplace’s default. The change does not alter the plugin’s `updated_at`; it is recorded on the Activity Feed. The response carries the composite key in place of an `id`; for the `organization` target its `target` is `{"type": "organization"}`. Requires the `write:plugins` scope.
22422242
22432243<CodeGroup>
22442244 ```bash cURL
from line 2357
23572357
23582358Shares exist only on member-owned plugins and are read-only in this API (see [Shares](https://platform.claude.com/docs/en/manage-claude/plugins-api#shares)).
23592359
2360### List a plugin's shares
2360### List a plugin’s shares
23612361
23622362`GET /v1/organizations/plugins/{plugin_id}/shares` lists who the owner of a member-owned plugin has shared it with, ordered by `granted_at` descending: every member (`organization`), a group (`rbac_group`), or a named member (`organization_member`). Filter by `target_type`. A plugin its owner has not shared returns an empty list; an organization-owned plugin returns `404`. Shares are read-only on this API, and a listed share gives access only while that kind of sharing is turned on for your organization in claude.ai (see [Shares](https://platform.claude.com/docs/en/manage-claude/plugins-api#shares)). `granted_at` is when the share was given; if the owner later changes the share in claude.ai, it is the time of that change. Requires the `read:plugins` scope.
23632363
from line 2477
24772477
24782478## Plugin marketplaces
24792479
2480This API reads [marketplaces](https://platform.claude.com/docs/en/manage-claude/plugins-api#marketplaces) and sets an organization marketplace's default installation setting; marketplaces themselves are created, connected to a repository, and deleted in claude.ai.
2480This API reads [marketplaces](https://platform.claude.com/docs/en/manage-claude/plugins-api#marketplaces) and sets an organization marketplace’s default installation setting; marketplaces themselves are created, connected to a repository, and deleted in claude.ai.
24812481
24822482```json
24832483{
from line 2496
24962496
24972497| Field | Description |
24982498| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2499| `name` | The marketplace's name. Fixed for its lifetime. |
2499| `name` | The marketplace’s name. Fixed for its lifetime. |
25002500| `owner` | Same shape as on the plugin. |
25012501| `source` | `manual`, `github`, `gitlab`, or `public_git`. See [Marketplaces](https://platform.claude.com/docs/en/manage-claude/plugins-api#marketplaces). |
25022502| `sync_status` | Outcome of the most recent synchronization: `success`, `in_progress`, `failed_content`, `failed_transient`, `failed_auth`, or `failed_limits`. `null` until a synchronization is first attempted, which never occurs for a marketplace whose source is `manual`. |
from line 2508
25082508
25092509### List marketplaces
25102510
2511`GET /v1/organizations/plugin_marketplaces` lists your organization's marketplaces and members' personal marketplaces, ordered by `created_at` descending. Use it to find a marketplace's ID, to filter the plugin list by it or to upload into it, before it holds any plugin. The library marketplace appears once something has first been created in it, in claude.ai or through this API. Filter by `owner_type` (`organization` or `user`) and `source`. `limit` is 1 to 1,000. Requires the `read:plugins` scope.
2511`GET /v1/organizations/plugin_marketplaces` lists your organization’s marketplaces and members’ personal marketplaces, ordered by `created_at` descending. Use it to find a marketplace’s ID, to filter the plugin list by it or to upload into it, before it holds any plugin. The library marketplace appears once something has first been created in it, in claude.ai or through this API. Filter by `owner_type` (`organization` or `user`) and `source`. `limit` is 1 to 1,000. Requires the `read:plugins` scope.
25122512
25132513<CodeGroup>
25142514 ```bash cURL
from line 2722
27222722 ```
27232723</CodeGroup>
27242724
2725### Set a marketplace's default installation setting
2725### Set a marketplace’s default installation setting
27262726
2727`POST /v1/organizations/plugin_marketplaces/{marketplace_id}` sets the default installation setting of an organization-owned marketplace. Every plugin in the marketplace without its own organization-wide setting reports this default as its `organization_installation_preference`, including plugins added later. It works for `manual` and synchronized marketplaces; a member's personal marketplace returns `403`. The only updatable field is `default_installation_preference`, and it is required. It cannot be set back to `null`: once a marketplace has a default, it keeps one, as in claude.ai. A change is recorded as one `marketplace_updated` event with no per-plugin events, and does not alter any plugin's `updated_at`. Setting the value already set changes nothing, with one exception: a marketplace whose default was never set reports `not_available` but holds no setting, so its first write (even `not_available`) counts as a change. Returns the marketplace. Requires the `write:plugins` scope.
2727`POST /v1/organizations/plugin_marketplaces/{marketplace_id}` sets the default installation setting of an organization-owned marketplace. Every plugin in the marketplace without its own organization-wide setting reports this default as its `organization_installation_preference`, including plugins added later. It works for `manual` and synchronized marketplaces; a member’s personal marketplace returns `403`. The only updatable field is `default_installation_preference`, and it is required. It cannot be set back to `null`: once a marketplace has a default, it keeps one, as in claude.ai. A change is recorded as one `marketplace_updated` event with no per-plugin events, and does not alter any plugin’s `updated_at`. Setting the value already set changes nothing, with one exception: a marketplace whose default was never set reports `not_available` but holds no setting, so its first write (even `not_available`) counts as a change. Returns the marketplace. Requires the `write:plugins` scope.
27282728
27292729<CodeGroup>
27302730 ```bash cURL
from line 2849
28492849
28502850### Validate marketplace content
28512851
2852Two endpoints report what a synchronization of the given marketplace content would do, without connecting or storing anything: `POST /v1/organizations/plugin_marketplaces/validate_repository` reads a public GitHub repository, and `POST /v1/organizations/plugin_marketplaces/validate_archive` reads a `.zip` of the marketplace directory that you upload. Both return the same report: whether `marketplace.json` is well-formed, which plugins would be skipped and why, and which plugins would synchronize with some contents left out. The checks are the ones a real synchronization runs. Problems with the content come back in the report, not as HTTP errors: the request succeeds with `valid: false`, even when the repository or archive cannot be read at all. A validation counts as a read, and the two endpoints together are additionally limited to 10 validations a minute for each organization (see [Rate limiting](https://platform.claude.com/docs/en/manage-claude/plugins-api#rate-limiting)); they record nothing on the Activity Feed. A validation can take up to 120 seconds before it returns, so set your client's timeout above that. Both endpoints require the `read:plugins` or `write:plugins` scope (`read:org_audit` and `read:compliance_org_data` do not grant them).
2852Two endpoints report what a synchronization of the given marketplace content would do, without connecting or storing anything: `POST /v1/organizations/plugin_marketplaces/validate_repository` reads a public GitHub repository, and `POST /v1/organizations/plugin_marketplaces/validate_archive` reads a `.zip` of the marketplace directory that you upload. Both return the same report: whether `marketplace.json` is well-formed, which plugins would be skipped and why, and which plugins would synchronize with some contents left out. The checks are the ones a real synchronization runs. Problems with the content come back in the report, not as HTTP errors: the request succeeds with `valid: false`, even when the repository or archive cannot be read at all. A validation counts as a read, and the two endpoints together are additionally limited to 10 validations a minute for each organization (see [Rate limiting](https://platform.claude.com/docs/en/manage-claude/plugins-api#rate-limiting)); they record nothing on the Activity Feed. A validation can take up to 120 seconds before it returns, so set your client’s timeout above that. Both endpoints require the `read:plugins` or `write:plugins` scope (`read:org_audit` and `read:compliance_org_data` do not grant them).
28532853
28542854Validation reads the repository anonymously. It does the same for any plugin source that is on GitHub but outside the repository. As a result, validation reports a private repository or a private plugin source as not found or as needing a GitHub sign-in. When validation reports that a GitHub sign-in is needed, the report code is `marketplace_repo_sign_in_required`.
28552855
from line 2861
28612861* Validation reports unpinned sources and sources on unsupported hosts as plugin errors.
28622862* For `validate_repository`, the branch that validation reads defaults to the branch that the marketplace synchronizes from.
28632863
2864`validate_repository` takes a JSON body with two fields: `repository_url`, the `https://` URL of a public repository on github.com (required), and `ref`, a branch name or a full 40-character commit SHA (optional). When `ref` is omitted or `null`, it defaults to the branch a synchronization would read, usually the repository's default branch. `validate_archive` takes `multipart/form-data` with exactly one part, `archive`, sent as a file part with a file name. The file is a `.zip` of the marketplace directory, at most 32 MB, that uses DEFLATE or STORE compression only. The archive's contents sit at the root or are wrapped in one folder (as a Git host's download produces). No other form field is accepted.
2864`validate_repository` takes a JSON body with two fields: `repository_url`, the `https://` URL of a public repository on github.com (required), and `ref`, a branch name or a full 40-character commit SHA (optional). When `ref` is omitted or `null`, it defaults to the branch a synchronization would read, usually the repository’s default branch. `validate_archive` takes `multipart/form-data` with exactly one part, `archive`, sent as a file part with a file name. The file is a `.zip` of the marketplace directory, at most 32 MB, that uses DEFLATE or STORE compression only. The archive’s contents sit at the root or are wrapped in one folder (as a Git host’s download produces). No other form field is accepted.
28652865
28662866Validate a public repository at a branch:
28672867
from line 3014
30143014| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30153015| `valid` | `true` when `marketplace.json` is well-formed and no plugin would be skipped. Warnings do not make it `false`. |
30163016| `ref` | The branch that was read, by name; `null` when no branch was named and the default branch was read, for a commit SHA, or for an archive. |
3017| `commit_sha` | The commit that was validated. For an archive downloaded from a Git host, the commit the host recorded in the ZIP file's comment field, if any (not verified). |
3017| `commit_sha` | The commit that was validated. For an archive downloaded from a Git host, the commit the host recorded in the ZIP file’s comment field, if any (not verified). |
30183018| `total_plugin_count` | How many plugins `marketplace.json` declares; `0` when it could not be read. |
30193019| `manifest_error`, `manifest_error_code` | Set when nothing could be validated: the source could not be read, or `marketplace.json` is missing, malformed, or over a limit. Validation that did not complete within 120 seconds reports `manifest_error_code: "marketplace_validate_deadline_exceeded"`. |
30203020| `plugin_errors` | One `{name, error, error_code}` per plugin a synchronization would skip. |
from line 3141
31413141
31423142| Status | Cause | What to do |
31433143| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
3144| 400 | On `validate_repository`: the body is not a JSON object; `repository_url` is missing, is longer than 2,048 characters, carries credentials, or is not of the form `https://github.com/{owner}/{repo}` (a `.git` suffix is accepted; another host, a longer path such as a branch page's `/tree/main`, or a port other than 443 or 80 is not); `ref` is empty, is longer than 255 characters, contains `..`, or contains a character other than ASCII letters, digits, `.`, `_`, `-`, `+`, and `/`; or another field is present. A `ref` that passes these checks but names a branch the repository does not have is not refused: the request succeeds with `valid: false` and `manifest_error` says the branch was not found. On `validate_archive`: the body is not `multipart/form-data`, the `archive` part is missing, repeated, or not sent as a file part with a file name, or another form field is present. | Fix the request and resend. |
3145| 413 | On `validate_archive`: the `archive` part, or the request's declared body length, exceeds 32 MB. | Validate the repository by URL instead, or trim the archive. |
3144| 400 | On `validate_repository`: the body is not a JSON object; `repository_url` is missing, is longer than 2,048 characters, carries credentials, or is not of the form `https://github.com/{owner}/{repo}` (a `.git` suffix is accepted; another host, a longer path such as a branch page’s `/tree/main`, or a port other than 443 or 80 is not); `ref` is empty, is longer than 255 characters, contains `..`, or contains a character other than ASCII letters, digits, `.`, `_`, `-`, `+`, and `/`; or another field is present. A `ref` that passes these checks but names a branch the repository does not have is not refused: the request succeeds with `valid: false` and `manifest_error` says the branch was not found. On `validate_archive`: the body is not `multipart/form-data`, the `archive` part is missing, repeated, or not sent as a file part with a file name, or another form field is present. | Fix the request and resend. |
3145| 413 | On `validate_archive`: the `archive` part, or the request’s declared body length, exceeds 32 MB. | Validate the repository by URL instead, or trim the archive. |
31463146
31473147#### Report codes
31483148
3149Each finding in a report has a stable code: `manifest_error_code` when nothing could be validated, `error_code` on each `plugin_errors` entry, and `error_code` on each warning. When a plugin has several problems, `error_code` is the first one's and `error` joins their messages. New codes may be added; an unrecognized `manifest_error_code` still means the content could not be validated, one on a `plugin_errors` entry still means the plugin would be skipped, and one on a warning still means the plugin would synchronize. These codes indicate transient conditions, so the same request might succeed later: `marketplace_host_rate_limited`, `marketplace_host_server_error`, `marketplace_host_timeout`, `marketplace_host_unreachable`, `marketplace_repo_access_denied`, `marketplace_sync_transient_fetch_budget_exhausted`, `marketplace_validate_network_error`, and usually `marketplace_validate_deadline_exceeded`.
3149Each finding in a report has a stable code: `manifest_error_code` when nothing could be validated, `error_code` on each `plugin_errors` entry, and `error_code` on each warning. When a plugin has several problems, `error_code` is the first one’s and `error` joins their messages. New codes may be added; an unrecognized `manifest_error_code` still means the content could not be validated, one on a `plugin_errors` entry still means the plugin would be skipped, and one on a warning still means the plugin would synchronize. These codes indicate transient conditions, so the same request might succeed later: `marketplace_host_rate_limited`, `marketplace_host_server_error`, `marketplace_host_timeout`, `marketplace_host_unreachable`, `marketplace_repo_access_denied`, `marketplace_sync_transient_fetch_budget_exhausted`, `marketplace_validate_network_error`, and usually `marketplace_validate_deadline_exceeded`.
31503150
31513151## Unrecognized values
31523152
from line 3154
31543154
31553155## Rate limiting
31563156
3157Read requests (every `GET` endpoint on this page) share a limit of **300 requests per minute** for each organization. Write requests (creating a plugin or version, changing the served version, deleting, setting or removing an installation setting, and updating a marketplace) share a limit of **60 requests per minute** for each organization. A marketplace validation (either endpoint) counts as a read, and validations are additionally limited to 10 a minute for each organization across both endpoints; both limits are checked before the request body is read. These limits are counted across all of your organization's keys and are separate from your organization's other Admin API limits. Requests over a limit return **429 Too Many Requests** with a `retry-after` header. Responses include `anthropic-ratelimit-requests-*` headers for the limit that applies (on marketplace validation, its 10-per-minute limit; on a `429`, whichever limit refused the request).
3157Read requests (every `GET` endpoint on this page) share a limit of **300 requests per minute** for each organization. Write requests (creating a plugin or version, changing the served version, deleting, setting or removing an installation setting, and updating a marketplace) share a limit of **60 requests per minute** for each organization. A marketplace validation (either endpoint) counts as a read, and validations are additionally limited to 10 a minute for each organization across both endpoints; both limits are checked before the request body is read. These limits are counted across all of your organization’s keys and are separate from your organization’s other Admin API limits. Requests over a limit return **429 Too Many Requests** with a `retry-after` header. Responses include `anthropic-ratelimit-requests-*` headers for the limit that applies (on marketplace validation, its 10-per-minute limit; on a `429`, whichever limit refused the request).
31583158
31593159An upload, a served-version change, or a validation can also return `429` with `retry-after` when the service briefly has no capacity for another one, and an upload returns `429` when your organization has exceeded its content-scan rate. Handle all of these the same way: wait for `retry-after`, then retry. Separately from these limits, send installation-setting writes for the same plugin one at a time: when several arrive at the same time, some can be answered with `503` and `x-should-retry: true`, and those are safe to send again after a second or two (see [Set an installation setting](https://platform.claude.com/docs/en/manage-claude/plugins-api#set-an-installation-setting)).
31603160
31613161## Pagination
31623162
3163List endpoints use an **opaque cursor**. The first request returns up to `limit` rows plus a `next_page` cursor; pass the cursor unchanged as the `page` parameter on the next request, and repeat until `next_page` is `null`. Treat the cursor string as opaque: don't parse, modify, or construct it yourself. [List plugins](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-plugins) can return a page with fewer than `limit` plugins, or none, while `next_page` is still set, so keep requesting pages until `next_page` is `null`. The Python, TypeScript, C#, Go, Java, and Ruby SDKs' list iterators and the CLI (except with `--format raw`) do that for you. They request the next page while `next_page` is set, so a short or empty page does not end them. In PHP and curl, request each page yourself and pass its `next_page` as `page`.
3163List endpoints use an **opaque cursor**. The first request returns up to `limit` rows plus a `next_page` cursor; pass the cursor unchanged as the `page` parameter on the next request, and repeat until `next_page` is `null`. Treat the cursor string as opaque: don’t parse, modify, or construct it yourself. [List plugins](https://platform.claude.com/docs/en/manage-claude/plugins-api#list-plugins) can return a page with fewer than `limit` plugins, or none, while `next_page` is still set, so keep requesting pages until `next_page` is `null`. The Python, TypeScript, C#, Go, Java, and Ruby SDKs’ list iterators and the CLI (except with `--format raw`) do that for you. They request the next page while `next_page` is set, so a short or empty page does not end them. In PHP and curl, request each page yourself and pass its `next_page` as `page`.
31643164
31653165`limit` defaults to 20 and has a minimum of 1. The maximum is 100 for plugins, installation settings, and shares, and 1,000 for versions and marketplaces. Every list is ordered newest first.
31663166
from line 3170
31703170
31713171| Status | Meaning |
31723172| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3173| 400 | Invalid input, or the operation does not apply to this plugin or marketplace (see each endpoint's section). Also returned for a query parameter the endpoint does not recognize, and for an organization that is not a Claude Enterprise organization (`this endpoint is not supported for this organization type`). |
3173| 400 | Invalid input, or the operation does not apply to this plugin or marketplace (see each endpoint’s section). Also returned for a query parameter the endpoint does not recognize, and for an organization that is not a Claude Enterprise organization (`this endpoint is not supported for this organization type`). |
31743174| 401 | Missing `x-api-key` header, or the key is not recognized. |
3175| 403 | The key is missing the required scope, or the request uploads to, changes the served version of, or sets the default for a member's plugin or personal marketplace. (Deleting a member's plugin is allowed.) |
3175| 403 | The key is missing the required scope, or the request uploads to, changes the served version of, or sets the default for a member’s plugin or personal marketplace. (Deleting a member’s plugin is allowed.) |
31763176| 404 | Resource not found. Also returned when the request omits the `anthropic-beta` value, or the API is not enabled for your organization, so that the endpoints read as nonexistent. |
3177| 409 | A name is taken, a version's `content_scan` reads `processing`, or a conflicting upload is in progress. |
3177| 409 | A name is taken, a version’s `content_scan` reads `processing`, or a conflicting upload is in progress. |
31783178| 413 | The request body is over the size limit: 200 MB for an upload, 32 MB for marketplace validation. |
31793179| 429 | Rate limit exceeded. See [Rate limiting](https://platform.claude.com/docs/en/manage-claude/plugins-api#rate-limiting). |
31803180| 500 | Internal error. |
from line 3201
32013201| ----------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
32023202| `plugin_name_taken` | 409 | A plugin with this name already exists in the marketplace. `details.plugin_id` is that plugin, and someone else might have made it. To see who, [get the plugin](https://platform.claude.com/docs/en/manage-claude/plugins-api#get-a-plugin) and read its `created_by`, which is `null` when no creator is recorded. Upload versions to it only if it is the plugin you meant to update. Otherwise, upload under another name. When `plugin_id` is absent, the name is held by a standalone skill: upload under another name, or delete the skill in claude.ai. |
32033203| `skill_name_taken` | 409 | The plugin is in the library marketplace and one of its skills has the same name as an organization skill (a skill an administrator uploaded for the whole organization in claude.ai). `details.skill_name` names it. Rename or remove one of them. |
3204| `registration_pending` | 503 | The files were stored, but the plugin's skills could not yet be made available to members. See [Retrying uploads](https://platform.claude.com/docs/en/manage-claude/plugins-api#retrying-uploads). |
3205| `scan_pending` | 409 | The version's `content_scan` reads `processing`. Retry once the scan finishes. |
3206| `scan_failed` | 400 | The version's `content_scan` reads `fail`, `unknown`, or `errored`, so it cannot be served. Choose another version. |
3207| `cmek_key_disabled`, `cmek_key_network_blocked` | 400 | Your organization's customer-managed encryption key is unavailable. See [Customer-managed encryption keys](https://platform.claude.com/docs/en/manage-claude/plugins-api#customer-managed-encryption-keys). |
3208| `unsupported_charset` | 400 | On an upload or `validate_archive`, the request's `Content-Type` header names a charset other than UTF-8, ISO-8859-1, or US-ASCII. Send the request with no charset, or with one of these. |
3204| `registration_pending` | 503 | The files were stored, but the plugin’s skills could not yet be made available to members. See [Retrying uploads](https://platform.claude.com/docs/en/manage-claude/plugins-api#retrying-uploads). |
3205| `scan_pending` | 409 | The version’s `content_scan` reads `processing`. Retry once the scan finishes. |
3206| `scan_failed` | 400 | The version’s `content_scan` reads `fail`, `unknown`, or `errored`, so it cannot be served. Choose another version. |
3207| `cmek_key_disabled`, `cmek_key_network_blocked` | 400 | Your organization’s customer-managed encryption key is unavailable. See [Customer-managed encryption keys](https://platform.claude.com/docs/en/manage-claude/plugins-api#customer-managed-encryption-keys). |
3208| `unsupported_charset` | 400 | On an upload or `validate_archive`, the request’s `Content-Type` header names a charset other than UTF-8, ISO-8859-1, or US-ASCII. Send the request with no charset, or with one of these. |
32093209
32103210New codes may be added. Treat a code you do not recognize the way you treat its status.
32113211
from line 3213
32133213
32143214No endpoint accepts an `Idempotency-Key`. Changing the served version, setting an installation setting, and setting a marketplace default are safe to repeat. A repeated delete, or a repeated removal of an installation setting, returns `404`.
32153215
3216An upload that returns an error stored nothing, with one exception: `503` with `error_code: "registration_pending"`. After storing an upload's files, the server registers the new version's skills with claude.ai, which is what makes them usable by members; `registration_pending` means the files were stored but that last step did not complete. Uploading the same files once more completes it (and stores one more, identical version):
3216An upload that returns an error stored nothing, with one exception: `503` with `error_code: "registration_pending"`. After storing an upload’s files, the server registers the new version’s skills with claude.ai, which is what makes them usable by members; `registration_pending` means the files were stored but that last step did not complete. Uploading the same files once more completes it (and stores one more, identical version):
32173217
32183218* On `POST /v1/organizations/plugins`, the plugin *was* created, and the response carries `x-should-retry: false`: do not resend the create (a resend returns `409 plugin_name_taken`); instead upload the same files as a version of `details.plugin_id`.
32193219* On `POST /v1/organizations/plugins/{plugin_id}/versions`, the version *was* stored (`details.plugin_version_id`); resend the same request when the response carries `x-should-retry: true`, and do not when it carries `false`.
32203220
3221If a create's response is lost, send the create again. A `409 plugin_name_taken` means either that the first attempt stored the plugin or that the name was already taken. Your lost create made the plugin in `details.plugin_id` only if its `created_by` is an `api_actor` with your key's `api_key_id`. Check that before you upload to it (see `plugin_name_taken` in the preceding table). Retrying a version create whose response was lost stores a second, identical version. To avoid that, record the plugin's `latest_version_id` before each upload; if a response is lost, read the plugin and retry only if `latest_version_id` is unchanged.
3221If a create’s response is lost, send the create again. A `409 plugin_name_taken` means either that the first attempt stored the plugin or that the name was already taken. Your lost create made the plugin in `details.plugin_id` only if its `created_by` is an `api_actor` with your key’s `api_key_id`. Check that before you upload to it (see `plugin_name_taken` in the preceding table). Retrying a version create whose response was lost stores a second, identical version. To avoid that, record the plugin’s `latest_version_id` before each upload; if a response is lost, read the plugin and retry only if `latest_version_id` is unchanged.
32223222
32233223## Activity Feed events
32243224
3225Every write through this API is recorded on your organization's [Compliance API Activity Feed](https://platform.claude.com/docs/en/manage-claude/compliance-activity-feed), attributed to the API key as an `api_actor` carrying its `apikey_` ID. The same actor appears in `created_by` on the plugins and versions the key creates.
3225Every write through this API is recorded on your organization’s [Compliance API Activity Feed](https://platform.claude.com/docs/en/manage-claude/compliance-activity-feed), attributed to the API key as an `api_actor` carrying its `apikey_` ID. The same actor appears in `created_by` on the plugins and versions the key creates.
32263226
32273227| Event | Emitted when |
32283228| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
from line 3233
32333233| `claude_plugin_deleted` | A plugin is deleted on its own, here or in claude.ai. |
32343234| `plugin_installation_preference_updated` | An installation setting is set or removed. |
32353235| `marketplace_created` | The first upload creates the library marketplace. |
3236| `marketplace_updated` | A marketplace's default installation setting changes, or an administrator or owner starts a synchronization in claude.ai. |
3236| `marketplace_updated` | A marketplace’s default installation setting changes, or an administrator or owner starts a synchronization in claude.ai. |
32373237| `marketplace_deleted` | A marketplace is deleted in claude.ai together with its plugins (no per-plugin events). |
3238| `claude_plugin_archive_accessed` | A member-owned plugin's archive is downloaded. |
3238| `claude_plugin_archive_accessed` | A member-owned plugin’s archive is downloaded. |
32393239| `claude_plugin_security_scan_completed` | A content scan completes. |
32403240
32413241The plugin, version, and marketplace IDs in these events are the same IDs this API returns. `plugin_installation_preference_updated` identifies the plugin by its `name` and `marketplace_id` rather than its `id`. In `claude_plugin_security_scan_completed`, `plugin_version` is not a version ID, and no field of a [plugin version](https://platform.claude.com/docs/en/manage-claude/plugins-api#plugin-versions) corresponds to it.
32423242
3243Changing a marketplace's default records one `marketplace_updated` event and no per-plugin events, even though it changes the value of every plugin that inherits the default. Reads are not recorded, except downloads of a member-owned plugin's archive. A write that changes nothing records nothing.
3243Changing a marketplace’s default records one `marketplace_updated` event and no per-plugin events, even though it changes the value of every plugin that inherits the default. Reads are not recorded, except downloads of a member-owned plugin’s archive. A write that changes nothing records nothing.
32443244
3245Shares given or withdrawn in claude.ai appear on the feed as `role_assignment_granted` and `role_assignment_revoked` events. This API does not report deletions: a deleted plugin is absent from the next list. A plugin removed by a Git synchronization, by deleting its marketplace (one `marketplace_deleted` event), or by deleting a member's account or the organization emits no per-plugin event, so re-list the full inventory periodically to catch removals.
3245Shares given or withdrawn in claude.ai appear on the feed as `role_assignment_granted` and `role_assignment_revoked` events. This API does not report deletions: a deleted plugin is absent from the next list. A plugin removed by a Git synchronization, by deleting its marketplace (one `marketplace_deleted` event), or by deleting a member’s account or the organization emits no per-plugin event, so re-list the full inventory periodically to catch removals.
32463246
32473247## Customer-managed encryption keys
32483248
3249If your organization uses a [customer-managed encryption key](https://platform.claude.com/docs/en/manage-claude/cmek), a version's `description`, `release_notes`, `components`, and files are encrypted with it. While the key is unavailable:
3249If your organization uses a [customer-managed encryption key](https://platform.claude.com/docs/en/manage-claude/cmek), a version’s `description`, `release_notes`, `components`, and files are encrypted with it. While the key is unavailable:
32503250
32513251* Reads and lists still succeed, with `description`, `release_notes`, and `components` returned as `null`.
32523252* Archive downloads, creates, version creates, and served-version changes return `400` with `cmek_key_disabled` or `cmek_key_network_blocked`.