archastro.platform.v1.resources.solutions
1# Copyright (c) 2026 ArchAstro Inc. Licensed under the MIT License. 2# This file is auto-generated by @archastro/sdk-generator. Do not edit. 3# Content hash: 593cc7ef4728 4 5from __future__ import annotations 6 7import builtins 8from typing import Any, Literal, Required, TypedDict 9 10from ...runtime.http_client import HttpClient, SyncHttpClient 11from ...types.common import ( 12 ProductKnowledgeReconcileResponse, 13 SolutionDependentsResponse, 14 SolutionImportResponse, 15 SolutionInstallResponse, 16 SolutionListResponse, 17 SolutionSummary, 18 SolutionUpgradeResponse, 19) 20 21 22class SolutionCreateInputSolutionBundleConfigsItem(TypedDict, total=False): 23 content: Required[str] 24 "Full text content of the configuration file." 25 content_type: str | None 26 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 27 relative_path: Required[str] 28 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation." 29 30 31class SolutionCreateInputSolutionBundleSetupActionsItem(TypedDict, total=False): 32 depends_on: list[str] | None 33 "List of other setup action identifiers that must be completed before this action becomes actionable." 34 description: str | None 35 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 36 kind: Required[str] 37 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 38 params: dict[str, Any] | None 39 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 40 required: bool | None 41 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 42 sort_order: int | None 43 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 44 title: Required[str] 45 "Short human-readable label displayed in the setup checklist." 46 verify_config: dict[str, Any] | None 47 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.' 48 49 50class SolutionCreateInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 51 content: Required[str] 52 "Full text content of the file." 53 content_type: str | None 54 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 55 relative_path: Required[str] 56 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.' 57 58 59class SolutionCreateInputSolutionBundleSkillsItem(TypedDict, total=False): 60 content: Required[str] 61 "Full text content of the `SKILL.md` file." 62 content_type: str | None 63 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 64 files: list[SolutionCreateInputSolutionBundleSkillsItemFilesItem] | None 65 "Additional files nested inside the skill folder, each with its own path and content." 66 relative_path: Required[str] 67 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).' 68 69 70class SolutionCreateInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 71 content: Required[str] 72 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 73 content_type: str | None 74 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 75 data_encoding: str | None 76 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 77 relative_path: Required[str] 78 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)." 79 80 81class SolutionCreateInputSolutionBundleSolution(TypedDict, total=False): 82 content: Required[str] 83 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 84 content_type: str | None 85 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 86 files: list[SolutionCreateInputSolutionBundleSolutionFilesItem] | None 87 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 88 lookup_key: Required[str] 89 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config." 90 91 92class SolutionCreateInputSolutionBundleTemplate(TypedDict, total=False): 93 content: Required[str] 94 "Full text content of the agent template file, typically a YAML document." 95 content_type: str | None 96 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 97 relative_path: Required[str] 98 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 99 100 101class SolutionCreateInputSolutionBundleTemplatesItem(TypedDict, total=False): 102 content: Required[str] 103 "Full text content of the agent template file, typically a YAML document." 104 content_type: str | None 105 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 106 relative_path: Required[str] 107 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 108 109 110class SolutionCreateInputSolutionBundle(TypedDict, total=False): 111 configs: list[SolutionCreateInputSolutionBundleConfigsItem] | None 112 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 113 lookup_key_prefix: str | None 114 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 115 lookup_key_suffix: str | None 116 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 117 setup_actions: list[SolutionCreateInputSolutionBundleSetupActionsItem] | None 118 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 119 skills: list[SolutionCreateInputSolutionBundleSkillsItem] | None 120 "Skill bundles (root config plus supporting files) that this solution depends on." 121 solution: Required[SolutionCreateInputSolutionBundleSolution] 122 "The solution config to install, including the solution.yaml body and any referenced component files." 123 template: SolutionCreateInputSolutionBundleTemplate | None 124 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 125 templates: list[SolutionCreateInputSolutionBundleTemplatesItem] | None 126 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`." 127 128 129class SolutionCreateInput(TypedDict, total=False): 130 "Import a Solution into the library" 131 132 dry_run: bool | None 133 "When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`." 134 org: str | None 135 "Organization ID (`org_...`) for the import destination scope." 136 solution: str | None 137 "Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`." 138 solution_bundle: SolutionCreateInputSolutionBundle | None 139 "Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`." 140 team: str | None 141 "Team ID (`team_...`) for the import destination scope." 142 user: str | None 143 "User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set." 144 virtual_path_prefix: str | None 145 "Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix." 146 147 148class SolutionInstallInputAclAddItem(TypedDict, total=False): 149 actions: Required[list[str]] 150 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' 151 principal: str | None 152 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' 153 principal_type: Required[str] 154 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' 155 156 157class SolutionInstallInputAclGrantsItem(TypedDict, total=False): 158 actions: Required[list[str]] 159 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' 160 principal: str | None 161 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' 162 principal_type: Required[str] 163 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' 164 165 166class SolutionInstallInputAclRemoveItem(TypedDict, total=False): 167 principal: str | None 168 'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.' 169 principal_type: Required[str] 170 'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.' 171 172 173class SolutionInstallInputAcl(TypedDict, total=False): 174 add: list[SolutionInstallInputAclAddItem] | None 175 "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`." 176 grants: list[SolutionInstallInputAclGrantsItem] | None 177 "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`." 178 remove: list[SolutionInstallInputAclRemoveItem] | None 179 "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`." 180 181 182class SolutionInstallInputDetailsPrefills(TypedDict, total=False): 183 participants: dict[str, Any] | None 184 "Participant slot-to-agent mappings applied by the platform. Caller values at these slots must match exactly." 185 payload: dict[str, Any] | None 186 "Partial invocation payload applied by the platform. A caller may omit these values, but supplying a different value at any locked path is rejected." 187 188 189class SolutionInstallInputDetails(TypedDict, total=False): 190 prefills: SolutionInstallInputDetailsPrefills | None 191 "Instance-specific locked payload and participant values. Payload paths and participant slots are validated against the installed template's resolved input schema and workflow." 192 type: Required[Literal["automation"]] 193 "Install-details discriminator. Always `automation` for this variant." 194 195 196class SolutionInstallInput(TypedDict, total=False): 197 "Install a Solution" 198 199 acl: SolutionInstallInputAcl | None 200 "Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team." 201 allow_auto_import: bool | None 202 "When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing." 203 details: SolutionInstallInputDetails | None 204 'Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.' 205 install_inputs: dict[str, Any] | None 206 'Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.' 207 lookup_key: str | None 208 "Lookup key override for the provisioned resource (for example, the Agent's `agent_key`)." 209 lookup_key_suffix: str | None 210 "Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions." 211 name: str | None 212 "Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions." 213 org: str | None 214 "Organization ID (`org_...`) for the install destination scope." 215 target: str | None 216 "ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources." 217 team: str | None 218 "Team ID (`team_...`) for the install destination scope." 219 template: str | None 220 "Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly." 221 user: str | None 222 "User ID (`usr_...`) for the install destination scope." 223 224 225class SolutionUpgradeInputSolutionBundleConfigsItem(TypedDict, total=False): 226 content: Required[str] 227 "Full text content of the configuration file." 228 content_type: str | None 229 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 230 relative_path: Required[str] 231 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation." 232 233 234class SolutionUpgradeInputSolutionBundleSetupActionsItem(TypedDict, total=False): 235 depends_on: list[str] | None 236 "List of other setup action identifiers that must be completed before this action becomes actionable." 237 description: str | None 238 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 239 kind: Required[str] 240 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 241 params: dict[str, Any] | None 242 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 243 required: bool | None 244 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 245 sort_order: int | None 246 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 247 title: Required[str] 248 "Short human-readable label displayed in the setup checklist." 249 verify_config: dict[str, Any] | None 250 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.' 251 252 253class SolutionUpgradeInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 254 content: Required[str] 255 "Full text content of the file." 256 content_type: str | None 257 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 258 relative_path: Required[str] 259 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.' 260 261 262class SolutionUpgradeInputSolutionBundleSkillsItem(TypedDict, total=False): 263 content: Required[str] 264 "Full text content of the `SKILL.md` file." 265 content_type: str | None 266 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 267 files: list[SolutionUpgradeInputSolutionBundleSkillsItemFilesItem] | None 268 "Additional files nested inside the skill folder, each with its own path and content." 269 relative_path: Required[str] 270 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).' 271 272 273class SolutionUpgradeInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 274 content: Required[str] 275 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 276 content_type: str | None 277 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 278 data_encoding: str | None 279 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 280 relative_path: Required[str] 281 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)." 282 283 284class SolutionUpgradeInputSolutionBundleSolution(TypedDict, total=False): 285 content: Required[str] 286 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 287 content_type: str | None 288 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 289 files: list[SolutionUpgradeInputSolutionBundleSolutionFilesItem] | None 290 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 291 lookup_key: Required[str] 292 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config." 293 294 295class SolutionUpgradeInputSolutionBundleTemplate(TypedDict, total=False): 296 content: Required[str] 297 "Full text content of the agent template file, typically a YAML document." 298 content_type: str | None 299 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 300 relative_path: Required[str] 301 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 302 303 304class SolutionUpgradeInputSolutionBundleTemplatesItem(TypedDict, total=False): 305 content: Required[str] 306 "Full text content of the agent template file, typically a YAML document." 307 content_type: str | None 308 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 309 relative_path: Required[str] 310 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).' 311 312 313class SolutionUpgradeInputSolutionBundle(TypedDict, total=False): 314 configs: list[SolutionUpgradeInputSolutionBundleConfigsItem] | None 315 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 316 lookup_key_prefix: str | None 317 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 318 lookup_key_suffix: str | None 319 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 320 setup_actions: list[SolutionUpgradeInputSolutionBundleSetupActionsItem] | None 321 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 322 skills: list[SolutionUpgradeInputSolutionBundleSkillsItem] | None 323 "Skill bundles (root config plus supporting files) that this solution depends on." 324 solution: Required[SolutionUpgradeInputSolutionBundleSolution] 325 "The solution config to install, including the solution.yaml body and any referenced component files." 326 template: SolutionUpgradeInputSolutionBundleTemplate | None 327 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 328 templates: list[SolutionUpgradeInputSolutionBundleTemplatesItem] | None 329 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`." 330 331 332class SolutionUpgradeInput(TypedDict, total=False): 333 "Upgrade an installed Solution" 334 335 allow_downgrade: bool | None 336 "When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`." 337 dry_run: bool | None 338 "When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`." 339 expected_review_fingerprint: str | None 340 "Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply." 341 org: str | None 342 "Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this." 343 solution_bundle: SolutionUpgradeInputSolutionBundle | None 344 "Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`." 345 target_solution: str | None 346 "Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`." 347 348 349class SolutionViewInput(TypedDict, total=False): 350 "Track a Solution detail-page view" 351 352 anonymous: str | None 353 "Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session." 354 355 356class AsyncSolutionResource: 357 def __init__(self, http: HttpClient): 358 self._http = http 359 360 async def list( 361 self, 362 *, 363 page: int | None = None, 364 page_size: int | None = None, 365 lookup_key: str | None = None, 366 path_prefix: str | None = None, 367 owners: builtins.list[str] | None = None, 368 ) -> SolutionListResponse: 369 """ 370 List Solutions 371 Returns a paginated list of Solutions visible to the caller, merging two 372 scopes: app-level Solutions (system-owned rows with no org affiliation, 373 visible to everyone including unauthenticated callers so they can power 374 the public catalog) and org-level Solutions (system-owned rows stamped with 375 the viewer's org ID, included when an authenticated viewer carries an org 376 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 377 and therefore only ever see the app-level scope. 378 Solutions that appear under both scopes are deduplicated by their stable 379 `solution_id` value. The merged entry's `owners` array lists every scope the 380 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 381 has a higher `solution_version` than the org-level copy, the response includes 382 `upgrade_available: true` and `latest_version` so callers can prompt for an 383 upgrade. 384 385 Args: 386 page: Page number to return. Defaults to `1`. 387 page_size: Number of Solutions per page. Defaults to `25`. 388 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 389 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 390 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 391 392 Returns: 393 Paginated list of Solution summaries visible to the caller. 394 """ 395 query: dict[str, object] = {} 396 if page is not None: 397 query["page"] = page 398 if page_size is not None: 399 query["page_size"] = page_size 400 if lookup_key is not None: 401 query["lookup_key"] = lookup_key 402 if path_prefix is not None: 403 query["path_prefix"] = path_prefix 404 if owners is not None: 405 query["owners"] = owners 406 return await self._http.request( 407 "/api/v1/solutions", 408 query=query, 409 response_type=SolutionListResponse, 410 ) 411 412 async def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 413 """ 414 Import a Solution into the library 415 Imports a Solution and its bundled configs (skills, scripts, templates, files) 416 into the library for the target scope. Two mutually exclusive import modes 417 are supported: pass `solution` to re-import an existing system-owned catalog 418 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 419 self-contained inline bundle. Exactly one must be present. 420 The operation upserts the bundle in a single transaction. When `dry_run` is 421 `true` the same pipeline runs but the transaction is rolled back no rows are 422 persisted and the response reflects what would have been written. The 423 response shape is the same in both cases: the Solution summary plus 424 `installed_configs` listing each config the import created or would create. 425 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 426 Solution into the library; install provisions a runtime resource (Agent, 427 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 428 429 Args: 430 input: Request body. 431 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 432 input.org: Organization ID (`org_...`) for the import destination scope. 433 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 434 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 435 input.team: Team ID (`team_...`) for the import destination scope. 436 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 437 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 438 439 Returns: 440 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 441 """ 442 return await self._http.request( 443 "/api/v1/solutions", 444 method="POST", 445 body=input, 446 response_type=SolutionImportResponse, 447 ) 448 449 async def delete(self, solution: str) -> None: 450 """ 451 Delete a Solution 452 Permanently deletes an imported Solution and all configs bundled with it, 453 including templates, skills, scripts, and files. The deletion runs in a 454 single transaction; provider-stored blobs are swept asynchronously after commit. 455 Org-scope callers (for example, an org admin in the Library settings page) can 456 only delete their org's copy of the Solution. App-scope callers can delete 457 Solutions at either scope. RBAC is enforced inside the core delete flow. 458 Returns `204 No Content` on success. 459 460 Args: 461 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 462 463 Returns: 464 Empty body. HTTP 204 indicates the Solution was deleted successfully. 465 """ 466 await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 467 468 async def get(self, solution: str) -> SolutionSummary: 469 """ 470 Retrieve a Solution 471 Returns a single Solution identified by its config ID (`cfg_...`) or 472 `lookup_key`, in the same summary shape the list endpoint emits. The response 473 always includes a freshly-minted `readme_url` call this endpoint to refresh 474 an expired README token without making any other state change. 475 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 476 are visible to everyone including unauthenticated callers, so the public 477 catalog can render a Solution's detail page logged-out; org-scoped Solutions 478 are only visible to authenticated viewers whose org context matches. Anything 479 else returns 404. 480 When the resolved Solution is org-scoped, the endpoint compares its 481 `solution_version` against the matching app-level copy. If the app-level copy 482 is at a higher version the response includes `upgrade_available: true` and 483 `latest_version`. App-level resolutions always report `upgrade_available: false`. 484 485 Args: 486 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 487 488 Returns: 489 Solution summary including a freshly-minted `readme_url` token valid for one hour. 490 """ 491 return await self._http.request( 492 f"/api/v1/solutions/{solution}", 493 response_type=SolutionSummary, 494 ) 495 496 async def dependents(self, solution: str) -> SolutionDependentsResponse: 497 """ 498 Preview Solution delete impact 499 Returns a read-only preview of what deleting the specified Solution would 500 affect: the agents that reference the Solution's bundle, and the count of 501 bundled configs that would be orphaned rather than cascade-deleted. 502 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 503 surface a warning when live agents depend on the Solution. 504 Visibility scope mirrors the delete endpoint: org-scope viewers see their 505 org's copy; app-scope viewers can inspect either scope. 506 507 Args: 508 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 509 510 Returns: 511 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 512 """ 513 return await self._http.request( 514 f"/api/v1/solutions/{solution}/dependents", 515 response_type=SolutionDependentsResponse, 516 ) 517 518 async def image( 519 self, solution: str, token: str, *, v: str | None = None, file: str | None = None 520 ) -> dict[str, str]: 521 """ 522 Fetch a Solution cover image or gallery screenshot 523 Returns the raw bytes of a Solution's cover image the bundled asset the 524 Solution body's `image:` field names or, with the optional `file` param, 525 one of its gallery screenshots (`screenshots:` entries). This endpoint backs 526 the `image_url` and `screenshot_urls` fields of catalog payloads (such as 527 `GET /api/v1/solutions`), which anonymous consumers the public 528 marketplace's page cache, OpenGraph scrapers may hold far longer than a 529 signed storage URL lives. Authorization is performed via a short, stable 530 capability `token` rather than an HTTP header, so the URL never expires. 531 The `token` is an HMAC-based capability tied to the Solution config ID. It 532 does not expire, but the endpoint checks at fetch time that the Solution 533 still declares the requested image without `file`, that it still declares 534 a cover; with `file`, that the path is still among the body's declared 535 `image`/`screenshots` so republishing without the asset (or hiding the 536 Solution) turns the URL into a 404. Shared caches may continue serving the 537 old image until the `Cache-Control` max-age of one hour elapses. `v` is an 538 opaque cache key minted alongside the token; it changes when the Solution 539 changes and is ignored by verification. 540 All failure modes invalid config ID, invalid token, hidden Solution, no 541 declared cover, a `file` outside the declared set, or an asset that doesn't 542 resolve to a bundled image return a uniform `404` to avoid acting as an 543 existence oracle. 544 545 Args: 546 solution: Solution config ID (`cfg_...`) whose cover image to fetch. 547 token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized. 548 v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification. 549 file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served. 550 551 Returns: 552 Raw cover image bytes, served with the asset's image content type. 553 """ 554 query: dict[str, object] = {} 555 query["token"] = token 556 if v is not None: 557 query["v"] = v 558 if file is not None: 559 query["file"] = file 560 return await self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query) 561 562 async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 563 """ 564 Install a Solution 565 Provisions a runtime resource from an already-imported Solution. The type of 566 resource created depends on the template the Solution wraps: an 567 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 568 Automation, and attachment templates (`AgentRoutineTemplate`, 569 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 570 sub-resource to an existing Agent specified by `target`. 571 For Solutions that bundle more than one template, pass `template` (the ID or 572 `lookup_key` of the desired template) to select which one to provision. 573 Single-template Solutions do not require `template`. 574 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 575 the library; install provisions a runtime resource from it. 576 577 Args: 578 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 579 input: Request body. 580 input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team. 581 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 582 input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`. 583 input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`. 584 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 585 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 586 input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions. 587 input.org: Organization ID (`org_...`) for the install destination scope. 588 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 589 input.team: Team ID (`team_...`) for the install destination scope. 590 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 591 input.user: User ID (`usr_...`) for the install destination scope. 592 593 Returns: 594 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 595 """ 596 return await self._http.request( 597 f"/api/v1/solutions/{solution}/install", 598 method="POST", 599 body=input, 600 response_type=SolutionInstallResponse, 601 ) 602 603 async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 604 """ 605 Retrieve a Solution README or asset 606 Serves the README markdown or a bundled asset for an imported Solution. Both 607 modes use the same path and require a short-lived signed `token` in the query 608 string rather than an `Authorization` header, so browsers can load asset URLs 609 directly from `<img src>` attributes without custom request logic. 610 When `file` is omitted the response is the Solution's `readme` field rendered 611 as `text/markdown`. All local asset references in the markdown are rewritten to 612 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 613 images inline without additional authentication. 614 When `file` is set the response is the raw bytes of the matching asset (a File 615 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 616 name) with the asset's stored `Content-Type`. 617 Tokens are scoped to a single Solution, carry the viewer's app, org, and 618 sandbox context from the time they were minted, and expire after one hour. 619 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 620 always returns a newly minted `readme_url`. 621 622 Args: 623 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 624 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 625 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 626 627 Returns: 628 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 629 """ 630 query: dict[str, object] = {} 631 query["token"] = token 632 if file is not None: 633 query["file"] = file 634 return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 635 636 async def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse: 637 """ 638 Reconcile Customer Management product knowledge 639 Recomputes every stamped customer's knowledge-search source allowlist 640 from the template-resolved customer refs plus the install's current marked 641 product-knowledge sources. Safe to retry; partial failures are returned by 642 customer key rather than hidden behind an aggregate success. 643 644 Args: 645 solution: Installed Customer Management Solution config (`cfg_...`) to reconcile. 646 647 Returns: 648 Per-customer convergence result for this installed solution. 649 """ 650 return await self._http.request( 651 f"/api/v1/solutions/{solution}/reconcile_product_knowledge", 652 method="POST", 653 response_type=ProductKnowledgeReconcileResponse, 654 ) 655 656 async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 657 """ 658 Upgrade an installed Solution 659 Applies an incoming bundle to an already-installed Solution in a single atomic 660 transaction, bringing its configs in line with the new bundle. Config IDs are 661 preserved across the upgrade. Configs that existed in the old bundle but are 662 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 663 Two mutually exclusive source modes: pass `target_solution` to pull the 664 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 665 `solution_bundle` to supply a complete inline bundle directly. Exactly one 666 must be present. 667 When `dry_run` is `true` the full diff is computed and returned but no 668 changes are written. Pass the dry-run response's `review_fingerprint` as 669 `expected_review_fingerprint` when applying to guard against the bundle 670 changing between review and apply. 671 672 Args: 673 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 674 input: Request body. 675 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 676 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 677 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 678 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 679 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 680 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 681 682 Returns: 683 Updated Solution plus the full upgrade diff. 684 """ 685 return await self._http.request( 686 f"/api/v1/solutions/{solution}/upgrade", 687 method="POST", 688 body=input, 689 response_type=SolutionUpgradeResponse, 690 ) 691 692 async def view(self, solution: str, input: SolutionViewInput) -> None: 693 """ 694 Track a Solution detail-page view 695 Records a `solution_viewed` analytics event for the identified Solution and 696 returns `204 No Content`. Fired by the marketplace when a Solution's detail 697 page is rendered in a browser, so publishers can see impressions alongside 698 installs in their Solution analytics. 699 Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers 700 (the logged-out marketplace) can only track Solutions published to the public 701 catalog; anything the caller could not retrieve returns 404 and records 702 nothing. 703 Pass `anonymous` (the analytics visitor ID) so logged-out views can be 704 counted as unique viewers. The event's Solution and publisher attribution are 705 resolved server-side from the Solution row never from request input. 706 707 Args: 708 solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution. 709 input: Request body. 710 input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session. 711 712 Returns: 713 Empty body. HTTP 204 indicates the Solution view was recorded successfully. 714 """ 715 await self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input) 716 717 718class SolutionResource: 719 def __init__(self, http: SyncHttpClient): 720 self._http = http 721 722 def list( 723 self, 724 *, 725 page: int | None = None, 726 page_size: int | None = None, 727 lookup_key: str | None = None, 728 path_prefix: str | None = None, 729 owners: builtins.list[str] | None = None, 730 ) -> SolutionListResponse: 731 """ 732 List Solutions 733 Returns a paginated list of Solutions visible to the caller, merging two 734 scopes: app-level Solutions (system-owned rows with no org affiliation, 735 visible to everyone including unauthenticated callers so they can power 736 the public catalog) and org-level Solutions (system-owned rows stamped with 737 the viewer's org ID, included when an authenticated viewer carries an org 738 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 739 and therefore only ever see the app-level scope. 740 Solutions that appear under both scopes are deduplicated by their stable 741 `solution_id` value. The merged entry's `owners` array lists every scope the 742 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 743 has a higher `solution_version` than the org-level copy, the response includes 744 `upgrade_available: true` and `latest_version` so callers can prompt for an 745 upgrade. 746 747 Args: 748 page: Page number to return. Defaults to `1`. 749 page_size: Number of Solutions per page. Defaults to `25`. 750 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 751 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 752 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 753 754 Returns: 755 Paginated list of Solution summaries visible to the caller. 756 """ 757 query: dict[str, object] = {} 758 if page is not None: 759 query["page"] = page 760 if page_size is not None: 761 query["page_size"] = page_size 762 if lookup_key is not None: 763 query["lookup_key"] = lookup_key 764 if path_prefix is not None: 765 query["path_prefix"] = path_prefix 766 if owners is not None: 767 query["owners"] = owners 768 return self._http.request( 769 "/api/v1/solutions", 770 query=query, 771 response_type=SolutionListResponse, 772 ) 773 774 def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 775 """ 776 Import a Solution into the library 777 Imports a Solution and its bundled configs (skills, scripts, templates, files) 778 into the library for the target scope. Two mutually exclusive import modes 779 are supported: pass `solution` to re-import an existing system-owned catalog 780 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 781 self-contained inline bundle. Exactly one must be present. 782 The operation upserts the bundle in a single transaction. When `dry_run` is 783 `true` the same pipeline runs but the transaction is rolled back no rows are 784 persisted and the response reflects what would have been written. The 785 response shape is the same in both cases: the Solution summary plus 786 `installed_configs` listing each config the import created or would create. 787 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 788 Solution into the library; install provisions a runtime resource (Agent, 789 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 790 791 Args: 792 input: Request body. 793 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 794 input.org: Organization ID (`org_...`) for the import destination scope. 795 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 796 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 797 input.team: Team ID (`team_...`) for the import destination scope. 798 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 799 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 800 801 Returns: 802 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 803 """ 804 return self._http.request( 805 "/api/v1/solutions", 806 method="POST", 807 body=input, 808 response_type=SolutionImportResponse, 809 ) 810 811 def delete(self, solution: str) -> None: 812 """ 813 Delete a Solution 814 Permanently deletes an imported Solution and all configs bundled with it, 815 including templates, skills, scripts, and files. The deletion runs in a 816 single transaction; provider-stored blobs are swept asynchronously after commit. 817 Org-scope callers (for example, an org admin in the Library settings page) can 818 only delete their org's copy of the Solution. App-scope callers can delete 819 Solutions at either scope. RBAC is enforced inside the core delete flow. 820 Returns `204 No Content` on success. 821 822 Args: 823 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 824 825 Returns: 826 Empty body. HTTP 204 indicates the Solution was deleted successfully. 827 """ 828 self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 829 830 def get(self, solution: str) -> SolutionSummary: 831 """ 832 Retrieve a Solution 833 Returns a single Solution identified by its config ID (`cfg_...`) or 834 `lookup_key`, in the same summary shape the list endpoint emits. The response 835 always includes a freshly-minted `readme_url` call this endpoint to refresh 836 an expired README token without making any other state change. 837 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 838 are visible to everyone including unauthenticated callers, so the public 839 catalog can render a Solution's detail page logged-out; org-scoped Solutions 840 are only visible to authenticated viewers whose org context matches. Anything 841 else returns 404. 842 When the resolved Solution is org-scoped, the endpoint compares its 843 `solution_version` against the matching app-level copy. If the app-level copy 844 is at a higher version the response includes `upgrade_available: true` and 845 `latest_version`. App-level resolutions always report `upgrade_available: false`. 846 847 Args: 848 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 849 850 Returns: 851 Solution summary including a freshly-minted `readme_url` token valid for one hour. 852 """ 853 return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary) 854 855 def dependents(self, solution: str) -> SolutionDependentsResponse: 856 """ 857 Preview Solution delete impact 858 Returns a read-only preview of what deleting the specified Solution would 859 affect: the agents that reference the Solution's bundle, and the count of 860 bundled configs that would be orphaned rather than cascade-deleted. 861 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 862 surface a warning when live agents depend on the Solution. 863 Visibility scope mirrors the delete endpoint: org-scope viewers see their 864 org's copy; app-scope viewers can inspect either scope. 865 866 Args: 867 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 868 869 Returns: 870 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 871 """ 872 return self._http.request( 873 f"/api/v1/solutions/{solution}/dependents", 874 response_type=SolutionDependentsResponse, 875 ) 876 877 def image( 878 self, solution: str, token: str, *, v: str | None = None, file: str | None = None 879 ) -> dict[str, str]: 880 """ 881 Fetch a Solution cover image or gallery screenshot 882 Returns the raw bytes of a Solution's cover image the bundled asset the 883 Solution body's `image:` field names or, with the optional `file` param, 884 one of its gallery screenshots (`screenshots:` entries). This endpoint backs 885 the `image_url` and `screenshot_urls` fields of catalog payloads (such as 886 `GET /api/v1/solutions`), which anonymous consumers the public 887 marketplace's page cache, OpenGraph scrapers may hold far longer than a 888 signed storage URL lives. Authorization is performed via a short, stable 889 capability `token` rather than an HTTP header, so the URL never expires. 890 The `token` is an HMAC-based capability tied to the Solution config ID. It 891 does not expire, but the endpoint checks at fetch time that the Solution 892 still declares the requested image without `file`, that it still declares 893 a cover; with `file`, that the path is still among the body's declared 894 `image`/`screenshots` so republishing without the asset (or hiding the 895 Solution) turns the URL into a 404. Shared caches may continue serving the 896 old image until the `Cache-Control` max-age of one hour elapses. `v` is an 897 opaque cache key minted alongside the token; it changes when the Solution 898 changes and is ignored by verification. 899 All failure modes invalid config ID, invalid token, hidden Solution, no 900 declared cover, a `file` outside the declared set, or an asset that doesn't 901 resolve to a bundled image return a uniform `404` to avoid acting as an 902 existence oracle. 903 904 Args: 905 solution: Solution config ID (`cfg_...`) whose cover image to fetch. 906 token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized. 907 v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification. 908 file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served. 909 910 Returns: 911 Raw cover image bytes, served with the asset's image content type. 912 """ 913 query: dict[str, object] = {} 914 query["token"] = token 915 if v is not None: 916 query["v"] = v 917 if file is not None: 918 query["file"] = file 919 return self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query) 920 921 def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 922 """ 923 Install a Solution 924 Provisions a runtime resource from an already-imported Solution. The type of 925 resource created depends on the template the Solution wraps: an 926 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 927 Automation, and attachment templates (`AgentRoutineTemplate`, 928 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 929 sub-resource to an existing Agent specified by `target`. 930 For Solutions that bundle more than one template, pass `template` (the ID or 931 `lookup_key` of the desired template) to select which one to provision. 932 Single-template Solutions do not require `template`. 933 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 934 the library; install provisions a runtime resource from it. 935 936 Args: 937 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 938 input: Request body. 939 input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team. 940 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 941 input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`. 942 input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`. 943 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 944 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 945 input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions. 946 input.org: Organization ID (`org_...`) for the install destination scope. 947 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 948 input.team: Team ID (`team_...`) for the install destination scope. 949 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 950 input.user: User ID (`usr_...`) for the install destination scope. 951 952 Returns: 953 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 954 """ 955 return self._http.request( 956 f"/api/v1/solutions/{solution}/install", 957 method="POST", 958 body=input, 959 response_type=SolutionInstallResponse, 960 ) 961 962 def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 963 """ 964 Retrieve a Solution README or asset 965 Serves the README markdown or a bundled asset for an imported Solution. Both 966 modes use the same path and require a short-lived signed `token` in the query 967 string rather than an `Authorization` header, so browsers can load asset URLs 968 directly from `<img src>` attributes without custom request logic. 969 When `file` is omitted the response is the Solution's `readme` field rendered 970 as `text/markdown`. All local asset references in the markdown are rewritten to 971 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 972 images inline without additional authentication. 973 When `file` is set the response is the raw bytes of the matching asset (a File 974 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 975 name) with the asset's stored `Content-Type`. 976 Tokens are scoped to a single Solution, carry the viewer's app, org, and 977 sandbox context from the time they were minted, and expire after one hour. 978 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 979 always returns a newly minted `readme_url`. 980 981 Args: 982 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 983 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 984 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 985 986 Returns: 987 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 988 """ 989 query: dict[str, object] = {} 990 query["token"] = token 991 if file is not None: 992 query["file"] = file 993 return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 994 995 def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse: 996 """ 997 Reconcile Customer Management product knowledge 998 Recomputes every stamped customer's knowledge-search source allowlist 999 from the template-resolved customer refs plus the install's current marked 1000 product-knowledge sources. Safe to retry; partial failures are returned by 1001 customer key rather than hidden behind an aggregate success. 1002 1003 Args: 1004 solution: Installed Customer Management Solution config (`cfg_...`) to reconcile. 1005 1006 Returns: 1007 Per-customer convergence result for this installed solution. 1008 """ 1009 return self._http.request( 1010 f"/api/v1/solutions/{solution}/reconcile_product_knowledge", 1011 method="POST", 1012 response_type=ProductKnowledgeReconcileResponse, 1013 ) 1014 1015 def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 1016 """ 1017 Upgrade an installed Solution 1018 Applies an incoming bundle to an already-installed Solution in a single atomic 1019 transaction, bringing its configs in line with the new bundle. Config IDs are 1020 preserved across the upgrade. Configs that existed in the old bundle but are 1021 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 1022 Two mutually exclusive source modes: pass `target_solution` to pull the 1023 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 1024 `solution_bundle` to supply a complete inline bundle directly. Exactly one 1025 must be present. 1026 When `dry_run` is `true` the full diff is computed and returned but no 1027 changes are written. Pass the dry-run response's `review_fingerprint` as 1028 `expected_review_fingerprint` when applying to guard against the bundle 1029 changing between review and apply. 1030 1031 Args: 1032 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 1033 input: Request body. 1034 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 1035 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 1036 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 1037 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 1038 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 1039 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 1040 1041 Returns: 1042 Updated Solution plus the full upgrade diff. 1043 """ 1044 return self._http.request( 1045 f"/api/v1/solutions/{solution}/upgrade", 1046 method="POST", 1047 body=input, 1048 response_type=SolutionUpgradeResponse, 1049 ) 1050 1051 def view(self, solution: str, input: SolutionViewInput) -> None: 1052 """ 1053 Track a Solution detail-page view 1054 Records a `solution_viewed` analytics event for the identified Solution and 1055 returns `204 No Content`. Fired by the marketplace when a Solution's detail 1056 page is rendered in a browser, so publishers can see impressions alongside 1057 installs in their Solution analytics. 1058 Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers 1059 (the logged-out marketplace) can only track Solutions published to the public 1060 catalog; anything the caller could not retrieve returns 404 and records 1061 nothing. 1062 Pass `anonymous` (the analytics visitor ID) so logged-out views can be 1063 counted as unique viewers. The event's Solution and publisher attribution are 1064 resolved server-side from the Solution row never from request input. 1065 1066 Args: 1067 solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution. 1068 input: Request body. 1069 input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session. 1070 1071 Returns: 1072 Empty body. HTTP 204 indicates the Solution view was recorded successfully. 1073 """ 1074 self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
23class SolutionCreateInputSolutionBundleConfigsItem(TypedDict, total=False): 24 content: Required[str] 25 "Full text content of the configuration file." 26 content_type: str | None 27 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 28 relative_path: Required[str] 29 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation."
32class SolutionCreateInputSolutionBundleSetupActionsItem(TypedDict, total=False): 33 depends_on: list[str] | None 34 "List of other setup action identifiers that must be completed before this action becomes actionable." 35 description: str | None 36 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 37 kind: Required[str] 38 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 39 params: dict[str, Any] | None 40 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 41 required: bool | None 42 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 43 sort_order: int | None 44 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 45 title: Required[str] 46 "Short human-readable label displayed in the setup checklist." 47 verify_config: dict[str, Any] | None 48 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.'
List of other setup action identifiers that must be completed before this action becomes actionable.
Markdown-formatted instructions or context shown beneath the checklist item. null if not provided.
Category of setup step. One of "env_var" (configure an environment variable), "install" (complete an installation step), "custom" (a user-defined action), or "integration" (authorize an OAuth-backed MCP server integration).
Kind-specific configuration for the action. For "env_var" steps this typically includes key and scope; for "install" steps it includes installation_kind; for "integration" steps it includes mcp_server_ref. Shape varies by kind.
When true, this action must be completed before the checklist progress bar reaches 100%. Defaults to true.
51class SolutionCreateInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 52 content: Required[str] 53 "Full text content of the file." 54 content_type: str | None 55 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 56 relative_path: Required[str] 57 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.'
60class SolutionCreateInputSolutionBundleSkillsItem(TypedDict, total=False): 61 content: Required[str] 62 "Full text content of the `SKILL.md` file." 63 content_type: str | None 64 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 65 files: list[SolutionCreateInputSolutionBundleSkillsItemFilesItem] | None 66 "Additional files nested inside the skill folder, each with its own path and content." 67 relative_path: Required[str] 68 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).'
Additional files nested inside the skill folder, each with its own path and content.
71class SolutionCreateInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 72 content: Required[str] 73 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 74 content_type: str | None 75 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 76 data_encoding: str | None 77 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 78 relative_path: Required[str] 79 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)."
Raw content of the file. When data_encoding is "base64", this must be a valid base64-encoded string.
MIME type of the file. Defaults to a value inferred from the file extension when omitted.
Encoding of content. "raw" (default) stores the value verbatim. "base64" decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.
82class SolutionCreateInputSolutionBundleSolution(TypedDict, total=False): 83 content: Required[str] 84 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 85 content_type: str | None 86 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 87 files: list[SolutionCreateInputSolutionBundleSolutionFilesItem] | None 88 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 89 lookup_key: Required[str] 90 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config."
Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations.
MIME type of content. Defaults to application/x-yaml; pass application/json when submitting JSON.
Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via path:// URIs. Each entry is persisted as a child file record.
93class SolutionCreateInputSolutionBundleTemplate(TypedDict, total=False): 94 content: Required[str] 95 "Full text content of the agent template file, typically a YAML document." 96 content_type: str | None 97 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 98 relative_path: Required[str] 99 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
102class SolutionCreateInputSolutionBundleTemplatesItem(TypedDict, total=False): 103 content: Required[str] 104 "Full text content of the agent template file, typically a YAML document." 105 content_type: str | None 106 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 107 relative_path: Required[str] 108 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
111class SolutionCreateInputSolutionBundle(TypedDict, total=False): 112 configs: list[SolutionCreateInputSolutionBundleConfigsItem] | None 113 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 114 lookup_key_prefix: str | None 115 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 116 lookup_key_suffix: str | None 117 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 118 setup_actions: list[SolutionCreateInputSolutionBundleSetupActionsItem] | None 119 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 120 skills: list[SolutionCreateInputSolutionBundleSkillsItem] | None 121 "Skill bundles (root config plus supporting files) that this solution depends on." 122 solution: Required[SolutionCreateInputSolutionBundleSolution] 123 "The solution config to install, including the solution.yaml body and any referenced component files." 124 template: SolutionCreateInputSolutionBundleTemplate | None 125 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 126 templates: list[SolutionCreateInputSolutionBundleTemplatesItem] | None 127 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`."
Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install.
String prepended (with a - separator) to every uploaded config's lookup_key and every path:// reference in the solution body. Typical value is solutions-<install-id>.
String appended to every uploaded config's lookup_key and every path:// reference in the solution body. Should be stable for a given install and unique per attempt.
Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own setup_actions. Omit when bundling multiple templates.
Skill bundles (root config plus supporting files) that this solution depends on.
The solution config to install, including the solution.yaml body and any referenced component files.
Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via template_path:. Mutually exclusive with template.
130class SolutionCreateInput(TypedDict, total=False): 131 "Import a Solution into the library" 132 133 dry_run: bool | None 134 "When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`." 135 org: str | None 136 "Organization ID (`org_...`) for the import destination scope." 137 solution: str | None 138 "Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`." 139 solution_bundle: SolutionCreateInputSolutionBundle | None 140 "Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`." 141 team: str | None 142 "Team ID (`team_...`) for the import destination scope." 143 user: str | None 144 "User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set." 145 virtual_path_prefix: str | None 146 "Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix."
Import a Solution into the library
When true, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to false.
Config ID (cfg_...) or lookup_key of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with solution_bundle.
Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with solution.
149class SolutionInstallInputAclAddItem(TypedDict, total=False): 150 actions: Required[list[str]] 151 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' 152 principal: str | None 153 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' 154 principal_type: Required[str] 155 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.
The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".
158class SolutionInstallInputAclGrantsItem(TypedDict, total=False): 159 actions: Required[list[str]] 160 'Array of action strings the principal is permitted to perform, e.g. `["read", "write"]`. Must contain at least one entry.' 161 principal: str | None 162 'The identifier of the principal. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`; omit entirely when `principal_type` is `"everyone"`.' 163 principal_type: Required[str] 164 'The kind of principal receiving the grant. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
Array of action strings the principal is permitted to perform, e.g. ["read", "write"]. Must contain at least one entry.
The identifier of the principal. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role"; omit entirely when principal_type is "everyone".
167class SolutionInstallInputAclRemoveItem(TypedDict, total=False): 168 principal: str | None 169 'The identifier of the principal to remove. A string ID for `"user"`, `"team"`, `"org"`, and `"agent"` types; one of `"admin"`, `"member"`, or `"viewer"` for `"org_role"`. Omit when `principal_type` is `"everyone"`.' 170 principal_type: Required[str] 171 'The kind of principal to remove. One of `"user"`, `"team"`, `"org"`, `"org_role"`, `"agent"`, or `"everyone"`.'
The identifier of the principal to remove. A string ID for "user", "team", "org", and "agent" types; one of "admin", "member", or "viewer" for "org_role". Omit when principal_type is "everyone".
174class SolutionInstallInputAcl(TypedDict, total=False): 175 add: list[SolutionInstallInputAclAddItem] | None 176 "Patch mode: grants to add or merge into the existing list. Cannot be combined with `grants`." 177 grants: list[SolutionInstallInputAclGrantsItem] | None 178 "Replace mode: the complete new list of grants that replaces all existing entries. Send an empty array (`[]`) to clear all grants. Cannot be combined with `add` or `remove`." 179 remove: list[SolutionInstallInputAclRemoveItem] | None 180 "Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with `grants`."
Patch mode: grants to add or merge into the existing list. Cannot be combined with grants.
Patch mode: principals whose grants should be removed from the existing list. Cannot be combined with grants.
183class SolutionInstallInputDetailsPrefills(TypedDict, total=False): 184 participants: dict[str, Any] | None 185 "Participant slot-to-agent mappings applied by the platform. Caller values at these slots must match exactly." 186 payload: dict[str, Any] | None 187 "Partial invocation payload applied by the platform. A caller may omit these values, but supplying a different value at any locked path is rejected."
190class SolutionInstallInputDetails(TypedDict, total=False): 191 prefills: SolutionInstallInputDetailsPrefills | None 192 "Instance-specific locked payload and participant values. Payload paths and participant slots are validated against the installed template's resolved input schema and workflow." 193 type: Required[Literal["automation"]] 194 "Install-details discriminator. Always `automation` for this variant."
Instance-specific locked payload and participant values. Payload paths and participant slots are validated against the installed template's resolved input schema and workflow.
197class SolutionInstallInput(TypedDict, total=False): 198 "Install a Solution" 199 200 acl: SolutionInstallInputAcl | None 201 "Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team." 202 allow_auto_import: bool | None 203 "When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing." 204 details: SolutionInstallInputDetails | None 205 'Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`.' 206 install_inputs: dict[str, Any] | None 207 'Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`.' 208 lookup_key: str | None 209 "Lookup key override for the provisioned resource (for example, the Agent's `agent_key`)." 210 lookup_key_suffix: str | None 211 "Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions." 212 name: str | None 213 "Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions." 214 org: str | None 215 "Organization ID (`org_...`) for the install destination scope." 216 target: str | None 217 "ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources." 218 team: str | None 219 "Team ID (`team_...`) for the install destination scope." 220 template: str | None 221 "Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly." 222 user: str | None 223 "User ID (`usr_...`) for the install destination scope."
Install a Solution
Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
When true, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit org param. Defaults to false; without it the endpoint returns 404 when the org-scoped Solution is missing.
Template-specific install options selected by the type discriminator. AutomationTemplate installs accept {type: "automation", prefills: ...}.
Values applied to parameterized AgentTemplate prose during a root Agent install. Use {values: {customer_label: "Acme"}}.
Lookup key override for the provisioned resource (for example, the Agent's agent_key).
Suffix appended to every config_ref: resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions.
Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
ID or lookup_key of the parent Agent to attach to. Required when installing an AgentRoutineTemplate, AgentToolTemplate, AgentSkillTemplate, or AgentComputerTemplate Solution, since those produce sub-resources attached to an existing Agent. Omit for AgentTemplate and AutomationTemplate Solutions, which provision standalone resources.
Config ID (cfg_...) or lookup_key of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly.
226class SolutionUpgradeInputSolutionBundleConfigsItem(TypedDict, total=False): 227 content: Required[str] 228 "Full text content of the configuration file." 229 content_type: str | None 230 'MIME type of the configuration content, e.g. `"application/x-yaml"` or `"application/json"`. `null` if not specified.' 231 relative_path: Required[str] 232 "Bundle-relative path to this config file. The path determines the config kind and its storage identity within the installation."
235class SolutionUpgradeInputSolutionBundleSetupActionsItem(TypedDict, total=False): 236 depends_on: list[str] | None 237 "List of other setup action identifiers that must be completed before this action becomes actionable." 238 description: str | None 239 "Markdown-formatted instructions or context shown beneath the checklist item. `null` if not provided." 240 kind: Required[str] 241 'Category of setup step. One of `"env_var"` (configure an environment variable), `"install"` (complete an installation step), `"custom"` (a user-defined action), or `"integration"` (authorize an OAuth-backed MCP server integration).' 242 params: dict[str, Any] | None 243 'Kind-specific configuration for the action. For `"env_var"` steps this typically includes `key` and `scope`; for `"install"` steps it includes `installation_kind`; for `"integration"` steps it includes `mcp_server_ref`. Shape varies by `kind`.' 244 required: bool | None 245 "When `true`, this action must be completed before the checklist progress bar reaches 100%. Defaults to `true`." 246 sort_order: int | None 247 "Numeric sort position controlling the display order of this action in the checklist. Defaults to `0` when not specified." 248 title: Required[str] 249 "Short human-readable label displayed in the setup checklist." 250 verify_config: dict[str, Any] | None 251 'Configuration passed to the runtime verifier to determine whether the action has been completed, e.g. `{"type": "secret_present"}`. `null` if no automated verification is configured.'
List of other setup action identifiers that must be completed before this action becomes actionable.
Markdown-formatted instructions or context shown beneath the checklist item. null if not provided.
Category of setup step. One of "env_var" (configure an environment variable), "install" (complete an installation step), "custom" (a user-defined action), or "integration" (authorize an OAuth-backed MCP server integration).
Kind-specific configuration for the action. For "env_var" steps this typically includes key and scope; for "install" steps it includes installation_kind; for "integration" steps it includes mcp_server_ref. Shape varies by kind.
When true, this action must be completed before the checklist progress bar reaches 100%. Defaults to true.
254class SolutionUpgradeInputSolutionBundleSkillsItemFilesItem(TypedDict, total=False): 255 content: Required[str] 256 "Full text content of the file." 257 content_type: str | None 258 "MIME type of the file content. Defaults to a value inferred from the file extension when omitted." 259 relative_path: Required[str] 260 'Path of this file relative to the skill folder root, e.g. `"skills/my-skill/helpers.md"`.'
263class SolutionUpgradeInputSolutionBundleSkillsItem(TypedDict, total=False): 264 content: Required[str] 265 "Full text content of the `SKILL.md` file." 266 content_type: str | None 267 "MIME type of the `SKILL.md` content. Defaults to `text/markdown` when omitted." 268 files: list[SolutionUpgradeInputSolutionBundleSkillsItemFilesItem] | None 269 "Additional files nested inside the skill folder, each with its own path and content." 270 relative_path: Required[str] 271 'Bundle-relative path to the skill root, which must end in `/SKILL.md` (e.g. `"skills/my-skill/SKILL.md"`).'
Additional files nested inside the skill folder, each with its own path and content.
274class SolutionUpgradeInputSolutionBundleSolutionFilesItem(TypedDict, total=False): 275 content: Required[str] 276 'Raw content of the file. When `data_encoding` is `"base64"`, this must be a valid base64-encoded string.' 277 content_type: str | None 278 "MIME type of the file. Defaults to a value inferred from the file extension when omitted." 279 data_encoding: str | None 280 'Encoding of `content`. `"raw"` (default) stores the value verbatim. `"base64"` decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.' 281 relative_path: Required[str] 282 "Path of this file relative to the solution root (e.g. `README.md`, `assets/diagram.png`)."
Raw content of the file. When data_encoding is "base64", this must be a valid base64-encoded string.
MIME type of the file. Defaults to a value inferred from the file extension when omitted.
Encoding of content. "raw" (default) stores the value verbatim. "base64" decodes the value server-side before storage use this to ship binary assets (PDFs, images) through a JSON body.
285class SolutionUpgradeInputSolutionBundleSolution(TypedDict, total=False): 286 content: Required[str] 287 "Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations." 288 content_type: str | None 289 "MIME type of `content`. Defaults to `application/x-yaml`; pass `application/json` when submitting JSON." 290 files: list[SolutionUpgradeInputSolutionBundleSolutionFilesItem] | None 291 "Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via `path://` URIs. Each entry is persisted as a child file record." 292 lookup_key: Required[str] 293 "Stable lookup key for this solution. A suffix is appended at install time to namespace the stored config."
Raw solution.yaml body (YAML or JSON). Describes the solution structure, template references, and asset declarations.
MIME type of content. Defaults to application/x-yaml; pass application/json when submitting JSON.
Component files (READMEs, diagrams, fixtures) referenced by the solution.yaml via path:// URIs. Each entry is persisted as a child file record.
296class SolutionUpgradeInputSolutionBundleTemplate(TypedDict, total=False): 297 content: Required[str] 298 "Full text content of the agent template file, typically a YAML document." 299 content_type: str | None 300 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 301 relative_path: Required[str] 302 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
305class SolutionUpgradeInputSolutionBundleTemplatesItem(TypedDict, total=False): 306 content: Required[str] 307 "Full text content of the agent template file, typically a YAML document." 308 content_type: str | None 309 "MIME type of the template content. Defaults to `application/x-yaml` when omitted." 310 relative_path: Required[str] 311 'Bundle-relative path to the template file, used to derive its storage identity (e.g. `"agent.yaml"`).'
314class SolutionUpgradeInputSolutionBundle(TypedDict, total=False): 315 configs: list[SolutionUpgradeInputSolutionBundleConfigsItem] | None 316 "Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install." 317 lookup_key_prefix: str | None 318 "String prepended (with a `-` separator) to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Typical value is `solutions-<install-id>`." 319 lookup_key_suffix: str | None 320 "String appended to every uploaded config's `lookup_key` and every `path://` reference in the solution body. Should be stable for a given install and unique per attempt." 321 setup_actions: list[SolutionUpgradeInputSolutionBundleSetupActionsItem] | None 322 "Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own `setup_actions`. Omit when bundling multiple templates." 323 skills: list[SolutionUpgradeInputSolutionBundleSkillsItem] | None 324 "Skill bundles (root config plus supporting files) that this solution depends on." 325 solution: Required[SolutionUpgradeInputSolutionBundleSolution] 326 "The solution config to install, including the solution.yaml body and any referenced component files." 327 template: SolutionUpgradeInputSolutionBundleTemplate | None 328 "Convenience shorthand for supplying a single template. Equivalent to setting `templates: [template]`. Mutually exclusive with `templates`. Use `templates` directly when bundling multiple sibling templates." 329 templates: list[SolutionUpgradeInputSolutionBundleTemplatesItem] | None 330 "Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via `template_path:`. Mutually exclusive with `template`."
Additional configs of any kind that the solution.yaml references and that should be upserted as part of this install.
String prepended (with a - separator) to every uploaded config's lookup_key and every path:// reference in the solution body. Typical value is solutions-<install-id>.
String appended to every uploaded config's lookup_key and every path:// reference in the solution body. Should be stable for a given install and unique per attempt.
Post-install setup checklist items for the wrapped template. Allowed only when the bundle contains a single template and that template's body does not already declare its own setup_actions. Omit when bundling multiple templates.
Skill bundles (root config plus supporting files) that this solution depends on.
The solution config to install, including the solution.yaml body and any referenced component files.
Ordered list of templates the solution wraps. The first entry is the deployable template; additional entries are sibling templates it references via template_path:. Mutually exclusive with template.
333class SolutionUpgradeInput(TypedDict, total=False): 334 "Upgrade an installed Solution" 335 336 allow_downgrade: bool | None 337 "When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`." 338 dry_run: bool | None 339 "When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`." 340 expected_review_fingerprint: str | None 341 "Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply." 342 org: str | None 343 "Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this." 344 solution_bundle: SolutionUpgradeInputSolutionBundle | None 345 "Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`." 346 target_solution: str | None 347 "Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`."
Upgrade an installed Solution
When true, permits an incoming solution_version lower than the currently installed version. Defaults to false.
When true, computes and returns the full upgrade diff without persisting any changes. Defaults to false.
Optional stale-review guard. Pass the review_fingerprint returned by a prior dry_run call to ensure the bundle has not changed between review and apply.
Organization ID (org_...) used to resolve org-scoped lookup_key values. Config IDs (cfg_...) are globally unique and do not require this.
Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with target_solution.
Config ID (cfg_...) or lookup_key of the Solution to use as the incoming upgrade source. Mutually exclusive with solution_bundle.
350class SolutionViewInput(TypedDict, total=False): 351 "Track a Solution detail-page view" 352 353 anonymous: str | None 354 "Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session."
Track a Solution detail-page view
357class AsyncSolutionResource: 358 def __init__(self, http: HttpClient): 359 self._http = http 360 361 async def list( 362 self, 363 *, 364 page: int | None = None, 365 page_size: int | None = None, 366 lookup_key: str | None = None, 367 path_prefix: str | None = None, 368 owners: builtins.list[str] | None = None, 369 ) -> SolutionListResponse: 370 """ 371 List Solutions 372 Returns a paginated list of Solutions visible to the caller, merging two 373 scopes: app-level Solutions (system-owned rows with no org affiliation, 374 visible to everyone including unauthenticated callers so they can power 375 the public catalog) and org-level Solutions (system-owned rows stamped with 376 the viewer's org ID, included when an authenticated viewer carries an org 377 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 378 and therefore only ever see the app-level scope. 379 Solutions that appear under both scopes are deduplicated by their stable 380 `solution_id` value. The merged entry's `owners` array lists every scope the 381 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 382 has a higher `solution_version` than the org-level copy, the response includes 383 `upgrade_available: true` and `latest_version` so callers can prompt for an 384 upgrade. 385 386 Args: 387 page: Page number to return. Defaults to `1`. 388 page_size: Number of Solutions per page. Defaults to `25`. 389 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 390 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 391 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 392 393 Returns: 394 Paginated list of Solution summaries visible to the caller. 395 """ 396 query: dict[str, object] = {} 397 if page is not None: 398 query["page"] = page 399 if page_size is not None: 400 query["page_size"] = page_size 401 if lookup_key is not None: 402 query["lookup_key"] = lookup_key 403 if path_prefix is not None: 404 query["path_prefix"] = path_prefix 405 if owners is not None: 406 query["owners"] = owners 407 return await self._http.request( 408 "/api/v1/solutions", 409 query=query, 410 response_type=SolutionListResponse, 411 ) 412 413 async def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 414 """ 415 Import a Solution into the library 416 Imports a Solution and its bundled configs (skills, scripts, templates, files) 417 into the library for the target scope. Two mutually exclusive import modes 418 are supported: pass `solution` to re-import an existing system-owned catalog 419 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 420 self-contained inline bundle. Exactly one must be present. 421 The operation upserts the bundle in a single transaction. When `dry_run` is 422 `true` the same pipeline runs but the transaction is rolled back no rows are 423 persisted and the response reflects what would have been written. The 424 response shape is the same in both cases: the Solution summary plus 425 `installed_configs` listing each config the import created or would create. 426 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 427 Solution into the library; install provisions a runtime resource (Agent, 428 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 429 430 Args: 431 input: Request body. 432 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 433 input.org: Organization ID (`org_...`) for the import destination scope. 434 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 435 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 436 input.team: Team ID (`team_...`) for the import destination scope. 437 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 438 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 439 440 Returns: 441 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 442 """ 443 return await self._http.request( 444 "/api/v1/solutions", 445 method="POST", 446 body=input, 447 response_type=SolutionImportResponse, 448 ) 449 450 async def delete(self, solution: str) -> None: 451 """ 452 Delete a Solution 453 Permanently deletes an imported Solution and all configs bundled with it, 454 including templates, skills, scripts, and files. The deletion runs in a 455 single transaction; provider-stored blobs are swept asynchronously after commit. 456 Org-scope callers (for example, an org admin in the Library settings page) can 457 only delete their org's copy of the Solution. App-scope callers can delete 458 Solutions at either scope. RBAC is enforced inside the core delete flow. 459 Returns `204 No Content` on success. 460 461 Args: 462 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 463 464 Returns: 465 Empty body. HTTP 204 indicates the Solution was deleted successfully. 466 """ 467 await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 468 469 async def get(self, solution: str) -> SolutionSummary: 470 """ 471 Retrieve a Solution 472 Returns a single Solution identified by its config ID (`cfg_...`) or 473 `lookup_key`, in the same summary shape the list endpoint emits. The response 474 always includes a freshly-minted `readme_url` call this endpoint to refresh 475 an expired README token without making any other state change. 476 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 477 are visible to everyone including unauthenticated callers, so the public 478 catalog can render a Solution's detail page logged-out; org-scoped Solutions 479 are only visible to authenticated viewers whose org context matches. Anything 480 else returns 404. 481 When the resolved Solution is org-scoped, the endpoint compares its 482 `solution_version` against the matching app-level copy. If the app-level copy 483 is at a higher version the response includes `upgrade_available: true` and 484 `latest_version`. App-level resolutions always report `upgrade_available: false`. 485 486 Args: 487 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 488 489 Returns: 490 Solution summary including a freshly-minted `readme_url` token valid for one hour. 491 """ 492 return await self._http.request( 493 f"/api/v1/solutions/{solution}", 494 response_type=SolutionSummary, 495 ) 496 497 async def dependents(self, solution: str) -> SolutionDependentsResponse: 498 """ 499 Preview Solution delete impact 500 Returns a read-only preview of what deleting the specified Solution would 501 affect: the agents that reference the Solution's bundle, and the count of 502 bundled configs that would be orphaned rather than cascade-deleted. 503 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 504 surface a warning when live agents depend on the Solution. 505 Visibility scope mirrors the delete endpoint: org-scope viewers see their 506 org's copy; app-scope viewers can inspect either scope. 507 508 Args: 509 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 510 511 Returns: 512 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 513 """ 514 return await self._http.request( 515 f"/api/v1/solutions/{solution}/dependents", 516 response_type=SolutionDependentsResponse, 517 ) 518 519 async def image( 520 self, solution: str, token: str, *, v: str | None = None, file: str | None = None 521 ) -> dict[str, str]: 522 """ 523 Fetch a Solution cover image or gallery screenshot 524 Returns the raw bytes of a Solution's cover image the bundled asset the 525 Solution body's `image:` field names or, with the optional `file` param, 526 one of its gallery screenshots (`screenshots:` entries). This endpoint backs 527 the `image_url` and `screenshot_urls` fields of catalog payloads (such as 528 `GET /api/v1/solutions`), which anonymous consumers the public 529 marketplace's page cache, OpenGraph scrapers may hold far longer than a 530 signed storage URL lives. Authorization is performed via a short, stable 531 capability `token` rather than an HTTP header, so the URL never expires. 532 The `token` is an HMAC-based capability tied to the Solution config ID. It 533 does not expire, but the endpoint checks at fetch time that the Solution 534 still declares the requested image without `file`, that it still declares 535 a cover; with `file`, that the path is still among the body's declared 536 `image`/`screenshots` so republishing without the asset (or hiding the 537 Solution) turns the URL into a 404. Shared caches may continue serving the 538 old image until the `Cache-Control` max-age of one hour elapses. `v` is an 539 opaque cache key minted alongside the token; it changes when the Solution 540 changes and is ignored by verification. 541 All failure modes invalid config ID, invalid token, hidden Solution, no 542 declared cover, a `file` outside the declared set, or an asset that doesn't 543 resolve to a bundled image return a uniform `404` to avoid acting as an 544 existence oracle. 545 546 Args: 547 solution: Solution config ID (`cfg_...`) whose cover image to fetch. 548 token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized. 549 v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification. 550 file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served. 551 552 Returns: 553 Raw cover image bytes, served with the asset's image content type. 554 """ 555 query: dict[str, object] = {} 556 query["token"] = token 557 if v is not None: 558 query["v"] = v 559 if file is not None: 560 query["file"] = file 561 return await self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query) 562 563 async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 564 """ 565 Install a Solution 566 Provisions a runtime resource from an already-imported Solution. The type of 567 resource created depends on the template the Solution wraps: an 568 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 569 Automation, and attachment templates (`AgentRoutineTemplate`, 570 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 571 sub-resource to an existing Agent specified by `target`. 572 For Solutions that bundle more than one template, pass `template` (the ID or 573 `lookup_key` of the desired template) to select which one to provision. 574 Single-template Solutions do not require `template`. 575 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 576 the library; install provisions a runtime resource from it. 577 578 Args: 579 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 580 input: Request body. 581 input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team. 582 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 583 input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`. 584 input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`. 585 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 586 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 587 input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions. 588 input.org: Organization ID (`org_...`) for the install destination scope. 589 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 590 input.team: Team ID (`team_...`) for the install destination scope. 591 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 592 input.user: User ID (`usr_...`) for the install destination scope. 593 594 Returns: 595 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 596 """ 597 return await self._http.request( 598 f"/api/v1/solutions/{solution}/install", 599 method="POST", 600 body=input, 601 response_type=SolutionInstallResponse, 602 ) 603 604 async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 605 """ 606 Retrieve a Solution README or asset 607 Serves the README markdown or a bundled asset for an imported Solution. Both 608 modes use the same path and require a short-lived signed `token` in the query 609 string rather than an `Authorization` header, so browsers can load asset URLs 610 directly from `<img src>` attributes without custom request logic. 611 When `file` is omitted the response is the Solution's `readme` field rendered 612 as `text/markdown`. All local asset references in the markdown are rewritten to 613 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 614 images inline without additional authentication. 615 When `file` is set the response is the raw bytes of the matching asset (a File 616 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 617 name) with the asset's stored `Content-Type`. 618 Tokens are scoped to a single Solution, carry the viewer's app, org, and 619 sandbox context from the time they were minted, and expire after one hour. 620 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 621 always returns a newly minted `readme_url`. 622 623 Args: 624 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 625 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 626 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 627 628 Returns: 629 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 630 """ 631 query: dict[str, object] = {} 632 query["token"] = token 633 if file is not None: 634 query["file"] = file 635 return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 636 637 async def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse: 638 """ 639 Reconcile Customer Management product knowledge 640 Recomputes every stamped customer's knowledge-search source allowlist 641 from the template-resolved customer refs plus the install's current marked 642 product-knowledge sources. Safe to retry; partial failures are returned by 643 customer key rather than hidden behind an aggregate success. 644 645 Args: 646 solution: Installed Customer Management Solution config (`cfg_...`) to reconcile. 647 648 Returns: 649 Per-customer convergence result for this installed solution. 650 """ 651 return await self._http.request( 652 f"/api/v1/solutions/{solution}/reconcile_product_knowledge", 653 method="POST", 654 response_type=ProductKnowledgeReconcileResponse, 655 ) 656 657 async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 658 """ 659 Upgrade an installed Solution 660 Applies an incoming bundle to an already-installed Solution in a single atomic 661 transaction, bringing its configs in line with the new bundle. Config IDs are 662 preserved across the upgrade. Configs that existed in the old bundle but are 663 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 664 Two mutually exclusive source modes: pass `target_solution` to pull the 665 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 666 `solution_bundle` to supply a complete inline bundle directly. Exactly one 667 must be present. 668 When `dry_run` is `true` the full diff is computed and returned but no 669 changes are written. Pass the dry-run response's `review_fingerprint` as 670 `expected_review_fingerprint` when applying to guard against the bundle 671 changing between review and apply. 672 673 Args: 674 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 675 input: Request body. 676 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 677 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 678 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 679 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 680 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 681 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 682 683 Returns: 684 Updated Solution plus the full upgrade diff. 685 """ 686 return await self._http.request( 687 f"/api/v1/solutions/{solution}/upgrade", 688 method="POST", 689 body=input, 690 response_type=SolutionUpgradeResponse, 691 ) 692 693 async def view(self, solution: str, input: SolutionViewInput) -> None: 694 """ 695 Track a Solution detail-page view 696 Records a `solution_viewed` analytics event for the identified Solution and 697 returns `204 No Content`. Fired by the marketplace when a Solution's detail 698 page is rendered in a browser, so publishers can see impressions alongside 699 installs in their Solution analytics. 700 Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers 701 (the logged-out marketplace) can only track Solutions published to the public 702 catalog; anything the caller could not retrieve returns 404 and records 703 nothing. 704 Pass `anonymous` (the analytics visitor ID) so logged-out views can be 705 counted as unique viewers. The event's Solution and publisher attribution are 706 resolved server-side from the Solution row never from request input. 707 708 Args: 709 solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution. 710 input: Request body. 711 input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session. 712 713 Returns: 714 Empty body. HTTP 204 indicates the Solution view was recorded successfully. 715 """ 716 await self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
361 async def list( 362 self, 363 *, 364 page: int | None = None, 365 page_size: int | None = None, 366 lookup_key: str | None = None, 367 path_prefix: str | None = None, 368 owners: builtins.list[str] | None = None, 369 ) -> SolutionListResponse: 370 """ 371 List Solutions 372 Returns a paginated list of Solutions visible to the caller, merging two 373 scopes: app-level Solutions (system-owned rows with no org affiliation, 374 visible to everyone including unauthenticated callers so they can power 375 the public catalog) and org-level Solutions (system-owned rows stamped with 376 the viewer's org ID, included when an authenticated viewer carries an org 377 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 378 and therefore only ever see the app-level scope. 379 Solutions that appear under both scopes are deduplicated by their stable 380 `solution_id` value. The merged entry's `owners` array lists every scope the 381 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 382 has a higher `solution_version` than the org-level copy, the response includes 383 `upgrade_available: true` and `latest_version` so callers can prompt for an 384 upgrade. 385 386 Args: 387 page: Page number to return. Defaults to `1`. 388 page_size: Number of Solutions per page. Defaults to `25`. 389 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 390 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 391 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 392 393 Returns: 394 Paginated list of Solution summaries visible to the caller. 395 """ 396 query: dict[str, object] = {} 397 if page is not None: 398 query["page"] = page 399 if page_size is not None: 400 query["page_size"] = page_size 401 if lookup_key is not None: 402 query["lookup_key"] = lookup_key 403 if path_prefix is not None: 404 query["path_prefix"] = path_prefix 405 if owners is not None: 406 query["owners"] = owners 407 return await self._http.request( 408 "/api/v1/solutions", 409 query=query, 410 response_type=SolutionListResponse, 411 )
List Solutions
Returns a paginated list of Solutions visible to the caller, merging two
scopes: app-level Solutions (system-owned rows with no org affiliation,
visible to everyone including unauthenticated callers so they can power
the public catalog) and org-level Solutions (system-owned rows stamped with
the viewer's org ID, included when an authenticated viewer carries an org
context). Unauthenticated callers resolve to an app-scoped anonymous viewer
and therefore only ever see the app-level scope.
Solutions that appear under both scopes are deduplicated by their stable
solution_id value. The merged entry's owners array lists every scope the
Solution was found under ("system" and/or "org"). When the app-level copy
has a higher solution_version than the org-level copy, the response includes
upgrade_available: true and latest_version so callers can prompt for an
upgrade.
Arguments:
- page: Page number to return. Defaults to
1. - page_size: Number of Solutions per page. Defaults to
25. - lookup_key: Filter to the Solution whose
lookup_keymatches exactly. - path_prefix: Filter to Solutions whose
virtual_pathstarts with this prefix. - owners: Restrict results to a subset of owner scopes. Accepted values:
"system"(app-level Solutions) and"org"(viewer's org-level Solutions). Omit to include all scopes the viewer can see.
Returns:
Paginated list of Solution summaries visible to the caller.
413 async def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 414 """ 415 Import a Solution into the library 416 Imports a Solution and its bundled configs (skills, scripts, templates, files) 417 into the library for the target scope. Two mutually exclusive import modes 418 are supported: pass `solution` to re-import an existing system-owned catalog 419 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 420 self-contained inline bundle. Exactly one must be present. 421 The operation upserts the bundle in a single transaction. When `dry_run` is 422 `true` the same pipeline runs but the transaction is rolled back no rows are 423 persisted and the response reflects what would have been written. The 424 response shape is the same in both cases: the Solution summary plus 425 `installed_configs` listing each config the import created or would create. 426 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 427 Solution into the library; install provisions a runtime resource (Agent, 428 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 429 430 Args: 431 input: Request body. 432 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 433 input.org: Organization ID (`org_...`) for the import destination scope. 434 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 435 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 436 input.team: Team ID (`team_...`) for the import destination scope. 437 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 438 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 439 440 Returns: 441 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 442 """ 443 return await self._http.request( 444 "/api/v1/solutions", 445 method="POST", 446 body=input, 447 response_type=SolutionImportResponse, 448 )
Import a Solution into the library
Imports a Solution and its bundled configs (skills, scripts, templates, files)
into the library for the target scope. Two mutually exclusive import modes
are supported: pass solution to re-import an existing system-owned catalog
Solution by ID or lookup_key, or pass solution_bundle to supply a
self-contained inline bundle. Exactly one must be present.
The operation upserts the bundle in a single transaction. When dry_run is
true the same pipeline runs but the transaction is rolled back no rows are
persisted and the response reflects what would have been written. The
response shape is the same in both cases: the Solution summary plus
installed_configs listing each config the import created or would create.
Pairs with POST /api/v1/solutions/:solution/install: this endpoint puts the
Solution into the library; install provisions a runtime resource (Agent,
AgentRoutine, AgentTool, etc.) from an already-imported Solution.
Arguments:
- input: Request body.
- input.dry_run: When
true, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults tofalse. - input.org: Organization ID (
org_...) for the import destination scope. - input.solution: Config ID (
cfg_...) orlookup_keyof an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive withsolution_bundle. - input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with
solution. - input.team: Team ID (
team_...) for the import destination scope. - input.user: User ID (
usr_...) for the import destination scope. Only one oforg,team, orusermay be set. - input.virtual_path_prefix: Path prefix under which all uploaded configs'
virtual_pathvalues are anchored (for examplesolutions/<uuid>). Stable per install; omit to use no prefix.
Returns:
The imported Solution in summary form, plus
installed_configsone entry per config the transaction created or would create indry_runmode.installed_configsis deprecated; prefer thesolutionsummary shape for new integrations.
450 async def delete(self, solution: str) -> None: 451 """ 452 Delete a Solution 453 Permanently deletes an imported Solution and all configs bundled with it, 454 including templates, skills, scripts, and files. The deletion runs in a 455 single transaction; provider-stored blobs are swept asynchronously after commit. 456 Org-scope callers (for example, an org admin in the Library settings page) can 457 only delete their org's copy of the Solution. App-scope callers can delete 458 Solutions at either scope. RBAC is enforced inside the core delete flow. 459 Returns `204 No Content` on success. 460 461 Args: 462 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 463 464 Returns: 465 Empty body. HTTP 204 indicates the Solution was deleted successfully. 466 """ 467 await self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
Delete a Solution
Permanently deletes an imported Solution and all configs bundled with it,
including templates, skills, scripts, and files. The deletion runs in a
single transaction; provider-stored blobs are swept asynchronously after commit.
Org-scope callers (for example, an org admin in the Library settings page) can
only delete their org's copy of the Solution. App-scope callers can delete
Solutions at either scope. RBAC is enforced inside the core delete flow.
Returns 204 No Content on success.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to delete.
Returns:
Empty body. HTTP 204 indicates the Solution was deleted successfully.
469 async def get(self, solution: str) -> SolutionSummary: 470 """ 471 Retrieve a Solution 472 Returns a single Solution identified by its config ID (`cfg_...`) or 473 `lookup_key`, in the same summary shape the list endpoint emits. The response 474 always includes a freshly-minted `readme_url` call this endpoint to refresh 475 an expired README token without making any other state change. 476 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 477 are visible to everyone including unauthenticated callers, so the public 478 catalog can render a Solution's detail page logged-out; org-scoped Solutions 479 are only visible to authenticated viewers whose org context matches. Anything 480 else returns 404. 481 When the resolved Solution is org-scoped, the endpoint compares its 482 `solution_version` against the matching app-level copy. If the app-level copy 483 is at a higher version the response includes `upgrade_available: true` and 484 `latest_version`. App-level resolutions always report `upgrade_available: false`. 485 486 Args: 487 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 488 489 Returns: 490 Solution summary including a freshly-minted `readme_url` token valid for one hour. 491 """ 492 return await self._http.request( 493 f"/api/v1/solutions/{solution}", 494 response_type=SolutionSummary, 495 )
Retrieve a Solution
Returns a single Solution identified by its config ID (cfg_...) or
lookup_key, in the same summary shape the list endpoint emits. The response
always includes a freshly-minted readme_url call this endpoint to refresh
an expired README token without making any other state change.
Visibility matches the list endpoint: app-level Solutions (no org affiliation)
are visible to everyone including unauthenticated callers, so the public
catalog can render a Solution's detail page logged-out; org-scoped Solutions
are only visible to authenticated viewers whose org context matches. Anything
else returns 404.
When the resolved Solution is org-scoped, the endpoint compares its
solution_version against the matching app-level copy. If the app-level copy
is at a higher version the response includes upgrade_available: true and
latest_version. App-level resolutions always report upgrade_available: false.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to retrieve.
Returns:
Solution summary including a freshly-minted
readme_urltoken valid for one hour.
497 async def dependents(self, solution: str) -> SolutionDependentsResponse: 498 """ 499 Preview Solution delete impact 500 Returns a read-only preview of what deleting the specified Solution would 501 affect: the agents that reference the Solution's bundle, and the count of 502 bundled configs that would be orphaned rather than cascade-deleted. 503 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 504 surface a warning when live agents depend on the Solution. 505 Visibility scope mirrors the delete endpoint: org-scope viewers see their 506 org's copy; app-scope viewers can inspect either scope. 507 508 Args: 509 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 510 511 Returns: 512 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 513 """ 514 return await self._http.request( 515 f"/api/v1/solutions/{solution}/dependents", 516 response_type=SolutionDependentsResponse, 517 )
Preview Solution delete impact
Returns a read-only preview of what deleting the specified Solution would
affect: the agents that reference the Solution's bundle, and the count of
bundled configs that would be orphaned rather than cascade-deleted.
Use this endpoint before calling DELETE /api/v1/solutions/:solution to
surface a warning when live agents depend on the Solution.
Visibility scope mirrors the delete endpoint: org-scope viewers see their
org's copy; app-scope viewers can inspect either scope.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to inspect.
Returns:
Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
519 async def image( 520 self, solution: str, token: str, *, v: str | None = None, file: str | None = None 521 ) -> dict[str, str]: 522 """ 523 Fetch a Solution cover image or gallery screenshot 524 Returns the raw bytes of a Solution's cover image the bundled asset the 525 Solution body's `image:` field names or, with the optional `file` param, 526 one of its gallery screenshots (`screenshots:` entries). This endpoint backs 527 the `image_url` and `screenshot_urls` fields of catalog payloads (such as 528 `GET /api/v1/solutions`), which anonymous consumers the public 529 marketplace's page cache, OpenGraph scrapers may hold far longer than a 530 signed storage URL lives. Authorization is performed via a short, stable 531 capability `token` rather than an HTTP header, so the URL never expires. 532 The `token` is an HMAC-based capability tied to the Solution config ID. It 533 does not expire, but the endpoint checks at fetch time that the Solution 534 still declares the requested image without `file`, that it still declares 535 a cover; with `file`, that the path is still among the body's declared 536 `image`/`screenshots` so republishing without the asset (or hiding the 537 Solution) turns the URL into a 404. Shared caches may continue serving the 538 old image until the `Cache-Control` max-age of one hour elapses. `v` is an 539 opaque cache key minted alongside the token; it changes when the Solution 540 changes and is ignored by verification. 541 All failure modes invalid config ID, invalid token, hidden Solution, no 542 declared cover, a `file` outside the declared set, or an asset that doesn't 543 resolve to a bundled image return a uniform `404` to avoid acting as an 544 existence oracle. 545 546 Args: 547 solution: Solution config ID (`cfg_...`) whose cover image to fetch. 548 token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized. 549 v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification. 550 file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served. 551 552 Returns: 553 Raw cover image bytes, served with the asset's image content type. 554 """ 555 query: dict[str, object] = {} 556 query["token"] = token 557 if v is not None: 558 query["v"] = v 559 if file is not None: 560 query["file"] = file 561 return await self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)
Fetch a Solution cover image or gallery screenshot
Returns the raw bytes of a Solution's cover image the bundled asset the
Solution body's image: field names or, with the optional file param,
one of its gallery screenshots (screenshots: entries). This endpoint backs
the image_url and screenshot_urls fields of catalog payloads (such as
GET /api/v1/solutions), which anonymous consumers the public
marketplace's page cache, OpenGraph scrapers may hold far longer than a
signed storage URL lives. Authorization is performed via a short, stable
capability token rather than an HTTP header, so the URL never expires.
The token is an HMAC-based capability tied to the Solution config ID. It
does not expire, but the endpoint checks at fetch time that the Solution
still declares the requested image without file, that it still declares
a cover; with file, that the path is still among the body's declared
image/screenshots so republishing without the asset (or hiding the
Solution) turns the URL into a 404. Shared caches may continue serving the
old image until the Cache-Control max-age of one hour elapses. v is an
opaque cache key minted alongside the token; it changes when the Solution
changes and is ignored by verification.
All failure modes invalid config ID, invalid token, hidden Solution, no
declared cover, a file outside the declared set, or an asset that doesn't
resolve to a bundled image return a uniform 404 to avoid acting as an
existence oracle.
Arguments:
- solution: Solution config ID (
cfg_...) whose cover image to fetch. - token: HMAC capability token authorizing access to this Solution's cover. Obtained from the
image_urlminted when the Solution was serialized. - v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
- file: Bundled asset path of the image to serve (for example
images/setup.png). Must be one of the paths the Solution body currently declares inimage/screenshotsanything else is a404. When absent the declared cover (image:) is served.
Returns:
Raw cover image bytes, served with the asset's image content type.
563 async def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 564 """ 565 Install a Solution 566 Provisions a runtime resource from an already-imported Solution. The type of 567 resource created depends on the template the Solution wraps: an 568 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 569 Automation, and attachment templates (`AgentRoutineTemplate`, 570 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 571 sub-resource to an existing Agent specified by `target`. 572 For Solutions that bundle more than one template, pass `template` (the ID or 573 `lookup_key` of the desired template) to select which one to provision. 574 Single-template Solutions do not require `template`. 575 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 576 the library; install provisions a runtime resource from it. 577 578 Args: 579 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 580 input: Request body. 581 input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team. 582 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 583 input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`. 584 input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`. 585 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 586 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 587 input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions. 588 input.org: Organization ID (`org_...`) for the install destination scope. 589 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 590 input.team: Team ID (`team_...`) for the install destination scope. 591 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 592 input.user: User ID (`usr_...`) for the install destination scope. 593 594 Returns: 595 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 596 """ 597 return await self._http.request( 598 f"/api/v1/solutions/{solution}/install", 599 method="POST", 600 body=input, 601 response_type=SolutionInstallResponse, 602 )
Install a Solution
Provisions a runtime resource from an already-imported Solution. The type of
resource created depends on the template the Solution wraps: an
AgentTemplate produces an Agent, an AutomationTemplate produces an
Automation, and attachment templates (AgentRoutineTemplate,
AgentToolTemplate, AgentSkillTemplate, AgentComputerTemplate) attach a
sub-resource to an existing Agent specified by target.
For Solutions that bundle more than one template, pass template (the ID or
lookup_key of the desired template) to select which one to provision.
Single-template Solutions do not require template.
Pairs with POST /api/v1/solutions (import): import puts the Solution into
the library; install provisions a runtime resource from it.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the already-imported Solution to install from. - input: Request body.
- input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
- input.allow_auto_import: When
true, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicitorgparam. Defaults tofalse; without it the endpoint returns 404 when the org-scoped Solution is missing. - input.details: Template-specific install options selected by the
typediscriminator. AutomationTemplate installs accept{type: "automation", prefills: ...}. - input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use
{values: {customer_label: "Acme"}}. - input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's
agent_key). - input.lookup_key_suffix: Suffix appended to every
config_ref:resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. - input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
- input.org: Organization ID (
org_...) for the install destination scope. - input.target: ID or
lookup_keyof the parent Agent to attach to. Required when installing anAgentRoutineTemplate,AgentToolTemplate,AgentSkillTemplate, orAgentComputerTemplateSolution, since those produce sub-resources attached to an existing Agent. Omit forAgentTemplateandAutomationTemplateSolutions, which provision standalone resources. - input.team: Team ID (
team_...) for the install destination scope. - input.template: Config ID (
cfg_...) orlookup_keyof the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. - input.user: User ID (
usr_...) for the install destination scope.
Returns:
The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
604 async def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 605 """ 606 Retrieve a Solution README or asset 607 Serves the README markdown or a bundled asset for an imported Solution. Both 608 modes use the same path and require a short-lived signed `token` in the query 609 string rather than an `Authorization` header, so browsers can load asset URLs 610 directly from `<img src>` attributes without custom request logic. 611 When `file` is omitted the response is the Solution's `readme` field rendered 612 as `text/markdown`. All local asset references in the markdown are rewritten to 613 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 614 images inline without additional authentication. 615 When `file` is set the response is the raw bytes of the matching asset (a File 616 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 617 name) with the asset's stored `Content-Type`. 618 Tokens are scoped to a single Solution, carry the viewer's app, org, and 619 sandbox context from the time they were minted, and expire after one hour. 620 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 621 always returns a newly minted `readme_url`. 622 623 Args: 624 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 625 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 626 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 627 628 Returns: 629 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 630 """ 631 query: dict[str, object] = {} 632 query["token"] = token 633 if file is not None: 634 query["file"] = file 635 return await self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
Retrieve a Solution README or asset
Serves the README markdown or a bundled asset for an imported Solution. Both
modes use the same path and require a short-lived signed token in the query
string rather than an Authorization header, so browsers can load asset URLs
directly from <img src> attributes without custom request logic.
When file is omitted the response is the Solution's readme field rendered
as text/markdown. All local asset references in the markdown are rewritten to
point back at this endpoint with ?file=PATH&token=TOKEN so browsers can load
images inline without additional authentication.
When file is set the response is the raw bytes of the matching asset (a File
child whose relative_path equals PATH, or an inline assets entry by
name) with the asset's stored Content-Type.
Tokens are scoped to a single Solution, carry the viewer's app, org, and
sandbox context from the time they were minted, and expire after one hour.
Obtain a fresh token by calling GET /api/v1/solutions/:solution, which
always returns a newly minted readme_url.
Arguments:
- solution: Solution config ID (
cfg_...) identifying the Solution whose README or asset to retrieve. - token: Signed URL token minted by the list or show endpoint. Expires after one hour.
- file: Relative path of the asset to retrieve (for example
images/hero.png). When present the response is the raw asset bytes with its realContent-Type; when absent the response is the README markdown.
Returns:
README markdown (
text/markdown) whenfileis omitted, or the raw asset bytes with the asset'sContent-Typewhenfileis set.
637 async def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse: 638 """ 639 Reconcile Customer Management product knowledge 640 Recomputes every stamped customer's knowledge-search source allowlist 641 from the template-resolved customer refs plus the install's current marked 642 product-knowledge sources. Safe to retry; partial failures are returned by 643 customer key rather than hidden behind an aggregate success. 644 645 Args: 646 solution: Installed Customer Management Solution config (`cfg_...`) to reconcile. 647 648 Returns: 649 Per-customer convergence result for this installed solution. 650 """ 651 return await self._http.request( 652 f"/api/v1/solutions/{solution}/reconcile_product_knowledge", 653 method="POST", 654 response_type=ProductKnowledgeReconcileResponse, 655 )
Reconcile Customer Management product knowledge Recomputes every stamped customer's knowledge-search source allowlist from the template-resolved customer refs plus the install's current marked product-knowledge sources. Safe to retry; partial failures are returned by customer key rather than hidden behind an aggregate success.
Arguments:
- solution: Installed Customer Management Solution config (
cfg_...) to reconcile.
Returns:
Per-customer convergence result for this installed solution.
657 async def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 658 """ 659 Upgrade an installed Solution 660 Applies an incoming bundle to an already-installed Solution in a single atomic 661 transaction, bringing its configs in line with the new bundle. Config IDs are 662 preserved across the upgrade. Configs that existed in the old bundle but are 663 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 664 Two mutually exclusive source modes: pass `target_solution` to pull the 665 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 666 `solution_bundle` to supply a complete inline bundle directly. Exactly one 667 must be present. 668 When `dry_run` is `true` the full diff is computed and returned but no 669 changes are written. Pass the dry-run response's `review_fingerprint` as 670 `expected_review_fingerprint` when applying to guard against the bundle 671 changing between review and apply. 672 673 Args: 674 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 675 input: Request body. 676 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 677 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 678 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 679 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 680 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 681 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 682 683 Returns: 684 Updated Solution plus the full upgrade diff. 685 """ 686 return await self._http.request( 687 f"/api/v1/solutions/{solution}/upgrade", 688 method="POST", 689 body=input, 690 response_type=SolutionUpgradeResponse, 691 )
Upgrade an installed Solution
Applies an incoming bundle to an already-installed Solution in a single atomic
transaction, bringing its configs in line with the new bundle. Config IDs are
preserved across the upgrade. Configs that existed in the old bundle but are
absent from the new one are orphaned (top-level) or hard-deleted (child rows).
Two mutually exclusive source modes: pass target_solution to pull the
incoming bundle from an existing Solution by ID or lookup_key, or pass
solution_bundle to supply a complete inline bundle directly. Exactly one
must be present.
When dry_run is true the full diff is computed and returned but no
changes are written. Pass the dry-run response's review_fingerprint as
expected_review_fingerprint when applying to guard against the bundle
changing between review and apply.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the currently installed Solution to upgrade. - input: Request body.
- input.allow_downgrade: When
true, permits an incomingsolution_versionlower than the currently installed version. Defaults tofalse. - input.dry_run: When
true, computes and returns the full upgrade diff without persisting any changes. Defaults tofalse. - input.expected_review_fingerprint: Optional stale-review guard. Pass the
review_fingerprintreturned by a priordry_runcall to ensure the bundle has not changed between review and apply. - input.org: Organization ID (
org_...) used to resolve org-scopedlookup_keyvalues. Config IDs (cfg_...) are globally unique and do not require this. - input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with
target_solution. - input.target_solution: Config ID (
cfg_...) orlookup_keyof the Solution to use as the incoming upgrade source. Mutually exclusive withsolution_bundle.
Returns:
Updated Solution plus the full upgrade diff.
693 async def view(self, solution: str, input: SolutionViewInput) -> None: 694 """ 695 Track a Solution detail-page view 696 Records a `solution_viewed` analytics event for the identified Solution and 697 returns `204 No Content`. Fired by the marketplace when a Solution's detail 698 page is rendered in a browser, so publishers can see impressions alongside 699 installs in their Solution analytics. 700 Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers 701 (the logged-out marketplace) can only track Solutions published to the public 702 catalog; anything the caller could not retrieve returns 404 and records 703 nothing. 704 Pass `anonymous` (the analytics visitor ID) so logged-out views can be 705 counted as unique viewers. The event's Solution and publisher attribution are 706 resolved server-side from the Solution row never from request input. 707 708 Args: 709 solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution. 710 input: Request body. 711 input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session. 712 713 Returns: 714 Empty body. HTTP 204 indicates the Solution view was recorded successfully. 715 """ 716 await self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
Track a Solution detail-page view
Records a solution_viewed analytics event for the identified Solution and
returns 204 No Content. Fired by the marketplace when a Solution's detail
page is rendered in a browser, so publishers can see impressions alongside
installs in their Solution analytics.
Visibility matches GET /api/v1/solutions/:solution: unauthenticated callers
(the logged-out marketplace) can only track Solutions published to the public
catalog; anything the caller could not retrieve returns 404 and records
nothing.
Pass anonymous (the analytics visitor ID) so logged-out views can be
counted as unique viewers. The event's Solution and publisher attribution are
resolved server-side from the Solution row never from request input.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the viewed Solution. - input: Request body.
- input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the
POST /api/v1/tevents use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
Returns:
Empty body. HTTP 204 indicates the Solution view was recorded successfully.
719class SolutionResource: 720 def __init__(self, http: SyncHttpClient): 721 self._http = http 722 723 def list( 724 self, 725 *, 726 page: int | None = None, 727 page_size: int | None = None, 728 lookup_key: str | None = None, 729 path_prefix: str | None = None, 730 owners: builtins.list[str] | None = None, 731 ) -> SolutionListResponse: 732 """ 733 List Solutions 734 Returns a paginated list of Solutions visible to the caller, merging two 735 scopes: app-level Solutions (system-owned rows with no org affiliation, 736 visible to everyone including unauthenticated callers so they can power 737 the public catalog) and org-level Solutions (system-owned rows stamped with 738 the viewer's org ID, included when an authenticated viewer carries an org 739 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 740 and therefore only ever see the app-level scope. 741 Solutions that appear under both scopes are deduplicated by their stable 742 `solution_id` value. The merged entry's `owners` array lists every scope the 743 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 744 has a higher `solution_version` than the org-level copy, the response includes 745 `upgrade_available: true` and `latest_version` so callers can prompt for an 746 upgrade. 747 748 Args: 749 page: Page number to return. Defaults to `1`. 750 page_size: Number of Solutions per page. Defaults to `25`. 751 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 752 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 753 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 754 755 Returns: 756 Paginated list of Solution summaries visible to the caller. 757 """ 758 query: dict[str, object] = {} 759 if page is not None: 760 query["page"] = page 761 if page_size is not None: 762 query["page_size"] = page_size 763 if lookup_key is not None: 764 query["lookup_key"] = lookup_key 765 if path_prefix is not None: 766 query["path_prefix"] = path_prefix 767 if owners is not None: 768 query["owners"] = owners 769 return self._http.request( 770 "/api/v1/solutions", 771 query=query, 772 response_type=SolutionListResponse, 773 ) 774 775 def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 776 """ 777 Import a Solution into the library 778 Imports a Solution and its bundled configs (skills, scripts, templates, files) 779 into the library for the target scope. Two mutually exclusive import modes 780 are supported: pass `solution` to re-import an existing system-owned catalog 781 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 782 self-contained inline bundle. Exactly one must be present. 783 The operation upserts the bundle in a single transaction. When `dry_run` is 784 `true` the same pipeline runs but the transaction is rolled back no rows are 785 persisted and the response reflects what would have been written. The 786 response shape is the same in both cases: the Solution summary plus 787 `installed_configs` listing each config the import created or would create. 788 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 789 Solution into the library; install provisions a runtime resource (Agent, 790 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 791 792 Args: 793 input: Request body. 794 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 795 input.org: Organization ID (`org_...`) for the import destination scope. 796 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 797 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 798 input.team: Team ID (`team_...`) for the import destination scope. 799 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 800 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 801 802 Returns: 803 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 804 """ 805 return self._http.request( 806 "/api/v1/solutions", 807 method="POST", 808 body=input, 809 response_type=SolutionImportResponse, 810 ) 811 812 def delete(self, solution: str) -> None: 813 """ 814 Delete a Solution 815 Permanently deletes an imported Solution and all configs bundled with it, 816 including templates, skills, scripts, and files. The deletion runs in a 817 single transaction; provider-stored blobs are swept asynchronously after commit. 818 Org-scope callers (for example, an org admin in the Library settings page) can 819 only delete their org's copy of the Solution. App-scope callers can delete 820 Solutions at either scope. RBAC is enforced inside the core delete flow. 821 Returns `204 No Content` on success. 822 823 Args: 824 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 825 826 Returns: 827 Empty body. HTTP 204 indicates the Solution was deleted successfully. 828 """ 829 self._http.request(f"/api/v1/solutions/{solution}", method="DELETE") 830 831 def get(self, solution: str) -> SolutionSummary: 832 """ 833 Retrieve a Solution 834 Returns a single Solution identified by its config ID (`cfg_...`) or 835 `lookup_key`, in the same summary shape the list endpoint emits. The response 836 always includes a freshly-minted `readme_url` call this endpoint to refresh 837 an expired README token without making any other state change. 838 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 839 are visible to everyone including unauthenticated callers, so the public 840 catalog can render a Solution's detail page logged-out; org-scoped Solutions 841 are only visible to authenticated viewers whose org context matches. Anything 842 else returns 404. 843 When the resolved Solution is org-scoped, the endpoint compares its 844 `solution_version` against the matching app-level copy. If the app-level copy 845 is at a higher version the response includes `upgrade_available: true` and 846 `latest_version`. App-level resolutions always report `upgrade_available: false`. 847 848 Args: 849 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 850 851 Returns: 852 Solution summary including a freshly-minted `readme_url` token valid for one hour. 853 """ 854 return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary) 855 856 def dependents(self, solution: str) -> SolutionDependentsResponse: 857 """ 858 Preview Solution delete impact 859 Returns a read-only preview of what deleting the specified Solution would 860 affect: the agents that reference the Solution's bundle, and the count of 861 bundled configs that would be orphaned rather than cascade-deleted. 862 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 863 surface a warning when live agents depend on the Solution. 864 Visibility scope mirrors the delete endpoint: org-scope viewers see their 865 org's copy; app-scope viewers can inspect either scope. 866 867 Args: 868 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 869 870 Returns: 871 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 872 """ 873 return self._http.request( 874 f"/api/v1/solutions/{solution}/dependents", 875 response_type=SolutionDependentsResponse, 876 ) 877 878 def image( 879 self, solution: str, token: str, *, v: str | None = None, file: str | None = None 880 ) -> dict[str, str]: 881 """ 882 Fetch a Solution cover image or gallery screenshot 883 Returns the raw bytes of a Solution's cover image the bundled asset the 884 Solution body's `image:` field names or, with the optional `file` param, 885 one of its gallery screenshots (`screenshots:` entries). This endpoint backs 886 the `image_url` and `screenshot_urls` fields of catalog payloads (such as 887 `GET /api/v1/solutions`), which anonymous consumers the public 888 marketplace's page cache, OpenGraph scrapers may hold far longer than a 889 signed storage URL lives. Authorization is performed via a short, stable 890 capability `token` rather than an HTTP header, so the URL never expires. 891 The `token` is an HMAC-based capability tied to the Solution config ID. It 892 does not expire, but the endpoint checks at fetch time that the Solution 893 still declares the requested image without `file`, that it still declares 894 a cover; with `file`, that the path is still among the body's declared 895 `image`/`screenshots` so republishing without the asset (or hiding the 896 Solution) turns the URL into a 404. Shared caches may continue serving the 897 old image until the `Cache-Control` max-age of one hour elapses. `v` is an 898 opaque cache key minted alongside the token; it changes when the Solution 899 changes and is ignored by verification. 900 All failure modes invalid config ID, invalid token, hidden Solution, no 901 declared cover, a `file` outside the declared set, or an asset that doesn't 902 resolve to a bundled image return a uniform `404` to avoid acting as an 903 existence oracle. 904 905 Args: 906 solution: Solution config ID (`cfg_...`) whose cover image to fetch. 907 token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized. 908 v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification. 909 file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served. 910 911 Returns: 912 Raw cover image bytes, served with the asset's image content type. 913 """ 914 query: dict[str, object] = {} 915 query["token"] = token 916 if v is not None: 917 query["v"] = v 918 if file is not None: 919 query["file"] = file 920 return self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query) 921 922 def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 923 """ 924 Install a Solution 925 Provisions a runtime resource from an already-imported Solution. The type of 926 resource created depends on the template the Solution wraps: an 927 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 928 Automation, and attachment templates (`AgentRoutineTemplate`, 929 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 930 sub-resource to an existing Agent specified by `target`. 931 For Solutions that bundle more than one template, pass `template` (the ID or 932 `lookup_key` of the desired template) to select which one to provision. 933 Single-template Solutions do not require `template`. 934 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 935 the library; install provisions a runtime resource from it. 936 937 Args: 938 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 939 input: Request body. 940 input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team. 941 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 942 input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`. 943 input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`. 944 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 945 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 946 input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions. 947 input.org: Organization ID (`org_...`) for the install destination scope. 948 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 949 input.team: Team ID (`team_...`) for the install destination scope. 950 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 951 input.user: User ID (`usr_...`) for the install destination scope. 952 953 Returns: 954 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 955 """ 956 return self._http.request( 957 f"/api/v1/solutions/{solution}/install", 958 method="POST", 959 body=input, 960 response_type=SolutionInstallResponse, 961 ) 962 963 def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 964 """ 965 Retrieve a Solution README or asset 966 Serves the README markdown or a bundled asset for an imported Solution. Both 967 modes use the same path and require a short-lived signed `token` in the query 968 string rather than an `Authorization` header, so browsers can load asset URLs 969 directly from `<img src>` attributes without custom request logic. 970 When `file` is omitted the response is the Solution's `readme` field rendered 971 as `text/markdown`. All local asset references in the markdown are rewritten to 972 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 973 images inline without additional authentication. 974 When `file` is set the response is the raw bytes of the matching asset (a File 975 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 976 name) with the asset's stored `Content-Type`. 977 Tokens are scoped to a single Solution, carry the viewer's app, org, and 978 sandbox context from the time they were minted, and expire after one hour. 979 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 980 always returns a newly minted `readme_url`. 981 982 Args: 983 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 984 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 985 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 986 987 Returns: 988 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 989 """ 990 query: dict[str, object] = {} 991 query["token"] = token 992 if file is not None: 993 query["file"] = file 994 return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query) 995 996 def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse: 997 """ 998 Reconcile Customer Management product knowledge 999 Recomputes every stamped customer's knowledge-search source allowlist 1000 from the template-resolved customer refs plus the install's current marked 1001 product-knowledge sources. Safe to retry; partial failures are returned by 1002 customer key rather than hidden behind an aggregate success. 1003 1004 Args: 1005 solution: Installed Customer Management Solution config (`cfg_...`) to reconcile. 1006 1007 Returns: 1008 Per-customer convergence result for this installed solution. 1009 """ 1010 return self._http.request( 1011 f"/api/v1/solutions/{solution}/reconcile_product_knowledge", 1012 method="POST", 1013 response_type=ProductKnowledgeReconcileResponse, 1014 ) 1015 1016 def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 1017 """ 1018 Upgrade an installed Solution 1019 Applies an incoming bundle to an already-installed Solution in a single atomic 1020 transaction, bringing its configs in line with the new bundle. Config IDs are 1021 preserved across the upgrade. Configs that existed in the old bundle but are 1022 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 1023 Two mutually exclusive source modes: pass `target_solution` to pull the 1024 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 1025 `solution_bundle` to supply a complete inline bundle directly. Exactly one 1026 must be present. 1027 When `dry_run` is `true` the full diff is computed and returned but no 1028 changes are written. Pass the dry-run response's `review_fingerprint` as 1029 `expected_review_fingerprint` when applying to guard against the bundle 1030 changing between review and apply. 1031 1032 Args: 1033 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 1034 input: Request body. 1035 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 1036 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 1037 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 1038 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 1039 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 1040 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 1041 1042 Returns: 1043 Updated Solution plus the full upgrade diff. 1044 """ 1045 return self._http.request( 1046 f"/api/v1/solutions/{solution}/upgrade", 1047 method="POST", 1048 body=input, 1049 response_type=SolutionUpgradeResponse, 1050 ) 1051 1052 def view(self, solution: str, input: SolutionViewInput) -> None: 1053 """ 1054 Track a Solution detail-page view 1055 Records a `solution_viewed` analytics event for the identified Solution and 1056 returns `204 No Content`. Fired by the marketplace when a Solution's detail 1057 page is rendered in a browser, so publishers can see impressions alongside 1058 installs in their Solution analytics. 1059 Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers 1060 (the logged-out marketplace) can only track Solutions published to the public 1061 catalog; anything the caller could not retrieve returns 404 and records 1062 nothing. 1063 Pass `anonymous` (the analytics visitor ID) so logged-out views can be 1064 counted as unique viewers. The event's Solution and publisher attribution are 1065 resolved server-side from the Solution row never from request input. 1066 1067 Args: 1068 solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution. 1069 input: Request body. 1070 input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session. 1071 1072 Returns: 1073 Empty body. HTTP 204 indicates the Solution view was recorded successfully. 1074 """ 1075 self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
723 def list( 724 self, 725 *, 726 page: int | None = None, 727 page_size: int | None = None, 728 lookup_key: str | None = None, 729 path_prefix: str | None = None, 730 owners: builtins.list[str] | None = None, 731 ) -> SolutionListResponse: 732 """ 733 List Solutions 734 Returns a paginated list of Solutions visible to the caller, merging two 735 scopes: app-level Solutions (system-owned rows with no org affiliation, 736 visible to everyone including unauthenticated callers so they can power 737 the public catalog) and org-level Solutions (system-owned rows stamped with 738 the viewer's org ID, included when an authenticated viewer carries an org 739 context). Unauthenticated callers resolve to an app-scoped anonymous viewer 740 and therefore only ever see the app-level scope. 741 Solutions that appear under both scopes are deduplicated by their stable 742 `solution_id` value. The merged entry's `owners` array lists every scope the 743 Solution was found under (`"system"` and/or `"org"`). When the app-level copy 744 has a higher `solution_version` than the org-level copy, the response includes 745 `upgrade_available: true` and `latest_version` so callers can prompt for an 746 upgrade. 747 748 Args: 749 page: Page number to return. Defaults to `1`. 750 page_size: Number of Solutions per page. Defaults to `25`. 751 lookup_key: Filter to the Solution whose `lookup_key` matches exactly. 752 path_prefix: Filter to Solutions whose `virtual_path` starts with this prefix. 753 owners: Restrict results to a subset of owner scopes. Accepted values: `"system"` (app-level Solutions) and `"org"` (viewer's org-level Solutions). Omit to include all scopes the viewer can see. 754 755 Returns: 756 Paginated list of Solution summaries visible to the caller. 757 """ 758 query: dict[str, object] = {} 759 if page is not None: 760 query["page"] = page 761 if page_size is not None: 762 query["page_size"] = page_size 763 if lookup_key is not None: 764 query["lookup_key"] = lookup_key 765 if path_prefix is not None: 766 query["path_prefix"] = path_prefix 767 if owners is not None: 768 query["owners"] = owners 769 return self._http.request( 770 "/api/v1/solutions", 771 query=query, 772 response_type=SolutionListResponse, 773 )
List Solutions
Returns a paginated list of Solutions visible to the caller, merging two
scopes: app-level Solutions (system-owned rows with no org affiliation,
visible to everyone including unauthenticated callers so they can power
the public catalog) and org-level Solutions (system-owned rows stamped with
the viewer's org ID, included when an authenticated viewer carries an org
context). Unauthenticated callers resolve to an app-scoped anonymous viewer
and therefore only ever see the app-level scope.
Solutions that appear under both scopes are deduplicated by their stable
solution_id value. The merged entry's owners array lists every scope the
Solution was found under ("system" and/or "org"). When the app-level copy
has a higher solution_version than the org-level copy, the response includes
upgrade_available: true and latest_version so callers can prompt for an
upgrade.
Arguments:
- page: Page number to return. Defaults to
1. - page_size: Number of Solutions per page. Defaults to
25. - lookup_key: Filter to the Solution whose
lookup_keymatches exactly. - path_prefix: Filter to Solutions whose
virtual_pathstarts with this prefix. - owners: Restrict results to a subset of owner scopes. Accepted values:
"system"(app-level Solutions) and"org"(viewer's org-level Solutions). Omit to include all scopes the viewer can see.
Returns:
Paginated list of Solution summaries visible to the caller.
775 def create(self, input: SolutionCreateInput) -> SolutionImportResponse: 776 """ 777 Import a Solution into the library 778 Imports a Solution and its bundled configs (skills, scripts, templates, files) 779 into the library for the target scope. Two mutually exclusive import modes 780 are supported: pass `solution` to re-import an existing system-owned catalog 781 Solution by ID or `lookup_key`, or pass `solution_bundle` to supply a 782 self-contained inline bundle. Exactly one must be present. 783 The operation upserts the bundle in a single transaction. When `dry_run` is 784 `true` the same pipeline runs but the transaction is rolled back no rows are 785 persisted and the response reflects what would have been written. The 786 response shape is the same in both cases: the Solution summary plus 787 `installed_configs` listing each config the import created or would create. 788 Pairs with `POST /api/v1/solutions/:solution/install`: this endpoint puts the 789 Solution into the library; install provisions a runtime resource (Agent, 790 AgentRoutine, AgentTool, etc.) from an already-imported Solution. 791 792 Args: 793 input: Request body. 794 input.dry_run: When `true`, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults to `false`. 795 input.org: Organization ID (`org_...`) for the import destination scope. 796 input.solution: Config ID (`cfg_...`) or `lookup_key` of an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive with `solution_bundle`. 797 input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with `solution`. 798 input.team: Team ID (`team_...`) for the import destination scope. 799 input.user: User ID (`usr_...`) for the import destination scope. Only one of `org`, `team`, or `user` may be set. 800 input.virtual_path_prefix: Path prefix under which all uploaded configs' `virtual_path` values are anchored (for example `solutions/<uuid>`). Stable per install; omit to use no prefix. 801 802 Returns: 803 The imported Solution in summary form, plus `installed_configs` one entry per config the transaction created or would create in `dry_run` mode. `installed_configs` is deprecated; prefer the `solution` summary shape for new integrations. 804 """ 805 return self._http.request( 806 "/api/v1/solutions", 807 method="POST", 808 body=input, 809 response_type=SolutionImportResponse, 810 )
Import a Solution into the library
Imports a Solution and its bundled configs (skills, scripts, templates, files)
into the library for the target scope. Two mutually exclusive import modes
are supported: pass solution to re-import an existing system-owned catalog
Solution by ID or lookup_key, or pass solution_bundle to supply a
self-contained inline bundle. Exactly one must be present.
The operation upserts the bundle in a single transaction. When dry_run is
true the same pipeline runs but the transaction is rolled back no rows are
persisted and the response reflects what would have been written. The
response shape is the same in both cases: the Solution summary plus
installed_configs listing each config the import created or would create.
Pairs with POST /api/v1/solutions/:solution/install: this endpoint puts the
Solution into the library; install provisions a runtime resource (Agent,
AgentRoutine, AgentTool, etc.) from an already-imported Solution.
Arguments:
- input: Request body.
- input.dry_run: When
true, runs the full import pipeline but rolls back the transaction no rows are persisted. The response reflects what would have been written. Defaults tofalse. - input.org: Organization ID (
org_...) for the import destination scope. - input.solution: Config ID (
cfg_...) orlookup_keyof an existing system-owned, org-less Solution to import into the target scope. Mutually exclusive withsolution_bundle. - input.solution_bundle: Self-contained inline bundle containing the Solution metadata plus all bundled configs (skills, templates, configs, files). Mutually exclusive with
solution. - input.team: Team ID (
team_...) for the import destination scope. - input.user: User ID (
usr_...) for the import destination scope. Only one oforg,team, orusermay be set. - input.virtual_path_prefix: Path prefix under which all uploaded configs'
virtual_pathvalues are anchored (for examplesolutions/<uuid>). Stable per install; omit to use no prefix.
Returns:
The imported Solution in summary form, plus
installed_configsone entry per config the transaction created or would create indry_runmode.installed_configsis deprecated; prefer thesolutionsummary shape for new integrations.
812 def delete(self, solution: str) -> None: 813 """ 814 Delete a Solution 815 Permanently deletes an imported Solution and all configs bundled with it, 816 including templates, skills, scripts, and files. The deletion runs in a 817 single transaction; provider-stored blobs are swept asynchronously after commit. 818 Org-scope callers (for example, an org admin in the Library settings page) can 819 only delete their org's copy of the Solution. App-scope callers can delete 820 Solutions at either scope. RBAC is enforced inside the core delete flow. 821 Returns `204 No Content` on success. 822 823 Args: 824 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to delete. 825 826 Returns: 827 Empty body. HTTP 204 indicates the Solution was deleted successfully. 828 """ 829 self._http.request(f"/api/v1/solutions/{solution}", method="DELETE")
Delete a Solution
Permanently deletes an imported Solution and all configs bundled with it,
including templates, skills, scripts, and files. The deletion runs in a
single transaction; provider-stored blobs are swept asynchronously after commit.
Org-scope callers (for example, an org admin in the Library settings page) can
only delete their org's copy of the Solution. App-scope callers can delete
Solutions at either scope. RBAC is enforced inside the core delete flow.
Returns 204 No Content on success.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to delete.
Returns:
Empty body. HTTP 204 indicates the Solution was deleted successfully.
831 def get(self, solution: str) -> SolutionSummary: 832 """ 833 Retrieve a Solution 834 Returns a single Solution identified by its config ID (`cfg_...`) or 835 `lookup_key`, in the same summary shape the list endpoint emits. The response 836 always includes a freshly-minted `readme_url` call this endpoint to refresh 837 an expired README token without making any other state change. 838 Visibility matches the list endpoint: app-level Solutions (no org affiliation) 839 are visible to everyone including unauthenticated callers, so the public 840 catalog can render a Solution's detail page logged-out; org-scoped Solutions 841 are only visible to authenticated viewers whose org context matches. Anything 842 else returns 404. 843 When the resolved Solution is org-scoped, the endpoint compares its 844 `solution_version` against the matching app-level copy. If the app-level copy 845 is at a higher version the response includes `upgrade_available: true` and 846 `latest_version`. App-level resolutions always report `upgrade_available: false`. 847 848 Args: 849 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to retrieve. 850 851 Returns: 852 Solution summary including a freshly-minted `readme_url` token valid for one hour. 853 """ 854 return self._http.request(f"/api/v1/solutions/{solution}", response_type=SolutionSummary)
Retrieve a Solution
Returns a single Solution identified by its config ID (cfg_...) or
lookup_key, in the same summary shape the list endpoint emits. The response
always includes a freshly-minted readme_url call this endpoint to refresh
an expired README token without making any other state change.
Visibility matches the list endpoint: app-level Solutions (no org affiliation)
are visible to everyone including unauthenticated callers, so the public
catalog can render a Solution's detail page logged-out; org-scoped Solutions
are only visible to authenticated viewers whose org context matches. Anything
else returns 404.
When the resolved Solution is org-scoped, the endpoint compares its
solution_version against the matching app-level copy. If the app-level copy
is at a higher version the response includes upgrade_available: true and
latest_version. App-level resolutions always report upgrade_available: false.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to retrieve.
Returns:
Solution summary including a freshly-minted
readme_urltoken valid for one hour.
856 def dependents(self, solution: str) -> SolutionDependentsResponse: 857 """ 858 Preview Solution delete impact 859 Returns a read-only preview of what deleting the specified Solution would 860 affect: the agents that reference the Solution's bundle, and the count of 861 bundled configs that would be orphaned rather than cascade-deleted. 862 Use this endpoint before calling `DELETE /api/v1/solutions/:solution` to 863 surface a warning when live agents depend on the Solution. 864 Visibility scope mirrors the delete endpoint: org-scope viewers see their 865 org's copy; app-scope viewers can inspect either scope. 866 867 Args: 868 solution: Solution config ID (`cfg_...`) or `lookup_key` of the Solution to inspect. 869 870 Returns: 871 Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete. 872 """ 873 return self._http.request( 874 f"/api/v1/solutions/{solution}/dependents", 875 response_type=SolutionDependentsResponse, 876 )
Preview Solution delete impact
Returns a read-only preview of what deleting the specified Solution would
affect: the agents that reference the Solution's bundle, and the count of
bundled configs that would be orphaned rather than cascade-deleted.
Use this endpoint before calling DELETE /api/v1/solutions/:solution to
surface a warning when live agents depend on the Solution.
Visibility scope mirrors the delete endpoint: org-scope viewers see their
org's copy; app-scope viewers can inspect either scope.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the Solution to inspect.
Returns:
Agents that reference this Solution's bundle plus the count of configs that would be orphaned on delete.
878 def image( 879 self, solution: str, token: str, *, v: str | None = None, file: str | None = None 880 ) -> dict[str, str]: 881 """ 882 Fetch a Solution cover image or gallery screenshot 883 Returns the raw bytes of a Solution's cover image the bundled asset the 884 Solution body's `image:` field names or, with the optional `file` param, 885 one of its gallery screenshots (`screenshots:` entries). This endpoint backs 886 the `image_url` and `screenshot_urls` fields of catalog payloads (such as 887 `GET /api/v1/solutions`), which anonymous consumers the public 888 marketplace's page cache, OpenGraph scrapers may hold far longer than a 889 signed storage URL lives. Authorization is performed via a short, stable 890 capability `token` rather than an HTTP header, so the URL never expires. 891 The `token` is an HMAC-based capability tied to the Solution config ID. It 892 does not expire, but the endpoint checks at fetch time that the Solution 893 still declares the requested image without `file`, that it still declares 894 a cover; with `file`, that the path is still among the body's declared 895 `image`/`screenshots` so republishing without the asset (or hiding the 896 Solution) turns the URL into a 404. Shared caches may continue serving the 897 old image until the `Cache-Control` max-age of one hour elapses. `v` is an 898 opaque cache key minted alongside the token; it changes when the Solution 899 changes and is ignored by verification. 900 All failure modes invalid config ID, invalid token, hidden Solution, no 901 declared cover, a `file` outside the declared set, or an asset that doesn't 902 resolve to a bundled image return a uniform `404` to avoid acting as an 903 existence oracle. 904 905 Args: 906 solution: Solution config ID (`cfg_...`) whose cover image to fetch. 907 token: HMAC capability token authorizing access to this Solution's cover. Obtained from the `image_url` minted when the Solution was serialized. 908 v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification. 909 file: Bundled asset path of the image to serve (for example `images/setup.png`). Must be one of the paths the Solution body currently declares in `image`/`screenshots` anything else is a `404`. When absent the declared cover (`image:`) is served. 910 911 Returns: 912 Raw cover image bytes, served with the asset's image content type. 913 """ 914 query: dict[str, object] = {} 915 query["token"] = token 916 if v is not None: 917 query["v"] = v 918 if file is not None: 919 query["file"] = file 920 return self._http.request_raw(f"/api/v1/solutions/{solution}/image", query=query)
Fetch a Solution cover image or gallery screenshot
Returns the raw bytes of a Solution's cover image the bundled asset the
Solution body's image: field names or, with the optional file param,
one of its gallery screenshots (screenshots: entries). This endpoint backs
the image_url and screenshot_urls fields of catalog payloads (such as
GET /api/v1/solutions), which anonymous consumers the public
marketplace's page cache, OpenGraph scrapers may hold far longer than a
signed storage URL lives. Authorization is performed via a short, stable
capability token rather than an HTTP header, so the URL never expires.
The token is an HMAC-based capability tied to the Solution config ID. It
does not expire, but the endpoint checks at fetch time that the Solution
still declares the requested image without file, that it still declares
a cover; with file, that the path is still among the body's declared
image/screenshots so republishing without the asset (or hiding the
Solution) turns the URL into a 404. Shared caches may continue serving the
old image until the Cache-Control max-age of one hour elapses. v is an
opaque cache key minted alongside the token; it changes when the Solution
changes and is ignored by verification.
All failure modes invalid config ID, invalid token, hidden Solution, no
declared cover, a file outside the declared set, or an asset that doesn't
resolve to a bundled image return a uniform 404 to avoid acting as an
existence oracle.
Arguments:
- solution: Solution config ID (
cfg_...) whose cover image to fetch. - token: HMAC capability token authorizing access to this Solution's cover. Obtained from the
image_urlminted when the Solution was serialized. - v: Opaque cache key minted alongside the token; changes when the Solution changes. Ignored by token verification.
- file: Bundled asset path of the image to serve (for example
images/setup.png). Must be one of the paths the Solution body currently declares inimage/screenshotsanything else is a404. When absent the declared cover (image:) is served.
Returns:
Raw cover image bytes, served with the asset's image content type.
922 def install(self, solution: str, input: SolutionInstallInput) -> SolutionInstallResponse: 923 """ 924 Install a Solution 925 Provisions a runtime resource from an already-imported Solution. The type of 926 resource created depends on the template the Solution wraps: an 927 `AgentTemplate` produces an Agent, an `AutomationTemplate` produces an 928 Automation, and attachment templates (`AgentRoutineTemplate`, 929 `AgentToolTemplate`, `AgentSkillTemplate`, `AgentComputerTemplate`) attach a 930 sub-resource to an existing Agent specified by `target`. 931 For Solutions that bundle more than one template, pass `template` (the ID or 932 `lookup_key` of the desired template) to select which one to provision. 933 Single-template Solutions do not require `template`. 934 Pairs with `POST /api/v1/solutions` (import): import puts the Solution into 935 the library; install provisions a runtime resource from it. 936 937 Args: 938 solution: Config ID (`cfg_...`) or `lookup_key` of the already-imported Solution to install from. 939 input: Request body. 940 input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team. 941 input.allow_auto_import: When `true`, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicit `org` param. Defaults to `false`; without it the endpoint returns 404 when the org-scoped Solution is missing. 942 input.details: Template-specific install options selected by the `type` discriminator. AutomationTemplate installs accept `{type: "automation", prefills: ...}`. 943 input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use `{values: {customer_label: "Acme"}}`. 944 input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's `agent_key`). 945 input.lookup_key_suffix: Suffix appended to every `config_ref:` resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. 946 input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions. 947 input.org: Organization ID (`org_...`) for the install destination scope. 948 input.target: ID or `lookup_key` of the parent Agent to attach to. Required when installing an `AgentRoutineTemplate`, `AgentToolTemplate`, `AgentSkillTemplate`, or `AgentComputerTemplate` Solution, since those produce sub-resources attached to an existing Agent. Omit for `AgentTemplate` and `AutomationTemplate` Solutions, which provision standalone resources. 949 input.team: Team ID (`team_...`) for the install destination scope. 950 input.template: Config ID (`cfg_...`) or `lookup_key` of the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. 951 input.user: User ID (`usr_...`) for the install destination scope. 952 953 Returns: 954 The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID. 955 """ 956 return self._http.request( 957 f"/api/v1/solutions/{solution}/install", 958 method="POST", 959 body=input, 960 response_type=SolutionInstallResponse, 961 )
Install a Solution
Provisions a runtime resource from an already-imported Solution. The type of
resource created depends on the template the Solution wraps: an
AgentTemplate produces an Agent, an AutomationTemplate produces an
Automation, and attachment templates (AgentRoutineTemplate,
AgentToolTemplate, AgentSkillTemplate, AgentComputerTemplate) attach a
sub-resource to an existing Agent specified by target.
For Solutions that bundle more than one template, pass template (the ID or
lookup_key of the desired template) to select which one to provision.
Single-template Solutions do not require template.
Pairs with POST /api/v1/solutions (import): import puts the Solution into
the library; install provisions a runtime resource from it.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the already-imported Solution to install from. - input: Request body.
- input.acl: Access control list applied atomically to a provisioned Agent or Automation. Team grants are useful when installing into a cross-organization collaboration team.
- input.allow_auto_import: When
true, automatically imports the Solution into the target tenant before installing if the org-scoped copy does not yet exist. Requires either an authenticated org user (member or admin) or a platform-privileged caller (S2S, developer JWT) that also supplies an explicitorgparam. Defaults tofalse; without it the endpoint returns 404 when the org-scoped Solution is missing. - input.details: Template-specific install options selected by the
typediscriminator. AutomationTemplate installs accept{type: "automation", prefills: ...}. - input.install_inputs: Values applied to parameterized AgentTemplate prose during a root Agent install. Use
{values: {customer_label: "Acme"}}. - input.lookup_key: Lookup key override for the provisioned resource (for example, the Agent's
agent_key). - input.lookup_key_suffix: Suffix appended to every
config_ref:resolution at install time. Should be stable per logical install and unique per attempt allows the same Solution to be installed multiple times in the same app without collisions. - input.name: Display name override for a provisioned Agent or Automation. Ignored for attachment Solutions.
- input.org: Organization ID (
org_...) for the install destination scope. - input.target: ID or
lookup_keyof the parent Agent to attach to. Required when installing anAgentRoutineTemplate,AgentToolTemplate,AgentSkillTemplate, orAgentComputerTemplateSolution, since those produce sub-resources attached to an existing Agent. Omit forAgentTemplateandAutomationTemplateSolutions, which provision standalone resources. - input.team: Team ID (
team_...) for the install destination scope. - input.template: Config ID (
cfg_...) orlookup_keyof the template within the Solution to provision. Required when the Solution bundles more than one template; omit for single-template Solutions, where the only template is selected implicitly. - input.user: User ID (
usr_...) for the install destination scope.
Returns:
The provisioned runtime resource (Agent, Automation, AgentRoutine, AgentTool, AgentSkill, or AgentComputer) together with the source Solution's config ID.
963 def readme(self, solution: str, token: str, *, file: str | None = None) -> dict[str, str]: 964 """ 965 Retrieve a Solution README or asset 966 Serves the README markdown or a bundled asset for an imported Solution. Both 967 modes use the same path and require a short-lived signed `token` in the query 968 string rather than an `Authorization` header, so browsers can load asset URLs 969 directly from `<img src>` attributes without custom request logic. 970 When `file` is omitted the response is the Solution's `readme` field rendered 971 as `text/markdown`. All local asset references in the markdown are rewritten to 972 point back at this endpoint with `?file=PATH&token=TOKEN` so browsers can load 973 images inline without additional authentication. 974 When `file` is set the response is the raw bytes of the matching asset (a File 975 child whose `relative_path` equals `PATH`, or an inline `assets` entry by 976 name) with the asset's stored `Content-Type`. 977 Tokens are scoped to a single Solution, carry the viewer's app, org, and 978 sandbox context from the time they were minted, and expire after one hour. 979 Obtain a fresh token by calling `GET /api/v1/solutions/:solution`, which 980 always returns a newly minted `readme_url`. 981 982 Args: 983 solution: Solution config ID (`cfg_...`) identifying the Solution whose README or asset to retrieve. 984 token: Signed URL token minted by the list or show endpoint. Expires after one hour. 985 file: Relative path of the asset to retrieve (for example `images/hero.png`). When present the response is the raw asset bytes with its real `Content-Type`; when absent the response is the README markdown. 986 987 Returns: 988 README markdown (`text/markdown`) when `file` is omitted, or the raw asset bytes with the asset's `Content-Type` when `file` is set. 989 """ 990 query: dict[str, object] = {} 991 query["token"] = token 992 if file is not None: 993 query["file"] = file 994 return self._http.request_raw(f"/api/v1/solutions/{solution}/readme", query=query)
Retrieve a Solution README or asset
Serves the README markdown or a bundled asset for an imported Solution. Both
modes use the same path and require a short-lived signed token in the query
string rather than an Authorization header, so browsers can load asset URLs
directly from <img src> attributes without custom request logic.
When file is omitted the response is the Solution's readme field rendered
as text/markdown. All local asset references in the markdown are rewritten to
point back at this endpoint with ?file=PATH&token=TOKEN so browsers can load
images inline without additional authentication.
When file is set the response is the raw bytes of the matching asset (a File
child whose relative_path equals PATH, or an inline assets entry by
name) with the asset's stored Content-Type.
Tokens are scoped to a single Solution, carry the viewer's app, org, and
sandbox context from the time they were minted, and expire after one hour.
Obtain a fresh token by calling GET /api/v1/solutions/:solution, which
always returns a newly minted readme_url.
Arguments:
- solution: Solution config ID (
cfg_...) identifying the Solution whose README or asset to retrieve. - token: Signed URL token minted by the list or show endpoint. Expires after one hour.
- file: Relative path of the asset to retrieve (for example
images/hero.png). When present the response is the raw asset bytes with its realContent-Type; when absent the response is the README markdown.
Returns:
README markdown (
text/markdown) whenfileis omitted, or the raw asset bytes with the asset'sContent-Typewhenfileis set.
996 def reconcile_product_knowledge(self, solution: str) -> ProductKnowledgeReconcileResponse: 997 """ 998 Reconcile Customer Management product knowledge 999 Recomputes every stamped customer's knowledge-search source allowlist 1000 from the template-resolved customer refs plus the install's current marked 1001 product-knowledge sources. Safe to retry; partial failures are returned by 1002 customer key rather than hidden behind an aggregate success. 1003 1004 Args: 1005 solution: Installed Customer Management Solution config (`cfg_...`) to reconcile. 1006 1007 Returns: 1008 Per-customer convergence result for this installed solution. 1009 """ 1010 return self._http.request( 1011 f"/api/v1/solutions/{solution}/reconcile_product_knowledge", 1012 method="POST", 1013 response_type=ProductKnowledgeReconcileResponse, 1014 )
Reconcile Customer Management product knowledge Recomputes every stamped customer's knowledge-search source allowlist from the template-resolved customer refs plus the install's current marked product-knowledge sources. Safe to retry; partial failures are returned by customer key rather than hidden behind an aggregate success.
Arguments:
- solution: Installed Customer Management Solution config (
cfg_...) to reconcile.
Returns:
Per-customer convergence result for this installed solution.
1016 def upgrade(self, solution: str, input: SolutionUpgradeInput) -> SolutionUpgradeResponse: 1017 """ 1018 Upgrade an installed Solution 1019 Applies an incoming bundle to an already-installed Solution in a single atomic 1020 transaction, bringing its configs in line with the new bundle. Config IDs are 1021 preserved across the upgrade. Configs that existed in the old bundle but are 1022 absent from the new one are orphaned (top-level) or hard-deleted (child rows). 1023 Two mutually exclusive source modes: pass `target_solution` to pull the 1024 incoming bundle from an existing Solution by ID or `lookup_key`, or pass 1025 `solution_bundle` to supply a complete inline bundle directly. Exactly one 1026 must be present. 1027 When `dry_run` is `true` the full diff is computed and returned but no 1028 changes are written. Pass the dry-run response's `review_fingerprint` as 1029 `expected_review_fingerprint` when applying to guard against the bundle 1030 changing between review and apply. 1031 1032 Args: 1033 solution: Config ID (`cfg_...`) or `lookup_key` of the currently installed Solution to upgrade. 1034 input: Request body. 1035 input.allow_downgrade: When `true`, permits an incoming `solution_version` lower than the currently installed version. Defaults to `false`. 1036 input.dry_run: When `true`, computes and returns the full upgrade diff without persisting any changes. Defaults to `false`. 1037 input.expected_review_fingerprint: Optional stale-review guard. Pass the `review_fingerprint` returned by a prior `dry_run` call to ensure the bundle has not changed between review and apply. 1038 input.org: Organization ID (`org_...`) used to resolve org-scoped `lookup_key` values. Config IDs (`cfg_...`) are globally unique and do not require this. 1039 input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with `target_solution`. 1040 input.target_solution: Config ID (`cfg_...`) or `lookup_key` of the Solution to use as the incoming upgrade source. Mutually exclusive with `solution_bundle`. 1041 1042 Returns: 1043 Updated Solution plus the full upgrade diff. 1044 """ 1045 return self._http.request( 1046 f"/api/v1/solutions/{solution}/upgrade", 1047 method="POST", 1048 body=input, 1049 response_type=SolutionUpgradeResponse, 1050 )
Upgrade an installed Solution
Applies an incoming bundle to an already-installed Solution in a single atomic
transaction, bringing its configs in line with the new bundle. Config IDs are
preserved across the upgrade. Configs that existed in the old bundle but are
absent from the new one are orphaned (top-level) or hard-deleted (child rows).
Two mutually exclusive source modes: pass target_solution to pull the
incoming bundle from an existing Solution by ID or lookup_key, or pass
solution_bundle to supply a complete inline bundle directly. Exactly one
must be present.
When dry_run is true the full diff is computed and returned but no
changes are written. Pass the dry-run response's review_fingerprint as
expected_review_fingerprint when applying to guard against the bundle
changing between review and apply.
Arguments:
- solution: Config ID (
cfg_...) orlookup_keyof the currently installed Solution to upgrade. - input: Request body.
- input.allow_downgrade: When
true, permits an incomingsolution_versionlower than the currently installed version. Defaults tofalse. - input.dry_run: When
true, computes and returns the full upgrade diff without persisting any changes. Defaults tofalse. - input.expected_review_fingerprint: Optional stale-review guard. Pass the
review_fingerprintreturned by a priordry_runcall to ensure the bundle has not changed between review and apply. - input.org: Organization ID (
org_...) used to resolve org-scopedlookup_keyvalues. Config IDs (cfg_...) are globally unique and do not require this. - input.solution_bundle: Complete inline bundle for a direct upgrade, including Solution metadata, templates, skills, configs, files, and setup actions. Mutually exclusive with
target_solution. - input.target_solution: Config ID (
cfg_...) orlookup_keyof the Solution to use as the incoming upgrade source. Mutually exclusive withsolution_bundle.
Returns:
Updated Solution plus the full upgrade diff.
1052 def view(self, solution: str, input: SolutionViewInput) -> None: 1053 """ 1054 Track a Solution detail-page view 1055 Records a `solution_viewed` analytics event for the identified Solution and 1056 returns `204 No Content`. Fired by the marketplace when a Solution's detail 1057 page is rendered in a browser, so publishers can see impressions alongside 1058 installs in their Solution analytics. 1059 Visibility matches `GET /api/v1/solutions/:solution`: unauthenticated callers 1060 (the logged-out marketplace) can only track Solutions published to the public 1061 catalog; anything the caller could not retrieve returns 404 and records 1062 nothing. 1063 Pass `anonymous` (the analytics visitor ID) so logged-out views can be 1064 counted as unique viewers. The event's Solution and publisher attribution are 1065 resolved server-side from the Solution row never from request input. 1066 1067 Args: 1068 solution: Solution config ID (`cfg_...`) or `lookup_key` of the viewed Solution. 1069 input: Request body. 1070 input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the `POST /api/v1/t` events use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session. 1071 1072 Returns: 1073 Empty body. HTTP 204 indicates the Solution view was recorded successfully. 1074 """ 1075 self._http.request(f"/api/v1/solutions/{solution}/view", method="POST", body=input)
Track a Solution detail-page view
Records a solution_viewed analytics event for the identified Solution and
returns 204 No Content. Fired by the marketplace when a Solution's detail
page is rendered in a browser, so publishers can see impressions alongside
installs in their Solution analytics.
Visibility matches GET /api/v1/solutions/:solution: unauthenticated callers
(the logged-out marketplace) can only track Solutions published to the public
catalog; anything the caller could not retrieve returns 404 and records
nothing.
Pass anonymous (the analytics visitor ID) so logged-out views can be
counted as unique viewers. The event's Solution and publisher attribution are
resolved server-side from the Solution row never from request input.
Arguments:
- solution: Solution config ID (
cfg_...) orlookup_keyof the viewed Solution. - input: Request body.
- input.anonymous: Analytics visitor ID to attribute the view to, for unique-viewer counting. Same identifier the
POST /api/v1/tevents use; the marketplace sends it on every view. Authenticated callers additionally get user attribution from their session.
Returns:
Empty body. HTTP 204 indicates the Solution view was recorded successfully.