---
title: Folders
path: api-guides/folders
status: published
---

Folder CRUD plus hierarchical listing and move. Folders group files and subfolders inside a share.

**Base path:** `/api/v1/folders/`

## Folder metadata shape

```json
{
  "id": "fld_01J3I",
  "name": "Engineering",
  "share_id": "shr_01J3H",
  "parent_id": null,
  "path": "/Engineering",
  "owner_id": "usr_01J3N",
  "created_by": "usr_01J3N",
  "created_at": "2026-04-01T10:00:00Z",
  "modified_at": "2026-04-23T10:15:00Z"
}
```

`parent_id: null` means the folder is at the share root.

## Get folder metadata

```bash
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/folders/fld_01J3I
```

Requires `READ` on the folder.

## List folder contents

```bash
curl -G $SCAIDRIVE_URL/api/v1/folders/fld_01J3I/children \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  --data-urlencode "share_id=shr_01J3H"
```

```python
resp = httpx.get(
    f"{url}/api/v1/folders/fld_01J3I/children",
    headers={"Authorization": f"Bearer {token}"},
    params={"share_id": "shr_01J3H"},
)
contents = resp.json()
for item in contents["items"]:
    print(item["type"], item["name"])
```

```typescript
const params = new URLSearchParams({ share_id: "shr_01J3H" });
const resp = await fetch(
  `${url}/api/v1/folders/fld_01J3I/children?${params}`,
  { headers: { Authorization: `Bearer ${token}` } },
);
const { items } = await resp.json();
```

Response:

```json
{
  "items": [
    {"id": "fld_01J3J", "name": "Specs", "type": "folder"},
    {"id": "fil_01J3K", "name": "spec.pdf", "type": "file", "size": 48293, "mime_type": "application/pdf", "modified_at": "2026-04-23T10:15:00Z"}
  ],
  "path": "/Engineering",
  "total": 2
}
```

Query parameters:

| Param | Default | Notes |
|-------|---------|-------|
| `share_id` | required | The share this folder is in |
| `include_deleted` | `false` | Include soft-deleted items (for trash views) |

Items are a mixed array of files and folders. Distinguish by `type`.

### Listing the share root

To list the root of a share (top-level folders and files):

```bash
curl -G $SCAIDRIVE_URL/api/v1/folders/root/children \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  --data-urlencode "share_id=shr_01J3H"
```

The special folder ID `root` refers to the share root.

## Create a folder

```bash
curl -X POST $SCAIDRIVE_URL/api/v1/folders \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "share_id": "shr_01J3H",
    "name": "Drafts",
    "parent_id": "fld_01J3I"
  }'
```

Omit `parent_id` to create at the share root.

Requires `CREATE` on the parent folder (or share, for root).

Folder names are unique within their parent. A second folder called `Drafts` in the same parent returns `409 NAME_CONFLICT`.

## Rename a folder

```bash
curl -X PATCH $SCAIDRIVE_URL/api/v1/folders/fld_01J3J \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Approved Specs"}'
```

Requires `WRITE`. The `path` of the folder and all descendants is recomputed; children all get `renamed` change-log entries propagated.

## Delete a folder

```bash
curl -X DELETE $SCAIDRIVE_URL/api/v1/folders/fld_01J3J \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN"
```

Soft delete. Descendants (subfolders and files) are recursively soft-deleted.

Permanent delete:

```bash
curl -X DELETE "$SCAIDRIVE_URL/api/v1/folders/fld_01J3J?permanent=true" \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN"
```

Requires `DELETE` on the folder and all its descendants. If the caller lacks `DELETE` on any descendant, the operation fails atomically — partial deletion is never left behind.

## Move a folder

```bash
curl -X POST $SCAIDRIVE_URL/api/v1/folders/fld_01J3J/move \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target_parent_id": "fld_01J4A"}'
```

Or move to another share:

```json
{"target_share_id": "shr_01J4Z"}
```

Moving a folder moves everything inside. Requires `DELETE` on source, `CREATE` on target, plus `READ` on the entire subtree. Cross-share moves trigger a permission re-evaluation for descendants — ACLs inherited from the old parent are replaced by ACLs inherited from the new parent.

## Walking a tree

To list an entire subtree, traverse recursively. There's no single-call "give me everything under this folder" endpoint on purpose — large subtrees would produce unbounded responses and time out. Instead:

```python
def walk_folder(client, folder_id, share_id):
    resp = client.get(
        f"/api/v1/folders/{folder_id}/children",
        params={"share_id": share_id},
    )
    for item in resp.json()["items"]:
        yield item
        if item["type"] == "folder":
            yield from walk_folder(client, item["id"], share_id)
```

If you need the whole share for caching/indexing, consume the sync changelog from cursor 0 — it yields every resource in order, once, with no pagination issues. See [Sync Model](/docs/scaidrive/core-concepts/sync-model).

## Error cases

| Code | When |
|------|------|
| `NOT_FOUND` | Folder doesn't exist or caller can't see it |
| `NAME_CONFLICT` | Sibling with same name exists |
| `AUTHZ_PERMISSION_DENIED` | Missing required permission (including on descendants during delete/move) |
| `INVALID_PATH` | Name contains disallowed characters (`/`, `\0`, leading/trailing whitespace) |
| `RESOURCE_DELETED` | Folder is soft-deleted |

## What's next

- [Files](/docs/scaidrive/api-guides/files) — file operations inside folders.
- [Uploads and Downloads](/docs/scaidrive/api-guides/uploads-and-downloads) — bulk content transfer.
- [Permissions and ACLs](/docs/scaidrive/core-concepts/permissions-and-acls) — folder ACL inheritance.