Content folder service¶
Knowledge-base folders are content scopes: they have a scopeId (exposed in the toolkit as scope_id on :class:~unique_toolkit.experimental.resources.content_folder.schemas.CreatedFolder and related models). The :class:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder wraps create, read, access changes, and delete calls so you can use the same credentials as the rest of the toolkit (see Getting started).
This page focuses on a short manual test loop: create a folder, confirm it exists (API + optional UI), optionally stop in a debugger, then delete it again.
Environment
Use a working UniqueSettings / SDK setup (UNIQUE_API_KEY, UNIQUE_APP_ID, user and company context) as in other examples. The runnable script below calls :meth:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.from_settings.
Ways to create folders¶
:meth:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.create accepts exactly one of these shapes per call (mixing them raises TypeError):
| Style | When to use |
|---|---|
paths= |
One or more absolute paths from the KB root—pass a str for a single path or a list[str] for several in one API call. |
parent_scope_id= + relative_path_segments= |
You already know the parent folder’s scope id and want to create one or more nested segments by name (not full paths). |
Segments are folder names only—e.g. ["2026", "Inbox"] creates Inbox under 2026 under the given parent—not a second absolute path string.
Several absolute paths¶
DEMO_MULTI_A = "/EntangledToolkitDocs/MultiA"
DEMO_MULTI_B = "/EntangledToolkitDocs/MultiB"
created = folder_service.create(paths=[DEMO_MULTI_A, DEMO_MULTI_B])
for folder in created:
print(folder.id, folder.name)
<<example-script-deps>>
<<folder-mgmt-imports>>
<<folder-mgmt-init>>
<<folder-mgmt-constants-multi>>
<<folder-mgmt-create-multi-paths>>
Multiple absolute paths (full script)
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "unique-toolkit>=2026.22.0",
# "unique-sdk>=2026.22.0",
# ]
# ///
# %%
from __future__ import annotations
from unique_toolkit.experimental.resources.content_folder import ContentFolder
folder_service = ContentFolder.from_settings()
DEMO_MULTI_A = "/EntangledToolkitDocs/MultiA"
DEMO_MULTI_B = "/EntangledToolkitDocs/MultiB"
created = folder_service.create(paths=[DEMO_MULTI_A, DEMO_MULTI_B])
for folder in created:
print(folder.id, folder.name)
Under a parent you know by scope id¶
Typical sources for parent_scope_id:
- The
idon :class:~unique_toolkit.experimental.resources.content_folder.schemas.CreatedFolderafter an earliercreate(oftencreated[-1].idfor the leaf of a chain). - The
idon :class:~unique_toolkit.experimental.resources.content_folder.schemas.FolderInfofrom :meth:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.read(byscope_idorfolder_path). - Any scope id you already store from the product UI or another API.
Then pass path segments (child folder names) to create under that parent:
PARENT_ROOT = "/EntangledToolkitDocs/ParentRoot"
parent_chain = folder_service.create(paths=PARENT_ROOT)
parent_scope_id = parent_chain[-1].id
nested_chain = folder_service.create(
parent_scope_id=parent_scope_id,
relative_path_segments=["Projects", "Scratch"],
)
leaf = nested_chain[-1]
print("parent_scope_id=", parent_scope_id)
print("leaf id=", leaf.id, "name=", leaf.name)
<<example-script-deps>>
<<folder-mgmt-imports>>
<<folder-mgmt-init>>
<<folder-mgmt-constants-nested>>
<<folder-mgmt-create-nested-under-parent>>
Create under parent by scope id (full script)
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "unique-toolkit>=2026.22.0",
# "unique-sdk>=2026.22.0",
# ]
# ///
# %%
from __future__ import annotations
from unique_toolkit.experimental.resources.content_folder import ContentFolder
folder_service = ContentFolder.from_settings()
PARENT_ROOT = "/EntangledToolkitDocs/ParentRoot"
parent_chain = folder_service.create(paths=PARENT_ROOT)
parent_scope_id = parent_chain[-1].id
nested_chain = folder_service.create(
parent_scope_id=parent_scope_id,
relative_path_segments=["Projects", "Scratch"],
)
leaf = nested_chain[-1]
print("parent_scope_id=", parent_scope_id)
print("leaf id=", leaf.id, "name=", leaf.name)
Under a parent you only know by path¶
If you have the parent’s absolute path but not its id, resolve it once, then create by scope id and segments:
parent_meta = folder_service.read(folder_path=PARENT_ROOT)
nested = folder_service.create(
parent_scope_id=parent_meta.id,
relative_path_segments=["FromPathLookup"],
)
print("leaf", nested[-1].id, nested[-1].name)
This is the same API shape as above; only the parent id comes from :meth:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.read instead of a previous create result.
Create, verify, delete¶
Typical flow:
- Create with an absolute path from the knowledge-base root (one of the supported shapes on :meth:
~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.create). - Verify with :meth:
~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.readusing the leaf folder’sscope_id(last entry returned from create when the path has multiple segments). - Pause on a breakpoint or
breakpoint()after step 2, reload the knowledge-base tree in the product UI, and confirm the folder appears. - Delete with :meth:
~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.deleteusing the samescope_id(or the absolutefolder_path). Inspectsuccess_folders/failed_folderson the :class:~unique_toolkit.experimental.resources.content_folder.schemas.DeleteResultif something does not match expectations.
from __future__ import annotations
from unique_toolkit.experimental.resources.content_folder import ContentFolder
# Absolute path from the KB root; change if this collides with real data.
DEMO_FOLDER_PATH = "/EntangledToolkitDocsDemo"
folder_service = ContentFolder.from_settings()
created = folder_service.create(paths=DEMO_FOLDER_PATH)
leaf = created[-1]
print(f"Created folder id={leaf.id!r} name={leaf.name!r}")
info = folder_service.read(scope_id=leaf.id)
print(
f"Verified id={info.id!r} name={info.name!r} parent_id={info.parent_id!r} "
f"(created as {DEMO_FOLDER_PATH!r}; compare in the UI)"
)
# Set a breakpoint here (IDE or `breakpoint()`) to inspect `info` and confirm in the UI.
delete_result = folder_service.delete(scope_id=leaf.id)
print("Deleted:", delete_result.success_folders, "Failed:", delete_result.failed_folders)
Run this example¶
Fetch the script at the latest released unique-toolkit tag and execute it with uv (requires network access to resolve dependencies).
Trust and dependencies
uv run may install packages declared in the script's inline metadata (PEP 723).
Set RELEASE=unique-toolkit-v<version> to lock to a specific release for a fully
reproducible run; otherwise the snippet always picks the newest non-prerelease
unique-toolkit-v* tag from Unique-AG/ai. We deliberately fetch from an immutable
release tag instead of a mutable branch tip.
# Optional: pin to a specific release, e.g.
# RELEASE=unique-toolkit-v2026.22.0
RELEASE="${RELEASE:-}"
if [ -z "$RELEASE" ]; then
# Unauthenticated GitHub API allows 60 req/hr/IP — export GH_TOKEN for higher limits.
RELEASE=$(
curl -fsSL ${GH_TOKEN:+-H "Authorization: Bearer $GH_TOKEN"} \
"https://api.github.com/repos/Unique-AG/ai/releases?per_page=100" \
| python3 -c '
import json, sys
for r in json.load(sys.stdin):
if r.get("prerelease") or r.get("draft"):
continue
tag = r.get("tag_name", "")
if tag.startswith("unique-toolkit-v"):
print(tag); break
'
)
fi
if [ -z "$RELEASE" ]; then
echo "Could not resolve latest unique-toolkit release; set RELEASE=unique-toolkit-v<version>." >&2
exit 1
fi
echo "Using release: $RELEASE"
curl -fsSL -o example.py \
"https://raw.githubusercontent.com/Unique-AG/ai/${RELEASE}/unique_toolkit/docs/examples_from_docs/folder_create_verify_delete.py"
uv run example.py
rm -f example.py
Environment variables
Set UNIQUE_APP_KEY, UNIQUE_APP_ID, UNIQUE_AUTH_USER_ID, and UNIQUE_AUTH_COMPANY_ID (and other UNIQUE_* values your tenant needs) in the environment or a .env file before running. See Getting started for details.
Assembled runnable script¶
The block below is tangled to docs/.python_files/ and copied to examples_from_docs when you run generate_examples.sh in the unique_toolkit package root.
<<example-script-deps>>
<<folder-mgmt-imports>>
<<folder-mgmt-constants>>
<<folder-mgmt-init>>
<<folder-mgmt-create>>
<<folder-mgmt-verify>>
<<folder-mgmt-delete>>
Full example (click to expand)
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "unique-toolkit>=2026.22.0",
# "unique-sdk>=2026.22.0",
# ]
# ///
# %%
from __future__ import annotations
from unique_toolkit.experimental.resources.content_folder import ContentFolder
# Absolute path from the KB root; change if this collides with real data.
DEMO_FOLDER_PATH = "/EntangledToolkitDocsDemo"
folder_service = ContentFolder.from_settings()
created = folder_service.create(paths=DEMO_FOLDER_PATH)
leaf = created[-1]
print(f"Created folder id={leaf.id!r} name={leaf.name!r}")
info = folder_service.read(scope_id=leaf.id)
print(
f"Verified id={info.id!r} name={info.name!r} parent_id={info.parent_id!r} "
f"(created as {DEMO_FOLDER_PATH!r}; compare in the UI)"
)
# Set a breakpoint here (IDE or `breakpoint()`) to inspect `info` and confirm in the UI.
delete_result = folder_service.delete(scope_id=leaf.id)
print(
"Deleted:", delete_result.success_folders, "Failed:", delete_result.failed_folders
)
Delete by path¶
If you prefer to address the folder by path instead of id, pass folder_path= (still exactly one of scope_id or folder_path):
folder_service.delete(folder_path=DEMO_FOLDER_PATH)
Behaviour notes¶
- Creator access: By default, :meth:
~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.creategrants the acting user READ and WRITE on created scopes wheninherit_accessis false. See the class docstring for details. - Recursive delete: Pass
recursive=Trueto :meth:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.deletewhen the API should remove nested content according to platform rules. - Async: Use :meth:
~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.create_async, :meth:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.read_async, and :meth:~unique_toolkit.experimental.resources.content_folder.service.ContentFolder.delete_asyncin async code paths.
For uploading and searching documents inside a scope, continue with the Knowledge Base service examples.