Organization Hierarchy
Organizations can be nested to create hierarchical structures that mirror your company's organizational chart.
Get Organization Tree
Retrieve the full hierarchy starting from a specific organization.
Endpoint
GET /organizations/:id/tree
Response (200 OK)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Engineering",
"slug": "engineering",
"status": "ACTIVE",
"depth": 0,
"children": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Frontend Team",
"slug": "frontend-team",
"status": "ACTIVE",
"depth": 1,
"children": []
},
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"name": "Backend Team",
"slug": "backend-team",
"status": "ACTIVE",
"depth": 1,
"children": [
{
"id": "d4e5f6a7-b8c9-0123-def0-234567890123",
"name": "API Team",
"slug": "api-team",
"status": "ACTIVE",
"depth": 2,
"children": []
}
]
}
]
}
Get Direct Children
Retrieve only the immediate children of an organization.
Endpoint
GET /organizations/:id/children
Response (200 OK)
{
"data": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Frontend Team",
"slug": "frontend-team",
"status": "ACTIVE",
"parentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"name": "Backend Team",
"slug": "backend-team",
"status": "ACTIVE",
"parentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
]
}
Moving Organizations
Change an organization's parent to restructure the hierarchy.
Endpoint
PATCH /organizations/:id
Request
{
"parentId": "new-parent-uuid"
}
Rules
- No circular references: An organization cannot be its own ancestor
- Depth limit: Cannot exceed maximum nesting depth (default: 5)
- Same tenant: Cannot move to a parent in a different tenant
Error: Circular Reference
{
"type": "https://api.cortex.purplelab.ai/errors/validation",
"title": "Validation Error",
"status": 400,
"detail": "Cannot set parent: would create circular reference"
}
Error: Depth Exceeded
{
"type": "https://api.cortex.purplelab.ai/errors/validation",
"title": "Validation Error",
"status": 400,
"detail": "Cannot move organization: would exceed maximum hierarchy depth of 5"
}
Get Root Organizations
List all top-level organizations (no parent).
Request
GET /organizations?parentId=null
or
GET /organizations?root=true
Hierarchy Visualization
Building a Tree UI
interface OrgNode {
id: string;
name: string;
children: OrgNode[];
depth: number;
}
function renderOrgTree(node: OrgNode, indent = 0): string {
const prefix = ' '.repeat(indent) + (indent > 0 ? '├── ' : '');
let output = `${prefix}${node.name}\n`;
node.children.forEach((child, index) => {
output += renderOrgTree(child, indent + 1);
});
return output;
}
// Usage
const tree = await fetch(`/organizations/${rootId}/tree`).then(r => r.json());
console.log(renderOrgTree(tree));
// Output:
// Engineering
// ├── Frontend Team
// ├── Backend Team
// ├── API Team
React Tree Component
interface TreeNodeProps {
node: OrgNode;
onSelect: (id: string) => void;
}
function TreeNode({ node, onSelect }: TreeNodeProps) {
const [expanded, setExpanded] = useState(true);
return (
<div className="tree-node">
<div
className="node-label"
onClick={() => onSelect(node.id)}
>
{node.children.length > 0 && (
<button onClick={() => setExpanded(!expanded)}>
{expanded ? '▼' : '▶'}
</button>
)}
{node.name}
</div>
{expanded && node.children.length > 0 && (
<div className="node-children">
{node.children.map(child => (
<TreeNode key={child.id} node={child} onSelect={onSelect} />
))}
</div>
)}
</div>
);
}
Path to Root
Get the path from an organization to the root:
async function getPathToRoot(orgId: string): Promise<Organization[]> {
const path: Organization[] = [];
let currentId: string | null = orgId;
while (currentId) {
const org = await getOrganization(currentId);
path.unshift(org); // Add to beginning
currentId = org.parentId;
}
return path;
}
// Usage
const path = await getPathToRoot('api-team-id');
// Result: [Engineering, Backend Team, API Team]
Breadcrumb Navigation
function OrgBreadcrumb({ orgId }: { orgId: string }) {
const [path, setPath] = useState<Organization[]>([]);
useEffect(() => {
getPathToRoot(orgId).then(setPath);
}, [orgId]);
return (
<nav className="breadcrumb">
{path.map((org, index) => (
<span key={org.id}>
{index > 0 && ' / '}
<Link to={`/organizations/${org.id}`}>{org.name}</Link>
</span>
))}
</nav>
);
}