API request and response examples
This page gives concrete examples for the public HTTP routes most integrations start with.
Use these examples to understand payload shape and workflow order. Use the generated public OpenAPI contract for exact field-level validation and any future contract changes.
Before you start
Examples below assume:
- your base URL is
https://api.aetherfs.io - you have an auth mechanism appropriate for your deployment
SESSION_ID,SOURCE_ID, andAPPROVAL_IDare real values from earlier calls
Important JSON encoding note
Several public endpoints use fields declared as format: byte in OpenAPI.
In JSON, those values are represented as base64-encoded strings.
That affects routes such as:
- session cache set/get
- bus publish payloads
1. Create a session
Typical route:
POST /v1/sessions
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"sourceRef": {
"sourceId": "src_webapp",
"revisionId": "rev_2026_03_10"
},
"initialTags": {
"task": "bugfix-123",
"owner": "alice"
}
}'
Example response:
{
"id": "sess_1234abcd",
"baseFingerprint": "4e52c7c1f6d387f2b5c6d09d8124b4f877ef4d97",
"status": "SESSION_STATUS_ACTIVE",
"tags": {
"task": "bugfix-123",
"owner": "alice"
},
"createdAt": "2026-03-11T05:55:21Z",
"lastActivityAt": "2026-03-11T05:55:21Z",
"capabilities": {
"commit": true,
"approvals": true,
"annotations": true
}
}
When to use it:
- provisioning a new workspace
- creating one session per task
- starting a review or automation flow from a managed source
2. Get session detail
Typical route:
GET /v1/sessions/{sessionId}
Example request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd' \
-H 'Authorization: Bearer <token>'
Example response:
{
"id": "sess_1234abcd",
"baseFingerprint": "4e52c7c1f6d387f2b5c6d09d8124b4f877ef4d97",
"status": "SESSION_STATUS_ACTIVE",
"tags": {
"task": "bugfix-123",
"owner": "alice"
},
"createdAt": "2026-03-11T05:55:21Z",
"lastActivityAt": "2026-03-11T06:02:11Z",
"capabilities": {
"commit": true,
"approvals": true,
"annotations": true
}
}
Use this before:
- destructive actions
- review-state transitions
- commit or restore
- rendering a session detail page
3. Read a manifest
Typical route:
GET /v1/sessions/{sessionId}/manifest
Example request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/manifest?pathPrefix=/src&pageSize=2&includeCasExtents=true' \
-H 'Authorization: Bearer <token>'
Example response:
{
"nodes": [
{
"path": "/src",
"fileType": "VFS_FILE_TYPE_DIRECTORY",
"sizeBytes": "0",
"contentHash": "",
"mode": 16877
},
{
"path": "/src/main.ts",
"fileType": "VFS_FILE_TYPE_FILE",
"sizeBytes": "1284",
"contentHash": "sha256:0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d",
"mode": 33188,
"casExtents": []
}
],
"nextPageToken": "eyJvZmZzZXQiOjJ9",
"manifestHash": "mf_01HRJ4D2S3SK3CFBB9K4CMQ0GE"
}
Use it for:
- file browsers
- review UIs
- change previews
- subtree loading and pagination
4. Fork a session
Typical route:
POST /v1/sessions/{sourceSessionId}/fork
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/fork' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"sourceCheckpointLabel": "before-review",
"newTags": {
"review-track": "proposal-b",
"owner": "reviewer-2"
}
}'
Example response:
{
"id": "sess_7890fork",
"baseFingerprint": "4e52c7c1f6d387f2b5c6d09d8124b4f877ef4d97",
"status": "SESSION_STATUS_ACTIVE",
"tags": {
"task": "bugfix-123",
"owner": "reviewer-2",
"review-track": "proposal-b"
},
"createdAt": "2026-03-11T06:10:08Z",
"lastActivityAt": "2026-03-11T06:10:08Z",
"capabilities": {
"commit": true,
"approvals": true
}
}
Use it when:
- you need parallel proposals
- you want a clean review branch
- several actors may diverge from the same baseline
5. Create a checkpoint
Typical route:
POST /v1/sessions/{sessionId}/checkpoints
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/checkpoints' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"label": "before-large-refactor"
}'
Example response:
{
"id": "cp_5678efgh",
"sessionId": "sess_1234abcd",
"label": "before-large-refactor",
"createdAt": "2026-03-11T06:14:22Z"
}
Use it before:
- mass edits
- import/sync operations
- risky agent-driven transformations
- review handoffs
6. Add an annotation
Typical route:
POST /v1/sessions/{sessionId}/annotations
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/annotations' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"filePath": "/src/main.ts",
"lineNumber": 42,
"expectedContentHash": "sha256:0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d",
"comment": "This branch adds the null-check, but the error path still needs coverage."
}'
Example response:
{
"annotation": {
"id": "ann_01HRJ5Q8D3RMY8H7RWX7Y1AVRJ",
"sessionId": "sess_1234abcd",
"filePath": "/src/main.ts",
"lineNumber": 42,
"contentHash": "sha256:0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d",
"comment": "This branch adds the null-check, but the error path still needs coverage.",
"reviewerId": "alice",
"status": "ANNOTATION_STATUS_OPEN",
"createdAt": "2026-03-11T06:18:04Z"
}
}
The important field here is expectedContentHash. It prevents a comment from landing on stale content.
List annotations for a file or session
Route:
GET /v1/sessions/{sessionId}/annotations
Request for one file:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/annotations?filePath=/src/main.ts' \
-H 'Authorization: Bearer <token>'
Response:
{
"annotations": [
{
"id": "ann_01HRJ5Q8D3RMY8H7RWX7Y1AVRJ",
"sessionId": "sess_1234abcd",
"filePath": "/src/main.ts",
"lineNumber": 42,
"contentHash": "sha256:0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d",
"comment": "This branch adds the null-check, but the error path still needs coverage.",
"reviewerId": "alice",
"status": "ANNOTATION_STATUS_OPEN",
"createdAt": "2026-03-11T06:18:04Z"
}
]
}
If filePath is omitted, the route returns all annotations visible for the session.
Resolve an annotation
Route:
PATCH /v1/sessions/{sessionId}/annotations/{annotationId}
Request:
curl -X PATCH 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/annotations/ann_01HRJ5Q8D3RMY8H7RWX7Y1AVRJ' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"newStatus": "ANNOTATION_STATUS_RESOLVED"
}'
Response:
{
"annotation": {
"id": "ann_01HRJ5Q8D3RMY8H7RWX7Y1AVRJ",
"sessionId": "sess_1234abcd",
"filePath": "/src/main.ts",
"lineNumber": 42,
"contentHash": "sha256:0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d",
"comment": "This branch adds the null-check, but the error path still needs coverage.",
"reviewerId": "alice",
"status": "ANNOTATION_STATUS_RESOLVED",
"createdAt": "2026-03-11T06:18:04Z"
}
}
7. Request approval
Typical route:
POST /v1/sessions/{sessionId}/request-approval
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/request-approval' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"reason": "COMMIT_TO_MAIN",
"details": {
"summary": "Applies the login validation fix and updates related tests.",
"changedFiles": [
"/src/main.ts",
"/tests/main.test.ts"
]
}
}'
Example response:
{
"approvalId": "appr_01HRJ62B2S2H12XATB9TQX6W7R"
}
At this point, session state may move to SESSION_STATUS_PENDING_APPROVAL.
8. Grant or deny approval
Typical routes:
POST /v1/sessions/{sessionId}/approvals/{approvalId}/grantPOST /v1/sessions/{sessionId}/approvals/{approvalId}/deny
Grant example:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/approvals/appr_01HRJ62B2S2H12XATB9TQX6W7R/grant' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{}'
Example response:
{}
Deny example:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/approvals/appr_01HRJ62B2S2H12XATB9TQX6W7R/deny' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"feedback": "Split the large change into two commits before retrying."
}'
Example response:
{}
9. Commit a session
Typical route:
POST /v1/sessions/{sessionId}/commit
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/commit' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"message": "Fix login validation and add regression tests",
"author": {
"name": "Alice Example",
"email": "alice@example.com"
},
"gitBranch": {
"branchName": "fix/login-validation"
}
}'
Example response:
{
"commitId": "a6c3a92c805b11d9e4e8f0c82be2b43277a1e3d5"
}
The response is intentionally small. Treat the returned commitId as the durable reference to the result.
10. Set and get a session cache entry
Typical routes:
PUT /v1/sessions/{sessionId}/cache/{key}GET /v1/sessions/{sessionId}/cache/{key}
Example set request:
curl -X PUT 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/cache/analysis.summary' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"value": "eyJzdGF0dXMiOiJyZWFkeS1mb3ItcmV2aWV3IiwicmlzayI6ImxvdyJ9",
"ttlSeconds": 3600
}'
The value above is base64-encoded JSON.
Decoded, it contains:
{"status":"ready-for-review","risk":"low"}
Example set response:
{}
Example get request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/cache/analysis.summary' \
-H 'Authorization: Bearer <token>'
Example get response:
{
"value": "eyJzdGF0dXMiOiJyZWFkeS1mb3ItcmV2aWV3IiwicmlzayI6ImxvdyJ9",
"found": true
}
Use the cache for:
- derived tool output
- workflow context
- machine-consumable summaries
- small structured session state
11. Publish a bus message
Typical route:
POST /v1/sessions/{sessionId}/bus:publish
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/bus:publish' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"topic": "review:status",
"payload": "eyJzdGF0dXMiOiJyZWFkeS1mb3ItYXBwcm92YWwifQ=="
}'
Decoded, the payload is:
{"status":"ready-for-approval"}
Example response:
{}
Use the bus for:
- live progress
- event-style workflow transitions
- tool-to-tool coordination
Consume a real-time bus subscription
Route:
GET /v1/sessions/{sessionId}/bus
This is a long-lived streaming response. Use a streaming-capable HTTP client. For curl, use -N so output is not buffered.
Example request:
curl -N 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/bus?topics=review:status&topics=tests:complete&scope=SESSION_ONLY&replayLimit=2' \
-H 'Authorization: Bearer <token>'
Representative streamed response items:
{"result":{"messageId":"msg_01HRJ9B2P4QDXY6RKEHHX9QWF4","sessionId":"sess_1234abcd","topic":"review:status","payload":"eyJzdGF0dXMiOiJyZWFkeS1mb3ItYXBwcm92YWwifQ==","senderId":"worker-7","publishedAt":"2026-03-11T08:44:10Z"}}
{"result":{"messageId":"msg_01HRJ9B5TY3CM95D2QZVFQ3QJB","sessionId":"sess_1234abcd","topic":"tests:complete","payload":"eyJwYXNzZWQiOjEyOCwiZmFpbGVkIjowfQ==","senderId":"worker-7","publishedAt":"2026-03-11T08:44:31Z"}}
Decoded example payloads:
eyJzdGF0dXMiOiJyZWFkeS1mb3ItYXBwcm92YWwifQ==->{"status":"ready-for-approval"}eyJwYXNzZWQiOjEyOCwiZmFpbGVkIjowfQ==->{"passed":128,"failed":0}
The public OpenAPI describes each streamed item as a wrapper with either:
result, containing av1Messageerror, containing agoogle.rpc.Status
Client guidance:
- keep the connection open
- treat
replayLimitandreplayAfterMessageIdas catch-up controls - design your client to handle a stream, not a single JSON document
12. Create a directory
Typical route:
POST /v1/sessions/{sessionId}/dirs
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/dirs' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/features",
"mode": 493
}'
493 is decimal for POSIX mode 0755.
Example response:
{}
Use this before:
- writing files into a new subtree
- preparing a refactor destination
- creating workflow-specific directories explicitly
13. Remove an empty directory
Typical route:
DELETE /v1/sessions/{sessionId}/dirs
Example request:
curl -X DELETE 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/dirs?path=/src/old-feature' \
-H 'Authorization: Bearer <token>'
Example response:
{}
This route is for empty directories. If the directory still has content, your client should remove or relocate that content first.
14. Rename or move a path
Typical route:
POST /v1/sessions/{sessionId}:rename
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:rename' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"sourcePath": "/src/legacy/config.ts",
"destinationPath": "/src/config/index.ts"
}'
Example response:
{}
Use this for:
- path cleanup
- file moves during refactors
- directory restructuring without inventing custom path routes
15. Delete a file with optimistic concurrency
Typical route:
DELETE /v1/sessions/{sessionId}/files
Example request:
curl -X DELETE 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/files?path=/src/obsolete.ts&expectedHash=sha256%3A0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d' \
-H 'Authorization: Bearer <token>'
Example response:
{}
The expectedHash query parameter is the important guard here. Use it when a stale client should not be allowed to delete newer content silently.
16. Execute a batch of filesystem operations
Typical route:
POST /v1/sessions/{sessionId}/batch
The public batch schema currently supports three operation kinds:
writedeletesegmentWrite
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/batch' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"operations": [
{
"write": {
"path": "/src/generated/version.ts",
"content": "ZXhwb3J0IGNvbnN0IFZFUlNJT04gPSAiMS4yLjAiOwo=",
"expectedHash": ""
}
},
{
"delete": {
"path": "/src/obsolete.ts",
"expectedHash": "sha256:0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d"
}
},
{
"segmentWrite": {
"path": "/src/changelog.txt",
"segments": [
{
"data": {
"offset": "0",
"content": "Q2hhbmdlZCBsb2dpbiB2YWxpZGF0aW9uLgo="
}
}
],
"logicalSize": "25",
"expectedHash": "",
"mode": 33188
}
}
]
}'
Decoded examples:
ZXhwb3J0IGNvbnN0IFZFUlNJT04gPSAiMS4yLjAiOwo=->export const VERSION = "1.2.0";Q2hhbmdlZCBsb2dpbiB2YWxpZGF0aW9uLgo=->Changed login validation.
Example response:
{}
Use batch when one user-visible action should become one atomic server-side action.
17. Apply sparse or incremental mutations with patchSegments
Typical route:
POST /v1/sessions/{sessionId}/patchSegments
This route is for partial file updates where you do not want to replace the whole file payload.
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/patchSegments' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"mutations": [
{
"path": "/src/feature.flag",
"segments": [
{
"data": {
"offset": "0",
"content": "ZW5hYmxlZD10cnVlCg=="
}
}
],
"logicalSize": "13",
"expectedHash": "sha256:2d4ed1af6f6fb9fb0f3c9ef5ec7849f9fe8f88c145d70ec4a34490e5f17f9f78",
"mode": 33188
}
],
"conflictPolicy": "SEGMENT_CONFLICT_POLICY_ABORT_ON_CONFLICT"
}'
Decoded content:
ZW5hYmxlZD10cnVlCg==->enabled=true
Example response:
{
"skippedPaths": []
}
Notes:
expectedHashmakes the mutation conditional.conflictPolicycontrols whether the whole request aborts or conflicting paths are skipped.- The schema also supports
holeandborrowedsegment chunks for more advanced sparse workflows.
18. Create, list, and read symlinks
Create route:
POST /v1/sessions/{sessionId}/symlinks
Create request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/symlinks' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"linkPath": "/app/current-config",
"target": "/app/configs/release-2026-03-11"
}'
Create response:
{}
List route:
GET /v1/sessions/{sessionId}/symlinks
List request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/symlinks?pathPrefix=/app&pageSize=50' \
-H 'Authorization: Bearer <token>'
List response:
{
"symlinks": [
{
"path": "/app/current-config",
"target": "/app/configs/release-2026-03-11"
}
],
"nextPageToken": ""
}
Read route:
GET /v1/sessions/{sessionId}/readSymlink
Read request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/readSymlink?path=/app/current-config' \
-H 'Authorization: Bearer <token>'
Read response:
{
"target": "/app/configs/release-2026-03-11"
}
Use these routes when your product needs explicit symlink awareness rather than treating symlinks as opaque path metadata.
19. Create a hard link or unlink a path
Hard link route:
POST /v1/sessions/{sessionId}:link
Hard link request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:link' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"srcPath": "/src/main.ts",
"dstPath": "/src/main-copy.ts"
}'
Hard link response:
{}
Unlink route:
POST /v1/sessions/{sessionId}:unlink
Unlink request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:unlink' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main-copy.ts",
"expectedHash": "sha256:0c9c7ca53c8f7d8b1efb62fdb5879f7ff6b6e2d9f0b3f0173d9d68c8a1d62d7d"
}'
Unlink response:
{}
20. Get CAS extents for a file
Typical route:
POST /v1/sessions/{sessionId}:getCasExtents
Use this route when a client wants to understand which parts of a file already exist as CAS-backed extents, so later sparse or borrowed-segment workflows can avoid re-uploading bytes.
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:getCasExtents' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts"
}'
Example response:
{
"extents": [
{
"offset": "0",
"length": "512",
"hash": "sha256:5b0f4f5e4f07a65f75f2e89a7216a4f6c2433e6c8f86f6c6c7f97ccf4b64560f",
"casOffset": "0"
},
{
"offset": "512",
"length": "772",
"hash": "sha256:8ce4f2d3a39d1e0b688af9a42f27de6ec2cc1fb8bf4cdd17193cde918f112390",
"casOffset": "0"
}
]
}
Use it for:
- advanced sync clients
- sparse patch planning
- borrowed segment reuse
21. Set, list, get, and remove xattrs
These routes manage extended attributes attached to a path.
Set an xattr
Route:
POST /v1/sessions/{sessionId}:setXattr
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:setXattr' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"name": "user.review.status",
"value": "bmVlZHMtYXR0ZW50aW9u"
}'
Decoded value:
bmVlZHMtYXR0ZW50aW9u->needs-attention
Response:
{}
List xattrs
Route:
POST /v1/sessions/{sessionId}:listXattrs
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:listXattrs' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts"
}'
Response:
{
"names": [
"user.review.status",
"user.owner"
]
}
Get one xattr
Route:
POST /v1/sessions/{sessionId}:getXattr
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:getXattr' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"name": "user.review.status"
}'
Response:
{
"value": "bmVlZHMtYXR0ZW50aW9u"
}
Remove an xattr
Route:
POST /v1/sessions/{sessionId}:removeXattr
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:removeXattr' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"name": "user.review.status"
}'
Response:
{}
Use xattrs when you need path-local metadata that is more file-system-like than annotations, but should still stay out of normal file content.
22. Inspect and set POSIX-style locks
These routes expose lower-level POSIX locking semantics.
Inspect a possible conflicting lock
Route:
POST /v1/sessions/{sessionId}:getPosixLock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:getPosixLock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"kind": "POSIX_LOCK_KIND_EXCLUSIVE",
"range": {
"start": "0",
"length": "0"
},
"owner": {
"ownerId": "1001",
"pid": 4242,
"clientId": "7"
},
"isFlock": false
}'
Example response with a conflict:
{
"hasConflict": true,
"conflict": {
"kind": "POSIX_LOCK_KIND_EXCLUSIVE",
"range": {
"start": "0",
"length": "0"
},
"owner": {
"ownerId": "1002",
"pid": 4310,
"clientId": "8"
},
"isFlock": false
}
}
Set a non-blocking POSIX lock
Route:
POST /v1/sessions/{sessionId}:setPosixLock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:setPosixLock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"kind": "POSIX_LOCK_KIND_EXCLUSIVE",
"range": {
"start": "0",
"length": "0"
},
"owner": {
"ownerId": "1001",
"pid": 4242,
"clientId": "7"
}
}'
Response:
{}
Set a blocking POSIX lock
Route:
POST /v1/sessions/{sessionId}:setPosixLockBlocking
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:setPosixLockBlocking' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"kind": "POSIX_LOCK_KIND_SHARED",
"range": {
"start": "0",
"length": "0"
},
"owner": {
"ownerId": "1001",
"pid": 4242,
"clientId": "7"
}
}'
Response:
{}
Whole-file flock
Route:
POST /v1/sessions/{sessionId}:flock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:flock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"mode": "POSIX_FLOCK_MODE_EXCLUSIVE",
"owner": {
"ownerId": "1001",
"pid": 4242,
"clientId": "7"
},
"blocking": false
}'
Response:
{}
Unlock
Route:
POST /v1/sessions/{sessionId}:funlock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd:funlock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"owner": {
"ownerId": "1001",
"pid": 4242,
"clientId": "7"
},
"range": {
"start": "0",
"length": "0"
},
"isFlock": false
}'
Response:
{}
Use these routes when your client needs low-level POSIX-compatible lock behavior. For higher-level collaboration between product users, prefer the file lock routes below.
23. Use collaboration file locks
These routes are higher-level than POSIX locks and are usually the better fit for product workflows involving editors, review tools, and multiple human or agent actors.
Acquire a file lock
Route:
POST /v1/sessions/{sessionId}/files/lock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/files/lock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"timeoutSeconds": 300
}'
Response:
{
"lockId": "lock_01HRJ8KXQMA75VPYF1E56DHQ0N"
}
Renew a file lock
Route:
POST /v1/sessions/{sessionId}/files/renew-lock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/files/renew-lock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"lockId": "lock_01HRJ8KXQMA75VPYF1E56DHQ0N",
"timeoutSeconds": 300
}'
Response:
{
"lockId": "lock_01HRJ8KXQMA75VPYF1E56DHQ0N",
"remainingTtlSeconds": "300"
}
List active locks
Route:
POST /v1/sessions/{sessionId}/files/list-locks
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/files/list-locks' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"pathPrefix": "/src"
}'
Response:
{
"locks": [
{
"path": "/src/main.ts",
"lockId": "lock_01HRJ8KXQMA75VPYF1E56DHQ0N",
"ownerId": "alice",
"remainingTtlSeconds": "287"
}
]
}
Unlock
Route:
POST /v1/sessions/{sessionId}/files/unlock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/files/unlock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"lockId": "lock_01HRJ8KXQMA75VPYF1E56DHQ0N"
}'
Response:
{}
Force unlock
Route:
POST /v1/sessions/{sessionId}/files/force-unlock
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/files/force-unlock' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"path": "/src/main.ts",
"reason": "client crashed during review handoff"
}'
Response:
{
"lockId": "lock_01HRJ8KXQMA75VPYF1E56DHQ0N",
"ownerId": "alice"
}
24. Get a session health snapshot
Typical route:
GET /v1/sessions/{sessionId}/health
Example request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/health' \
-H 'Authorization: Bearer <token>'
Example response:
{
"health": {
"status": "STATUS_PASSING",
"testsPassed": 128,
"testsFailed": 0,
"testsSkipped": 3,
"lastUpdatedAt": "2026-03-11T08:12:55Z"
}
}
Use this route for:
- session status badges
- support dashboards
- “last known health” views in a review UI
25. Report test results and update health
Primary route:
POST /v1/sessions/{sessionId}/health/report
The contract also exposes the alias:
POST /v1/sessions/{sessionId}/health:testResults
Example request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/health/report' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"reportFormat": "junit-xml/v1",
"reportContent": "PHRlc3RzdWl0ZSB0ZXN0cz0iMiIgZmFpbHVyZXM9IjAiLz4="
}'
Decoded report content:
PHRlc3RzdWl0ZSB0ZXN0cz0iMiIgZmFpbHVyZXM9IjAiLz4=-><testsuite tests="2" failures="0"/>
Example response:
{
"newHealthStatus": {
"status": "STATUS_PASSING",
"testsPassed": 2,
"testsFailed": 0,
"testsSkipped": 0,
"lastUpdatedAt": "2026-03-11T08:12:55Z"
}
}
Use this route when an agent or runner needs to report health back into the shared session state.
26. Get session analytics
Typical route:
GET /v1/sessions/{sessionId}/analytics
Example request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/analytics' \
-H 'Authorization: Bearer <token>'
Example response:
{
"sessionId": "sess_1234abcd",
"timeToFirstWriteMs": "4820",
"totalFileMutations": "37",
"checkpointCount": "3",
"restoreCount": "1",
"lastReportedHealth": {
"status": "STATUS_PASSING",
"testsPassed": 128,
"testsFailed": 0,
"testsSkipped": 3,
"lastUpdatedAt": "2026-03-11T08:12:55Z"
},
"completionTime": "2026-03-11T08:15:31Z"
}
Use this route for:
- workspace analytics views
- efficiency reporting
- post-run summaries
27. Get tenant usage
Typical route:
GET /v1/usage
Example request:
curl 'https://api.aetherfs.io/v1/usage?windowStart=2026-03-01T00:00:00Z&windowEnd=2026-03-31T23:59:59Z&granularity=day' \
-H 'Authorization: Bearer <token>'
Example response:
{
"tenantId": "tenant_acme",
"windowStart": "2026-03-01T00:00:00Z",
"windowEnd": "2026-03-31T23:59:59Z",
"granularity": "day",
"aggregationVersion": 3,
"sessionHours": 184.5,
"storageGbMonth": 92.75,
"egressGb": 18.2
}
Use this route for account visibility, billing-style reporting, and tenant-level dashboards.
28. Create, list, and inspect sources
Sources are reusable baselines for new sessions.
Create a source
Route:
POST /v1/sources
Request:
curl -X POST 'https://api.aetherfs.io/v1/sources' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"displayName": "webapp-main",
"gitRemote": {
"remoteUrl": "https://github.com/acme/webapp.git",
"defaultRef": "main",
"credentialsSecretId": "sec_git_pat_prod"
},
"labels": {
"team": "web",
"env": "prod"
}
}'
Response:
{
"sourceId": "src_webapp",
"displayName": "webapp-main",
"kind": "UNDERLAY_SOURCE_KIND_GIT_REMOTE",
"status": "UNDERLAY_SOURCE_STATUS_READY",
"latestRevisionId": "rev_2026_03_11_main",
"createdAt": "2026-03-11T08:20:10Z",
"updatedAt": "2026-03-11T08:20:10Z",
"createdByPrincipal": "alice@example.com",
"labels": {
"team": "web",
"env": "prod"
}
}
List sources
Route:
GET /v1/sources
Request:
curl 'https://api.aetherfs.io/v1/sources?filter=team%3Dweb&pageSize=20' \
-H 'Authorization: Bearer <token>'
Response:
{
"sources": [
{
"sourceId": "src_webapp",
"displayName": "webapp-main",
"kind": "UNDERLAY_SOURCE_KIND_GIT_REMOTE",
"status": "UNDERLAY_SOURCE_STATUS_READY",
"latestRevisionId": "rev_2026_03_11_main",
"createdAt": "2026-03-11T08:20:10Z",
"updatedAt": "2026-03-11T08:20:10Z",
"createdByPrincipal": "alice@example.com",
"labels": {
"team": "web",
"env": "prod"
}
}
],
"nextPageToken": ""
}
Get one source
Route:
GET /v1/sources/{sourceId}
Request:
curl 'https://api.aetherfs.io/v1/sources/src_webapp' \
-H 'Authorization: Bearer <token>'
Response:
{
"sourceId": "src_webapp",
"displayName": "webapp-main",
"kind": "UNDERLAY_SOURCE_KIND_GIT_REMOTE",
"status": "UNDERLAY_SOURCE_STATUS_READY",
"latestRevisionId": "rev_2026_03_11_main",
"createdAt": "2026-03-11T08:20:10Z",
"updatedAt": "2026-03-11T08:20:10Z",
"createdByPrincipal": "alice@example.com",
"labels": {
"team": "web",
"env": "prod"
}
}
29. Start import, poll job status, refresh a source, and promote a session
These routes all return the import-job shape, so clients should treat them as asynchronous flows.
Start a source import
Route:
POST /v1/sources/{sourceId}/startImport
Request:
curl -X POST 'https://api.aetherfs.io/v1/sources/src_webapp/startImport' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"git": {
"gitRef": "refs/heads/main"
}
}'
Response:
{
"jobId": "job_01HRJD8JAZM4H1JY70T6QW5C0V",
"sourceId": "src_webapp",
"state": "UNDERLAY_IMPORT_JOB_STATE_QUEUED",
"revisionId": "",
"createdAt": "2026-03-11T08:30:04Z",
"startedAt": "",
"completedAt": "",
"errorCode": "",
"errorMessage": "",
"bytesProcessed": "0",
"filesProcessed": "0"
}
Poll the import job
Route:
GET /v1/sources/{sourceId}/jobs/{jobId}
Request:
curl 'https://api.aetherfs.io/v1/sources/src_webapp/jobs/job_01HRJD8JAZM4H1JY70T6QW5C0V' \
-H 'Authorization: Bearer <token>'
Response:
{
"jobId": "job_01HRJD8JAZM4H1JY70T6QW5C0V",
"sourceId": "src_webapp",
"state": "UNDERLAY_IMPORT_JOB_STATE_SUCCEEDED",
"revisionId": "rev_2026_03_11_main",
"createdAt": "2026-03-11T08:30:04Z",
"startedAt": "2026-03-11T08:30:07Z",
"completedAt": "2026-03-11T08:31:19Z",
"errorCode": "",
"errorMessage": "",
"bytesProcessed": "14829203",
"filesProcessed": "4211"
}
Refresh a source
Route:
POST /v1/sources/{sourceId}/refresh
Request:
curl -X POST 'https://api.aetherfs.io/v1/sources/src_webapp/refresh' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"gitRef": "refs/heads/release/2026.03"
}'
Response:
{
"jobId": "job_01HRJDCJ2GG7CH80X3Q5RP83V1",
"sourceId": "src_webapp",
"state": "UNDERLAY_IMPORT_JOB_STATE_QUEUED",
"revisionId": "",
"createdAt": "2026-03-11T08:34:41Z",
"startedAt": "",
"completedAt": "",
"errorCode": "",
"errorMessage": "",
"bytesProcessed": "0",
"filesProcessed": "0"
}
Promote a session into a source
Route:
POST /v1/sources/{sourceId}/promoteSession
Request:
curl -X POST 'https://api.aetherfs.io/v1/sources/src_webapp/promoteSession' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"sessionId": "sess_1234abcd",
"revisionLabel": "release-candidate-7"
}'
Response:
{
"jobId": "job_01HRJDHQK3QSWCZJQ12XCEEFK8",
"sourceId": "src_webapp",
"state": "UNDERLAY_IMPORT_JOB_STATE_QUEUED",
"revisionId": "",
"createdAt": "2026-03-11T08:39:17Z",
"startedAt": "",
"completedAt": "",
"errorCode": "",
"errorMessage": "",
"bytesProcessed": "0",
"filesProcessed": "0"
}
30. Read the session event log
Typical route:
GET /v1/sessions/{sessionId}/events
Use this route when you want an immutable, paginated audit trail of session changes rather than a live message stream.
Example request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/events?startTime=2026-03-11T08:00:00Z&endTime=2026-03-11T09:00:00Z&pageSize=2' \
-H 'Authorization: Bearer <token>'
Example response:
{
"events": [
{
"eventId": "evt_01HRJA1KR9T0S8A4P1B5VVN5ER",
"timestamp": "2026-03-11T08:12:11Z",
"eventType": "v1.FileSystemService.Rename",
"details": {
"sourcePath": "/src/legacy/config.ts",
"destinationPath": "/src/config/index.ts"
}
},
{
"eventId": "evt_01HRJA235Q9B0W7R5E2KJ4N55V",
"timestamp": "2026-03-11T08:18:04Z",
"eventType": "v1.AnnotationService.AddAnnotation",
"details": {
"filePath": "/src/main.ts",
"annotationId": "ann_01HRJ5Q8D3RMY8H7RWX7Y1AVRJ"
}
}
],
"nextPageToken": "eyJvZmZzZXQiOjJ9"
}
Use the event log for:
- audit trails
- support tooling
- reconstructing what changed over time
- paginated historical views
Use the bus for live activity. Use the event log for durable history.
31. List, set, get, and delete secrets
The secrets surface is session-scoped for authorization and visibility, but the values themselves are not ordinary filesystem content.
Important boundary from the public contract:
- retrieved secret values should never be written into the session overlay as ordinary files
- secret values should be redacted from logs
List visible secrets
Route:
GET /v1/sessions/{sessionId}/secrets
Request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/secrets?scope=SECRET_SCOPE_SESSION' \
-H 'Authorization: Bearer <token>'
Response:
{
"secrets": [
{
"secretName": "NPM_TOKEN",
"scope": "SECRET_SCOPE_SESSION"
},
{
"secretName": "OPENAI_API_KEY",
"scope": "SECRET_SCOPE_SESSION"
}
]
}
Set a secret
Route:
PUT /v1/sessions/{sessionId}/secrets/{secretName}
Request:
curl -X PUT 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/secrets/NPM_TOKEN' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"secretValue": "npm_xxxxxxxxxxxxxxxxx",
"scope": "SECRET_SCOPE_SESSION"
}'
Response:
{
"secretName": "NPM_TOKEN",
"scope": "SECRET_SCOPE_SESSION"
}
Get a secret
Route:
GET /v1/sessions/{sessionId}/secrets/{secretName}
Request:
curl 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/secrets/NPM_TOKEN' \
-H 'Authorization: Bearer <token>'
Response:
{
"secretName": "NPM_TOKEN",
"secretValue": "npm_xxxxxxxxxxxxxxxxx"
}
Delete a secret
Route:
DELETE /v1/sessions/{sessionId}/secrets/{secretName}
Request:
curl -X DELETE 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/secrets/NPM_TOKEN?scope=SECRET_SCOPE_SESSION' \
-H 'Authorization: Bearer <token>'
Response:
{
"secretName": "NPM_TOKEN",
"scope": "SECRET_SCOPE_SESSION"
}
Use secrets for:
- credentials
- API tokens
- connection strings
- deployment-sensitive inputs that should not live in the working tree
Do not use secrets for:
- ordinary config that can safely live in source
- review notes
- cache entries
- workflow messages
32. Approval edge cases
The success paths for approval request, grant, and deny are already covered earlier. The cases below are the ones client authors usually miss.
32.1 Granting an approval that has already been resolved
If a reviewer tries to grant an approval that has already been denied or granted, the route still returns the public generic error envelope on failure.
Route:
POST /v1/sessions/{sessionId}/approvals/{approvalId}/grant
Representative request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/approvals/appr_01HRJ62B2S2H12XATB9TQX6W7R/grant' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{}'
Representative error response:
{
"code": 9,
"message": "approval appr_01HRJ62B2S2H12XATB9TQX6W7R is no longer pending",
"details": []
}
This example is representative of the public google.rpc.Status failure shape. The exact code and message text may vary by deployment and implementation details.
Client guidance:
- treat approval actions as non-idempotent workflow transitions
- refresh session and review state after any failure
- do not assume you can retry grant blindly
32.2 Denying an approval with reviewer feedback
The deny body is intentionally small. Use it to return actionable feedback rather than a long audit trail.
Route:
POST /v1/sessions/{sessionId}/approvals/{approvalId}/deny
Request:
curl -X POST 'https://api.aetherfs.io/v1/sessions/sess_1234abcd/approvals/appr_01HRJ62B2S2H12XATB9TQX6W7R/deny' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{
"feedback": "Split the generated changes into smaller checkpoints and rerun tests before requesting approval again."
}'
Response:
{}
Client guidance:
- preserve the feedback for the user or agent that requested approval
- expect the session to return to an active state rather than remain blocked forever
- follow denial with fresh annotations, a new summary, or a new approval request only after the underlying issue is fixed
32.3 Unknown approval ID
If the approval ID is wrong or no longer visible in the current auth context, the failure still uses the same generic public error envelope.
Representative error response:
{
"code": 5,
"message": "approval appr_missing was not found for session sess_1234abcd",
"details": []
}
Client guidance:
- do not synthesize approval IDs client-side
- store the
approvalIdreturned byrequest-approval - refresh the session state before presenting retry controls
33. Standard error shape
Unexpected errors follow the google.rpc.Status shape in the public contract.
Example:
{
"code": 7,
"message": "permission denied for session sess_1234abcd",
"details": []
}
Treat this as the generic envelope for authorization, validation, not-found, and other API failures unless a route documents something more specific.
Recommended client approach
Use these examples as a starting point, then combine them into workflows such as:
- create session
- read manifest
- annotate or publish progress
- request approval
- commit after approval
See also: