Install products
POST /api/v1/products/install: request fields and declared response contract.
POST/api/v1/products/install
| Permission | products.install |
|---|---|
| Workspace scope | required |
| Scope target | workspace |
Request body
| Field | Type | Required | Nullable | Default |
|---|---|---|---|---|
| productKey | string | required | non-null | — |
| productVersion | string | required | non-null | — |
| namespace | string | optional | non-null | — |
| idempotencyKey | string | required | non-null | — |
| expectedPlan | object | required | non-null | — |
| expectedPlan.planSchemaVersion | number | required | non-null | — |
| expectedPlan.planHash | string | required | non-null | — |
| bindings | object | optional | non-null | — |
| productConfig | object | optional | non-null | — |
| referenceInputs | object | optional | non-null | — |
| profile | object | optional | non-null | — |
| profile.profileKey | string | required | non-null | — |
| profile.profileVersion | string | required | non-null | — |
| bindingAnswers | object | optional | non-null | — |
| roleSatisfactions | object | optional | non-null | — |
| acceptedRecommendations | object | optional | non-null | — |
Response
201
| Field | Type | Required | Nullable | Default |
|---|---|---|---|---|
| data | object | required | non-null | — |
| data.attempt | object | required | non-null | — |
| data.attempt.id | string | required | non-null | — |
| data.attempt.companyId | string | required | non-null | — |
| data.attempt.workspaceId | string | required | non-null | — |
| data.attempt.installationId | string | required | non-null | — |
| data.attempt.planId | string | required | non-null | — |
| data.attempt.attemptKey | integer | required | non-null | — |
| data.attempt.invocationKey | string | required | non-null | — |
| data.attempt.operation | "install" | "upgrade" | required | non-null | "install" |
| data.attempt.state | "running" | "succeeded" | "failed_retryable" | "failed_permanent" | "requires_resolution" | required | non-null | — |
| data.attempt.executionFence | integer | required | non-null | 0 |
| data.attempt.executionClaimId | unknown | required | non-null | — |
| data.attempt.executionClaimedAt | unknown | required | non-null | — |
| data.attempt.executionClaimExpiresAt | unknown | required | non-null | — |
| data.attempt.startedAt | unknown | required | non-null | — |
| data.attempt.completedAt | unknown | required | non-null | — |
| data.attemptState | "running" | "succeeded" | "failed_retryable" | "failed_permanent" | "requires_resolution" | required | non-null | — |
| data.installationState | "installing" | "partial" | "installed" | "requires_resolution" | "failed" | "retired" | required | non-null | — |
| data.completedUnitCount | integer | required | non-null | — |
| data.failedNodeKey | string | optional | non-null | — |
| data.failedUnitKind | "identity" | "owned_content" | optional | non-null | — |
| data.terminalReceipt | object | optional | non-null | — |
Declared failures
| PRODUCT_OPERATION_FAILED | 500 — not retryable |
|---|---|
| PRODUCT_REQUIRES_RESOLUTION | 409 — not retryable |
| PRODUCT_STALE | 409 — not retryable |
Readback
Read the result back with:
- products.inspectInstalled
Request example
Schematic only. Replace placeholders with real values and include all required nested fields.
POST /api/v1/products/install HTTP/1.1
Authorization: Bearer <token>
x-merulan-workspace-id: <workspace-id>
Content-Type: application/json
{
"productKey": "<productKey>",
"productVersion": "<productVersion>",
"idempotencyKey": "<idempotencyKey>",
"expectedPlan": {}
}