committed workspace with applied or pending listing
API reference sections
HTTP API reference
Workspaces
Workspaces, their manifests, listings, pins and lifecycle.
34 operations. The contract has no example values, so example bodies are generated from each schema: every field is shown with a placeholder of its type.
PUT/v1/workspaces/{workspace}/pinned
- Authorization
- Bearer token with
manage_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| workspacerequired | path | string |
Request body (required)
application/json WorkspacePin
| Field | Type | Description |
|---|---|---|
| pinnedrequired | boolean |
{
"pinned": false
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | PinnedWorkspace |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
},
"listing": {
"state": "applied"
}
}curl -X PUT "http://127.0.0.1:$PORT/v1/workspaces/$WORKSPACE/pinned" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pinned": false
}'POST/v1/workspaces/{workspace}/reconcile-listing
reconciled workspace; uncertainty refuses repair
- Authorization
- Bearer token with
manage_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| workspacerequired | path | string |
Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$WORKSPACE/reconcile-listing" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/workspaces/unassigned
each live workspace placed under nobody, under a deleted or absent Profile, or under a legacy holder, with its former Profile; moving one is the ordinary move, restoring its Profile is the batch restore
- Authorization
- Bearer token with
manage_workspaces
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | array of UnassignedWorkspace |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
[
{
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
},
"former_profile": "00000000-0000-4000-8000-000000000000",
"reason": "explicitly_unassigned"
}
]curl "http://127.0.0.1:$PORT/v1/workspaces/unassigned" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/workspaces
an array of workspaces
- Authorization
- Bearer token with
read_documents
Responses
[
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}
]curl "http://127.0.0.1:$PORT/v1/workspaces" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces
the workspace, with its new immutable id
- Authorization
- Bearer token with
manage_workspaces
Request body (required)
application/json NewWorkspace
| Field | Type | Description |
|---|---|---|
| ownerrequired | ProfileId | |
| namerequired | string |
{
"owner": "00000000-0000-4000-8000-000000000000",
"name": "string"
}Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owner": "00000000-0000-4000-8000-000000000000",
"name": "string"
}'GET/v1/workspaces/{id}
a workspace
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl "http://127.0.0.1:$PORT/v1/workspaces/$ID" \
-H "Authorization: Bearer $KORU_TOKEN"PATCH/v1/workspaces/{id}
the workspace, renamed
- Authorization
- Bearer token with
manage_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json RenameWorkspace
| Field | Type | Description |
|---|---|---|
| namerequired | string |
{
"name": "string"
}Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl -X PATCH "http://127.0.0.1:$PORT/v1/workspaces/$ID" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "string"
}'DELETE/v1/workspaces/{id}
the workspace with deleted_at set. Takes destroy_workspaces, which no ordinary token holds: removing a workspace closes its terminals, and the Gateway's lifecycle operation is what does that first
- Authorization
- Bearer token with
destroy_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl -X DELETE "http://127.0.0.1:$PORT/v1/workspaces/$ID" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/archive-replacing
authority, listing, and replacement facts; HTTP 409 listing_pending retains committed authority
- Authorization
- Bearer token with
destroy_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json RemoveWorkspaceReplacing
| Field | Type | Description |
|---|---|---|
| namerequired | string | Localized name supplied by the caller for a required replacement. |
| force | boolean | Default |
| removal_attempt | RemovalAttemptId or null |
{
"name": "string",
"force": false,
"removal_attempt": "00000000-0000-4000-8000-000000000000"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | RemovedWorkspace |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
},
"authority_committed": false,
"listing": {
"state": "applied"
},
"replacement": {
"state": "created",
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/archive-replacing" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"force": false,
"removal_attempt": "00000000-0000-4000-8000-000000000000"
}'POST/v1/workspaces/{id}/delete-replacing
authority, listing, and replacement facts; HTTP 409 listing_pending retains committed authority
- Authorization
- Bearer token with
destroy_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json RemoveWorkspaceReplacing
| Field | Type | Description |
|---|---|---|
| namerequired | string | Localized name supplied by the caller for a required replacement. |
| force | boolean | Default |
| removal_attempt | RemovalAttemptId or null |
{
"name": "string",
"force": false,
"removal_attempt": "00000000-0000-4000-8000-000000000000"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | RemovedWorkspace |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
},
"authority_committed": false,
"listing": {
"state": "applied"
},
"replacement": {
"state": "created",
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/delete-replacing" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"force": false,
"removal_attempt": "00000000-0000-4000-8000-000000000000"
}'POST/v1/workspaces/{id}/move-replacing
the moved workspace, previous owner, and replacement facts
- Authorization
- Bearer token with
migrate_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json MoveWorkspaceReplacing
| Field | Type | Description |
|---|---|---|
| ownerrequired | ProfileId | |
| namerequired | string |
{
"owner": "00000000-0000-4000-8000-000000000000",
"name": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | MovedWorkspace |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
},
"from": "00000000-0000-4000-8000-000000000000",
"replacement": {
"state": "created",
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/move-replacing" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owner": "00000000-0000-4000-8000-000000000000",
"name": "string"
}'POST/v1/workspaces/{id}/restore
the workspace with deleted_at cleared. The same scope the delete takes: whoever decides what the trash contains decides both ways
- Authorization
- Bearer token with
destroy_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/restore" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/move
the workspace with its new owner and its position at the end of that profile's order. Its id, its documents and its history are untouched. Takes migrate_workspaces, which no ordinary token holds: a move closes every terminal the workspace owns, and the Gateway's migration operation is what does that first
- Authorization
- Bearer token with
migrate_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json MoveWorkspace
| Field | Type | Description |
|---|---|---|
| ownerrequired | ProfileId |
{
"owner": "00000000-0000-4000-8000-000000000000"
}Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/move" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owner": "00000000-0000-4000-8000-000000000000"
}'PATCH/v1/workspaces/{id}/appearance
the workspace. All four are sent every time - there is no absent-versus-null. Changing archived additionally takes destroy_workspaces: archiving closes the workspace's terminals and hides its projects, and it only travels here because these four fields are edited together
- Authorization
- Bearer token with
manage_workspaces
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json WorkspaceAppearance
| Field | Type | Description |
|---|---|---|
| icon | string or null | Default |
| color | string or null | Default |
| pinned | boolean | Default |
| archived | boolean | Default |
{
"icon": "string",
"color": "string",
"pinned": false,
"archived": false
}Responses
{
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}curl -X PATCH "http://127.0.0.1:$PORT/v1/workspaces/$ID/appearance" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"icon": "string",
"color": "string",
"pinned": false,
"archived": false
}'GET/v1/workspaces/{id}/documents
an array of document entries in tree order
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | array of DocumentEntry |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
[
{
"id": "00000000-0000-4000-8000-000000000000",
"parent": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"title": "string",
"kind": "string",
"mime": "string",
"note": "00000000-0000-4000-8000-000000000000",
"trashed_at": 0,
"trashed_by": "string"
}
]curl "http://127.0.0.1:$PORT/v1/workspaces/$ID/documents" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/documents
the new document id
- Authorization
- Bearer token with
edit_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json NewDocument
| Field | Type | Description |
|---|---|---|
| id | DocumentId or null | Optional client-minted identity. Importers use this so snapshots can contain routed references to other entries in the same batch. Default |
| titlerequired | string | |
| kindrequired | string | The application's own kind - |
| mime | string or null | MIME type for file entries. Optional for ordinary documents and older clients. |
| parent | DocumentId or null | Absent means the workspace root. Default |
| index | integer (uint, >= 0) or null | Where among The same coordinate Default |
| trashed_at | Timestamp or null | Create the manifest row already in Trash at this source timestamp. Absent preserves the pre-capability behavior for older clients. |
| snapshot | array of integer (uint8, >= 0, <= 255) or null | Optional complete initial state. Used by importers so registering the manifest row and committing content is one Store operation rather than a briefly visible empty page followed by a client update. |
{
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"kind": "string",
"mime": "string",
"parent": "00000000-0000-4000-8000-000000000000",
"index": 0,
"trashed_at": 0,
"snapshot": [
0
]
}Responses
{
"id": "00000000-0000-4000-8000-000000000000"
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/documents" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"kind": "string",
"mime": "string",
"parent": "00000000-0000-4000-8000-000000000000",
"index": 0,
"trashed_at": 0,
"snapshot": [
0
]
}'POST/v1/workspaces/{id}/documents/{document}/note
the note's document id - the existing one when the file already has a note, so the call is the answer to both Add note and Open note - and whether this call created it
- Authorization
- Bearer token with
edit_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| documentrequired | path | string |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | EnsuredFileNote |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"id": {},
"created": false
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/documents/$DOCUMENT/note" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/transfer
the document ids that moved, root first. Ids are preserved.
- Authorization
- Bearer token with
edit_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Request body (required)
application/json TransferDocuments
| Field | Type | Description |
|---|---|---|
| targetrequired | WorkspaceId | |
| rootrequired | DocumentId | DocumentId - a client-minted v4 UUID, hyphenated and lowercase. Immutable, and never derived from a name or a path. |
| parent | DocumentId or null | Default |
| index | integer (uint, >= 0) | Entry index among siblings under Default |
{
"target": "00000000-0000-4000-8000-000000000000",
"root": "00000000-0000-4000-8000-000000000000",
"parent": "00000000-0000-4000-8000-000000000000",
"index": 0
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | Transferred |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"ids": [
"00000000-0000-4000-8000-000000000000"
]
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/transfer" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target": "00000000-0000-4000-8000-000000000000",
"root": "00000000-0000-4000-8000-000000000000",
"parent": "00000000-0000-4000-8000-000000000000",
"index": 0
}'GET/v1/workspaces/{id}/manifest
a document id
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ManifestRef |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"document": "00000000-0000-4000-8000-000000000000"
}curl "http://127.0.0.1:$PORT/v1/workspaces/$ID/manifest" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/snapshot/{document}
application/octet-stream - a Loro snapshot, ready to import
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| documentrequired | path | string |
Responses
curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/snapshot/$DOCUMENT" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/workspaces/{id}/documents/{document}/history
a cursor-paginated history page
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| documentrequired | path | string |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | HistoryPage |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"entries": [
{
"id": "00000000-0000-4000-8000-000000000000",
"document": "00000000-0000-4000-8000-000000000000",
"version": "00000000-0000-4000-8000-000000000000",
"reason": "string",
"recorded_at": 0,
"label": "string",
"message": "string",
"boundary": "string",
"pinned": false
}
],
"next_before_at": 0,
"next_before_id": "00000000-0000-4000-8000-000000000000",
"stale": false
}curl "http://127.0.0.1:$PORT/v1/workspaces/$ID/documents/$DOCUMENT/history" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/documents/{document}/history
the new history marker
- Authorization
- Bearer token with
edit_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| documentrequired | path | string |
Request body (required)
application/json CaptureHistory
| Field | Type | Description |
|---|---|---|
| reasonrequired | string | One of the |
| frontiers | array of integer (uint8, >= 0, <= 255) or null | Default |
| label | string or null | Default |
| message | string or null | Default |
| boundary | string or null | Default |
{
"reason": "string",
"frontiers": [
0
],
"label": "string",
"message": "string",
"boundary": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | HistoryEntry |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"id": "00000000-0000-4000-8000-000000000000",
"document": "00000000-0000-4000-8000-000000000000",
"version": "00000000-0000-4000-8000-000000000000",
"reason": "string",
"recorded_at": 0,
"label": "string",
"message": "string",
"boundary": "string",
"pinned": false
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/documents/$DOCUMENT/history" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "string",
"frontiers": [
0
],
"label": "string",
"message": "string",
"boundary": "string"
}'POST/v1/workspaces/{id}/history/{entry}/label
the marker with its new label
- Authorization
- Bearer token with
edit_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| entryrequired | path | string |
Request body (required)
application/json RelabelHistory
| Field | Type | Description |
|---|---|---|
| label | string or null | Default |
{
"label": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | HistoryEntry |
| 401 | no bearer token, or one that is not recognised | Failure |
| 403 | the credential does not hold the required scope | Failure |
{
"id": "00000000-0000-4000-8000-000000000000",
"document": "00000000-0000-4000-8000-000000000000",
"version": "00000000-0000-4000-8000-000000000000",
"reason": "string",
"recorded_at": 0,
"label": "string",
"message": "string",
"boundary": "string",
"pinned": false
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/history/$ENTRY/label" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"label": "string"
}'POST/v1/workspaces/{id}/history/{entry}/snapshot
application/octet-stream - an immutable Loro snapshot
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| entryrequired | path | string |
Responses
curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/history/$ENTRY/snapshot" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/workspaces/{id}/versions/{version}/snapshot
application/octet-stream - an immutable Loro snapshot, with its document id, checksum and capture time on X-Koru-Document-Id, X-Koru-Checksum and X-Koru-Captured-At; document_version_not_found when the workspace holds no such version
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| versionrequired | path | string |
Responses
curl "http://127.0.0.1:$PORT/v1/workspaces/$ID/versions/$VERSION/snapshot" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/workspaces/{id}/blobs
an array of blobs - hash, size, mime, filename, created_at
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
[
{
"hash": "string",
"size": 0,
"mime": "string",
"filename": "string",
"created_at": 0,
"attributes": {
"key": {
"type": "int",
"value": 0
}
}
}
]curl "http://127.0.0.1:$PORT/v1/workspaces/$ID/blobs" \
-H "Authorization: Bearer $KORU_TOKEN"PUT/v1/workspaces/{id}/blobs
a blob. The caller does not choose the hash: the store hashes what it receives, so storing the same bytes twice makes no second copy. The filename travels percent-encoded in X-Koru-Filename, because a filename is arbitrary Unicode and a header value is not
- Authorization
- Bearer token with
edit_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
{
"hash": "string",
"size": 0,
"mime": "string",
"filename": "string",
"created_at": 0,
"attributes": {
"key": {
"type": "int",
"value": 0
}
}
}curl -X PUT "http://127.0.0.1:$PORT/v1/workspaces/$ID/blobs" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/workspaces/{id}/blobs/{hash}
application/octet-stream, fetched from an authorized connected peer when absent locally, then verified and persisted against the address
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| hashrequired | path | string |
Responses
curl "http://127.0.0.1:$PORT/v1/workspaces/$ID/blobs/$HASH" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/blobs/{hash}
the target workspace's durable blob record. No bytes are copied: one object serves every associated workspace, and the record is written before the caller publishes a CRDT reference to it
- Authorization
- Bearer token with
edit_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string | |
| hashrequired | path | string |
Request body (required)
application/json AssociateBlob
| Field | Type | Description |
|---|---|---|
| targetrequired | WorkspaceId |
{
"target": "00000000-0000-4000-8000-000000000000"
}Responses
{
"hash": "string",
"size": 0,
"mime": "string",
"filename": "string",
"created_at": 0,
"attributes": {
"key": {
"type": "int",
"value": 0
}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/blobs/$HASH" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target": "00000000-0000-4000-8000-000000000000"
}'POST/v1/workspaces/{id}/export
application/octet-stream - manifest, a snapshot per document, then the blobs. A POST because it is work; it changes nothing
- Authorization
- Bearer token with
read_documents
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string |
Responses
curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/export" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/workspaces/{id}/lifecycle/confirmation
The consequence - the terminals this action would close, named - and a single-use token bound to this workspace and this action. The caller presents the consequence, and sends the token back in the lifecycle request if the person accepts. The same scope as the operation itself: computing what would be destroyed is not a smaller permission than destroying it.
- Authorization
- Bearer token with
workspace.destroy - Operation id
- workspace_lifecycle_confirmation
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string (uuid) |
Request body (required)
application/json WorkspaceConfirmationRequest
| Field | Type | Description |
|---|---|---|
| actionrequired | WorkspaceLifecycleAction | What Three verbs, and no permanent delete. There is no purge route anywhere in Koru - the retention sweep is the only thing that removes bytes - so naming one here would be describing an operation that does not exist. |
{
"action": "archive"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | WorkspaceConfirmation |
| 400 | The body is not a confirmation request | |
| 401 | The gateway bearer is absent or invalid | |
| 403 | The session does not hold workspace.destroy | |
| 404 | There is no such workspace, deleted ones included | |
| 503 | The catalog could not be read, so no consequence could be computed |
{
"confirmation": "string",
"workspace": "string",
"action": "archive",
"sessions": [
{
"id": "string",
"display_name": "string",
"working_directory": "string"
}
],
"expires_at": 0
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/lifecycle/confirmation" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "archive"
}'POST/v1/workspaces/{id}/lifecycle
What the operation did: the sessions it closed, whether they all closed, and whether the catalog mutation committed. A refusal carrying workspace_lifecycle_incomplete with sessions_closed: true and mutation_applied: false is a partial failure - the terminals are gone and the catalog is unchanged - and the same request again is the retry, which needs a fresh confirmation because the first one was spent. Asking for a state the workspace is already in succeeds and closes nothing.
- Authorization
- Bearer token with
workspace.destroy - Operation id
- workspace_lifecycle
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string (uuid) |
Request body (required)
application/json WorkspaceLifecycleRequest
| Field | Type | Description |
|---|---|---|
| actionrequired | WorkspaceLifecycleAction | What Three verbs, and no permanent delete. There is no purge route anywhere in Koru - the retention sweep is the only thing that removes bytes - so naming one here would be describing an operation that does not exist. |
| confirmationrequired | string | |
| replacement_name | string | Localized default for a required replacement. Older callers use the protocol's English fallback; this is not a second creation operation. Default |
| removal_attempt | RemovalAttemptId or null |
{
"action": "archive",
"confirmation": "string",
"replacement_name": "My Workspace",
"removal_attempt": "00000000-0000-4000-8000-000000000000"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | WorkspaceLifecycleOutcome |
| 400 | The body is not a lifecycle request, or the confirmation is not one this gateway issued for this workspace and this action - expired, spent, or never minted here | |
| 401 | The gateway bearer is absent or invalid | |
| 403 | The session does not hold workspace.destroy | |
| 404 | There is no such workspace, deleted ones included - or the mutation refused because another window removed it first, which is a partial failure carrying its outcome | |
| 409 | The mutation refused: the body carries the outcome, so a partial failure is told apart from nothing having happened | |
| 500 | A partial failure: the body says which steps completed and whether a retry is safe | |
| 503 | The session-host bridge is unavailable, so no terminal could be closed |
{
"workspace": "string",
"action": "archive",
"terminated_sessions": [
"string"
],
"sessions_closed": false,
"mutation_applied": false,
"authority_committed": false,
"listing": {
"state": "applied"
},
"replacement": {
"state": "created",
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/lifecycle" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "archive",
"confirmation": "string",
"replacement_name": "My Workspace",
"removal_attempt": "00000000-0000-4000-8000-000000000000"
}'Compute what moving a workspace to a profile would close, and issue the token that proves it was shown
POST/v1/workspaces/{id}/migration/confirmation
Both ends of the move - the profile that owns the workspace and the one it would go to - the terminals the move would close, named, and a single-use token bound to this workspace and this destination. A token issued for one destination does not spend against another: a person accepted a move to a particular profile, not a move. The same scope as the operation itself.
- Authorization
- Bearer token with
workspace.migrate - Operation id
- workspace_migration_confirmation
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string (uuid) |
Request body (required)
application/json WorkspaceMigrationConfirmationRequest
| Field | Type | Description |
|---|---|---|
| torequired | string | The profile the workspace would move to. |
{
"to": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | WorkspaceMigration |
| 400 | The body is not a migration confirmation request, or the destination is not a profile id | |
| 401 | The gateway bearer is absent or invalid | |
| 403 | The session does not hold workspace.migrate | |
| 404 | There is no such workspace, or no such destination profile - a consequence is never computed for a move that could not happen | |
| 503 | The catalog could not be read, so no consequence could be computed |
{
"confirmation": "string",
"workspace": "string",
"from": "string",
"to": "string",
"sessions": [
{
"id": "string",
"display_name": "string",
"working_directory": "string"
}
],
"expires_at": 0
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/migration/confirmation" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "string"
}'POST/v1/workspaces/{id}/migration
What the operation did: both ends, the sessions it closed, and whether the reassignment committed. A refusal carrying workspace_migration_incomplete with sessions_closed: true and mutation_applied: false is a partial failure - the terminals are gone and the workspace has not moved - and the same request again is the retry, which needs a fresh confirmation because the first one was spent. Moving a workspace to the profile that already owns it succeeds and closes nothing.
- Authorization
- Bearer token with
workspace.migrate - Operation id
- workspace_migration
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| idrequired | path | string (uuid) |
Request body (required)
application/json WorkspaceMigrationRequest
| Field | Type | Description |
|---|---|---|
| torequired | string | |
| confirmationrequired | string | |
| replacement_name | string | Default |
{
"to": "string",
"confirmation": "string",
"replacement_name": "My Workspace"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | WorkspaceMigrationOutcome |
| 400 | The body is not a migration request, or the confirmation is not one this gateway issued for this workspace and this destination - expired, spent, or never minted here | |
| 401 | The gateway bearer is absent or invalid | |
| 403 | The session does not hold workspace.migrate | |
| 404 | There is no such workspace, or the reassignment refused because the destination profile is gone - which after the terminals closed is a partial failure carrying its outcome | |
| 409 | The reassignment refused: the body carries the outcome, so a partial failure is told apart from nothing having happened | |
| 500 | A partial failure: the terminals are closed, the workspace has not moved, and the body says so | |
| 503 | The session-host bridge is unavailable, so no terminal could be closed |
{
"workspace": "string",
"from": "string",
"to": "string",
"terminated_sessions": [
"string"
],
"sessions_closed": false,
"mutation_applied": false,
"replacement": {
"state": "created",
"workspace": {
"id": "00000000-0000-4000-8000-000000000000",
"owner": {},
"name": "string",
"created_at": 0,
"last_opened_at": 0,
"icon": "string",
"color": "string",
"pinned": false,
"archived": false,
"deleted_at": 0,
"position": 0
}
},
"projects_rescoped": [
"string"
]
}curl -X POST "http://127.0.0.1:$PORT/v1/workspaces/$ID/migration" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "string",
"confirmation": "string",
"replacement_name": "My Workspace"
}'Generated from contract version 1. Last updated .
