Storage

Preview — pending review

This reference is generated from the platform API contract and is still under review. Operations, fields, and descriptions can change before publication.

Detach a volume from its instance

POST
/scopes/{scopeId}/storage/volumes/{volumeId}/actions/detach

Detaches the block volume volumeId from the instance it is currently attached to, releasing the device path. Triggered from the volume detail header, the attachment panel and the per-row "Detach" action. On success attachedTo becomes null and the status moves from IN_USE back to AVAILABLE; the resulting state is visible to subsequent reads. Validation: volumeId must reference an existing volume in the scope (unknown → 404 NOT_FOUND); the volume must currently be attached — detaching a detached volume → 409 CONFLICT; for multi-attach volumes the optional instanceId selects which attachment to remove and must match an existing attachment (mismatch → 404/409); root/boot volumes of a running instance cannot be detached while the instance is running (→ 409 CONFLICT); caller needs owner|admin|contributor (→ 403). Async & state: Conditional — a fast detach completes synchronously with 200 returning the updated Volume (IN_USE → AVAILABLE, attachedTo: null); a slow detach (in-flight I/O draining) returns 202 with an Operation and the same terminal transition. Progress is emitted as resource.updated SSE frames (kind storage.volume, patch {status, attachedTo, updatedAt}). Errors: 401 unauthenticated; 403 caller lacks permission on the scope; 404 unknown scopeId/volumeId or no matching attachment; 409 CONFLICT volume not attached, boot volume of a running instance, or state does not permit detach; 412 If-Match mismatch; 422 malformed body; 500 unexpected backend failure. Idempotency/Concurrency: Idempotency-Key is optional (recommended); replaying the same key returns the original outcome. If-Match with the volume ETag guards against racing a concurrent attach/detach.

Authorization

bearerToken
headerAuthorizationBearer <token>

An API token (pc_live_…) or a service-account key (sak_live_…), created in Identity & Access Management and sent as Authorization: Bearer <token>. Requests are rate limited per credential and per tenant: every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and a refused request gets 429 RATE_LIMITED with Retry-After.

Path Parameters

scopeId*string

Opaque ownership+authorization scope resolving to tenant + subscription + project.

volumeId*string

Identifier of the block volume.

Header Parameters

Idempotency-Key?string

Optional; recommended for safe retries. When sent, the same key+body within 24h replays the original result and the same key with a different body is 409 CONFLICT; keys are unique per tenant. When an API token or service-account (bearer) caller omits it, the request is applied once with no replay protection (a retry may apply it again). A console session gets one from the edge.

Formatuuid
If-Match?string

ETag for optimistic concurrency on mutations/deletes; mismatch results in 412.

Request Body

application/json
  1. body

Optional payload for detaching a volume; only needed to disambiguate multi-attach volumes.

instanceId?|

For multi-attach volumes, the specific instance attachment to remove; omitted detaches the sole attachment.

Response Body

Detach completed synchronously; returns the updated volume (status AVAILABLE, attachedTo null).

application/json
  1. response

Block volume read model as rendered by the Volumes page and the volume detail page.

origin?string

Where the resource was made -- portal for a create on the console, api for an object made straight against the OpenStack API (CLI, Terraform, the Kubernetes cloud provider) that discovery adopted. Informational; nothing behaves differently. Absent from a backend that predates it, which means portal.

Value in"portal""api""system""imported"
id*string

Volume identifier (e.g. vol-0a1b2c3d).

name*string

Volume display name.

status*string

Lifecycle status of the volume.

Value in"CREATING""AVAILABLE""IN_USE""AWAITING_TRANSFER""ERROR"
statusReason*|

Human-readable, customer-facing reason for the current status when it is a failure (e.g. "This request exceeds the quota available to this project. Free up existing resources or request a quota increase." on an ERROR volume); never raw provider text. Null while the volume is healthy or the failure carried no detail.

statusReasonCode*|

Machine-readable companion to statusReason: a small, OPEN vocabulary of codes the console can branch on without parsing the human text, deliberately a plain string rather than a closed enum so a code the client does not recognize yet degrades to the generic failure message. Today the only value is cancelled: the resource is in its failure state because its create operation was CANCELLED; a provider object the create had already built may be left in place, and the resource's DELETE removes it. Reported only while the resource is in that failure state; null otherwise.

sizeGb*integer

Provisioned size in GB (1–16,384).

type*|

Volume performance tier. Null when the volume sits on a provider volume type no platform tier maps to (e.g. a volume adopted from an imported project): the tier is then UNKNOWN and unpriced, never reported as a tier it is not. providerVolumeType carries the provider's own type name in that case.

providerVolumeType*|

The provider's (Cinder's) own volume type name, present only when type is null because no platform tier maps to it (an adopted volume on a legacy type). Null for a volume on a platform tier. Display-only: it has no price and cannot be requested.

attachedTo*|null

Instance attachment (instance id, name, device), or null when detached.

bootable*boolean

Whether the volume carries a bootable OS image.

encrypted*boolean

Whether the volume is encrypted at rest, DERIVED from the provider's own view of the volume (the volume type's encryption spec) — never from the create request. False until the provider confirms encryption, so this field never asserts a security property the platform did not actually deliver.

multiAttach*boolean

Allow attaching to more than one instance (RWX).

availabilityZone*string

Availability zone the volume lives in (e.g. ist-1a).

description*string

Operator-supplied description.

source*

Provenance of a block volume — empty, created from an image, restored from a snapshot, or cloned from another volume.

kmsKeyId*|

Customer-managed KMS key for envelope encryption; null when platform-managed or encryption is off.

keyRotationDays*integer

Automatic key-rotation period in days (0 = manual).

pricePerGbMonthlyCents*|

Price per GB per month in hundredths of the currency unit (an integer, not a Money object): the rate billing meters this volume's tier at for the volume's subscription, rounded down to the hundredth. Null when no price applies to the tier: there is no per-type default figure.

deletionProtection*boolean

Platform-owned deletion guard. While true, storage.volume.delete is refused with 409 DELETION_PROTECTED before any work starts; turn it off with the metadata PATCH, then delete. It guards the console and this API ONLY: Cinder has no lock, so an OpenStack API credential with Full access can still delete the volume directly. False for every volume unless a caller turned it on; the collector never writes it.

tags*array<>

Key-value pairs shown on the Tags tab; double as Cost Explorer dimensions.

createdAt*string

Creation timestamp (ISO 8601).

Formatdate-time
updatedAt*string

Last modification timestamp (ISO 8601).

Formatdate-time
curl -X POST "https://example.com/scopes/string/storage/volumes/string/actions/detach" \  -H "Content-Type: application/json" \  -d '{}'
{  "origin": "portal",  "id": "string",  "name": "string",  "status": "CREATING",  "statusReason": "string",  "statusReasonCode": "string",  "sizeGb": 0,  "type": "Standard SSD",  "providerVolumeType": "string",  "attachedTo": {    "instanceId": "string",    "instanceName": "string",    "device": "string"  },  "bootable": true,  "encrypted": true,  "multiAttach": true,  "availabilityZone": "string",  "description": "string",  "source": {    "kind": "empty",    "ref": "string"  },  "kmsKeyId": "string",  "keyRotationDays": 0,  "pricePerGbMonthlyCents": 0,  "deletionProtection": true,  "tags": [    {      "key": "string",      "value": "string"    }  ],  "createdAt": "2019-08-24T14:15:22Z",  "updatedAt": "2019-08-24T14:15:22Z"}