Outcome
Remove one visible local placement while preserving the provider resource and the placement’s stored history.
DELETE /api/developer-console/placements/{placementId}
Action: app_topolo_developers.placements.delete.
Before you start
Use the original authenticated active organization with Developers connections:write and current read_write access to the placement’s consumer workspace. Discover/read the actual placement first and review the intended removal before confirmation. Being an organization admin or having provider access is not a substitute for the selected consumer-workspace check.
Input is exactly placementId matching ^plc_[A-Za-z0-9-]{1,80}$. The original organization comes from identity, and the consumer app/workspace comes from the stored row; no organization/workspace/provider id override is accepted. Preserve the complete prior placement and reference before an approved deletion.
This removes the local placement, not the provider resource, consumer object or organization. No provider grant, live reference refresh, link creation, connection enable or notification send is authorized as incidental recovery.
All four worked responses are full source-derived synthetic UNRUN carriers. No actual placement deletion/private record/provider/Notify/grant or business operation, app import/test or source write occurred. Current declaration matches immutable7b89; serving runtime is not certified.
Request
Discover the real placement first. This identifier is fictional:
{
"placementId": "plc_docs-example-b"
}
Access, deletion and partial outcomes
DELETE /api/developer-console/placements/{placementId} loads the row by original organization and placement id. A missing row, truthy removedAt or consumer workspace absent from the caller’s current workspace list returns404 placement_not_found with the same message. Missing Auth workspace data instead returns503 workspace_access_unavailable; it is not converted into404 or an empty success.
After the live visible row is found, a consumer workspace with accessLevel read returns403 workspace_write_required. The selected helper asks for read_write access and performs no organization-admin shortcut here. It does not check the current provider workspace, provider record, connection usability or workspace-pair link before local cleanup. Those omitted checks do not grant provider authority.
The storage UPDATE writes removed_by, removed_at and updated_at on the id only while removed_at IS NULL. It does not hard-delete the row, clear the reference/label, change status/statusReason or refresh lastCheckedAt. In particular provider-derived status removed is distinct from local soft deletion; a deleted local row may still have status unavailable or ready in the response.
The complete normal200 envelope is {success:true,message:"OK",data:{outcome:"deleted",placement:<full stored PlacementRecord>}}. It contains id,organizationId,consumer,slot,reference,referenceString,label,status,statusReason,lastCheckedAt,createdBy,createdAt,updatedAt,removedBy,removedAt. Nullable fields and optional reference.version are retained exactly as stored/mapped. There is no previousStatus, count or provider deletion receipt added by this helper.
The write and final row reload are separate; no expectedRevision/idempotency key or whole-operation transaction is provided. The UPDATE’s rowsWritten is not checked. Concurrent removals may leave the first remover/time in the returned row while another caller also reports deleted; final hard disappearance may produce placement:null because the TypeScript nonnull assertion has no runtime guard. Do not treat a200 as a global transaction or as proof that this caller supplied the final removal stamp.
After a normal soft deletion, placements.get hides the row and a later delete returns the shared404. Visible list/used_in reads exclude soft-deleted rows. A404 after a timeout can mean a previous removal committed, a row never existed or current access changed; it does not prove which. Preserve the successful response/preimage and actual request id rather than repeating delete as verification.
The selected helper performs one local soft-removal write and one reload after consumer access lookup. It does not invoke provider business APIs, remove the provider’s resource, delete the consumer object, change grants/links/connections/subscriptions or send Notify events. Shared console authentication/dependency initialization remains outside that selected-helper statement.
The DELETE route combines path id with query input and rejects conflicting/invalid strict input with422 invalid_input, message Invalid placement request and nested details.issues. Supported CLI/MCP callers should send only the discovered placementId and validate the current contract; do not guess an id, add selectors or bypass authorization with raw query manipulation.
Worked responses
Soft-remove a visible placement without touching the provider
Complete synthetic UNRUN HTTP200. The fictional caller has consumer read_write access; the original full unavailable placement is soft-removed and the final read returns it. No concurrent writer is present in this fixture.
{
"success": true,
"message": "OK",
"data": {
"outcome": "deleted",
"placement": {
"id": "plc_docs-example-b",
"organizationId": "org_docs_fixture",
"consumer": {
"appId": "app_example_consumer",
"workspaceId": "ws_consumer_example",
"kind": "example.page",
"id": "page_other"
},
"slot": null,
"reference": {
"app": "app_example_provider",
"workspace": "ws_provider_example",
"type": "example.item",
"id": "item_example"
},
"referenceString": "topolo:app_example_provider/ws_provider_example/example.item/item_example",
"label": null,
"status": "unavailable",
"statusReason": null,
"lastCheckedAt": null,
"createdBy": "user_docs_fixture",
"createdAt": "2026-10-20T10:00:00.000Z",
"updatedAt": "2026-10-22T18:00:00.000Z",
"removedBy": "user_docs_fixture",
"removedAt": "2026-10-22T18:00:00.000Z"
}
}
}
removedAt/removedBy identify local deletion while status remains unavailable and lastCheckedAt stays null. The provider reference and all other historical fields are preserved.
A visible consumer workspace is read-only
Complete synthetic UNRUN HTTP403 from requireVisiblePlacement after the live row and consumer workspace are found with accessLevel read. Unlike another requireWorkspace helper, this branch does not append error.details.
{
"success": false,
"error": {
"message": "You can only read workspace ws_consumer_example",
"code": "workspace_write_required"
}
}
Do not widen grants or switch identity automatically. Have the legitimate consumer-workspace owner review the requested removal.
The placement is absent, removed or not visible
Complete synthetic UNRUN HTTP404; the same carrier covers missing/soft-deleted rows and absent consumer-workspace access.
{
"success": false,
"error": {
"message": "No placement plc_docs-example-b you can see in this organization",
"code": "placement_not_found"
}
}
Preserve original context and any previous write evidence. Do not recreate the placement or infer provider deletion from this refusal.
Current consumer workspace access cannot be confirmed
Complete synthetic UNRUN HTTP503 when the current workspace dependency returns null before the selected placement write. Shared context initialization is outside this no-placement-write statement.
{
"success": false,
"error": {
"message": "Topolo Auth could not confirm your workspaces; retry later",
"code": "workspace_access_unavailable"
}
}
Resolve the legitimate Auth dependency through its owner before considering a reviewed later attempt; do not treat unavailable access as an empty workspace list.
Verify and recover
-
Retain actual data.outcome and the complete returned placement, especially original id/consumer/reference, removedBy/removedAt and updatedAt. Confirm preserved source fields and the intended local removal rather than interpreting status alone as deletion.
-
A separately authorized placements.get should no longer expose the soft-deleted row, but its shared404 does not independently distinguish deletion from missing access. Reconcile it with the retained mutation response and preimage, not by probing another organization or invoking the provider.
-
After an error/timeout, preserve actual request id and reconcile existing consumer-visible state and owner evidence before any further mutation. Do not blind-retry delete, erase provider data or create a replacement to conceal uncertain effects.
-
There is no undelete operation in this selected route. A separately approved placements.create has its own current declaration/provider/workspace/slot checks and may produce a different identity; it is not an automatic exact restoration of this row or its history.
No automatic inverse or same-row undelete. Reconcile actual removal and retained reference first. Any reviewed new placement creation has current access/provider/declaration/slot prerequisites and may use a different identity; do not blindly recreate, refresh the provider or broaden grants after an uncertain delete.
Related actions
- Get a visible placement before planning removal and to interpret the shared404 afterward.
- List visible consumer placements without treating a bounded empty list as global absence proof.
Change log
- 2026-10-22: Added exact consumer write-access and404/503 boundaries, full soft-removal carrier, unchanged stored-status/reference semantics and uncertain-outcome recovery. All examples synthetic UNRUN; no provider or local deletion was executed.