Guide
When a new employee joins, they usually ask three questions: Who is my manager? Who are my peers? And where does my team sit in the grand scheme of things?
Static PDFs or buried slideshows are usually out of date by the time a new hire starts. By automating the Org Chart, you provide every "newbie" with a real-time map of the company. It transforms onboarding from a series of "Who is that?" questions into a clear, visual understanding of the company's DNA.
How the Data is structured
Before the magic happens, you need a simple, logical foundation in your Fibery workspace. We rely on two primary databases and a "self-relation" to build the tree.
The Challenge - navigating the "Wall of Names"
New hires often face these specific hurdles when trying to learn the team structure:
A flat list of 50+ contacts is impossible to parse. Without a visual hierarchy, it’s just a "wall of names."
A job title like "Lead Coordinator" doesn't explain where that person sits in the reporting line.
Standard relational views require users to click through multiple levels to see the full tree. Newcomers often get lost or give up before seeing the whole picture.
Whiteboard is a great thing to have, but it doesn't add new entities automatically and requires manual update… (for now)
The relatively "live" visual Org Chart
We solve this by using Mermaid.js to render a visual diagram inside a Rich Text field on the Organization entity.
Instead of a boring list, the new hire sees a top-down tree. The system looks at your "Reports To" links and draws the lines automatically. If a manager changes or a new person joins, the diagram updates the next time the script runs—no manual drawing required.
They open the Organization page and see the entire structure immediately.
Names and Job Titles are displayed in the boxes, helping them associate faces with functions.
They can trust the chart is accurate because it’s powered by the same data used for HR and operations.
Automations for dynamic updates
To ensure the Org Chart isn't just a static snapshot, we set up an Automation Rule on the Organization database level. This rule acts as a "live observer" that watches for changes in your team structure and triggers the script to redraw the diagram instantly.
Trigger: "When Entity linked to a Organization"
The rule is set to fire every time a new Contact is linked to an Organization. This is the most critical part of the dynamic logic:
As soon as you hire a new employee or move a contractor into a team, the rule detects the new link.
Because it triggers on the "Link" action, you don't have to remember to click a button or manually refresh the page; the system handles it in the background.
Action: Executing the Script
Once the trigger is activated, the system moves to the "Then" step: Execute Javascript code.
The rule passes the specific Organization involved (Step 1 Organization) directly into the script.
The script begins by reading the specific fields you've defined - such as Direct Reports and Job Title - to gather the latest data.
You can also add additional filters - for example, also to check whether this person is active or not.
Script and explanation
Please, note that Script action is accessible on the Pro & Enterprise plans.
Read more about Scripts in Automations
// ===== CONFIGURATION =====
const ORGANIZATION_CONTACTS_FIELD = 'Contacts';
const CONTACT_CHILDREN_FIELD = 'Direct Reports';
const ORG_DIAGRAM_FIELD = 'Org Chart';
const CONTACT_DB = 'Org structure/Contact';
const JOB_TITLE_FIELD = 'Job Title';
const fibery = context.getService('fibery');
// ===== HELPER FUNCTIONS =====
function node(contact) {
const jobTitle = contact[JOB_TITLE_FIELD] ? `<br>${contact[JOB_TITLE_FIELD]}` : '';
const cleanName = contact.Name.replace(/[<>"/]/g, '');
return `C${contact.id}(["<b>${cleanName}</b>${jobTitle}"])`;
}
function edge(parent, child) {
return `C${parent.id} --> C${child.id}`;
}
// ===== MAIN EXECUTION =====
for (const organization of args.currentEntities) {
const organizationEntity = await fibery.getEntityById(
organization.type,
organization.id,
[ORGANIZATION_CONTACTS_FIELD, ORG_DIAGRAM_FIELD]
);
const organizationContacts = organizationEntity[ORGANIZATION_CONTACTS_FIELD] ?? [];
if (organizationContacts.length === 0) {
await fibery.setDocumentContent(organizationEntity[ORG_DIAGRAM_FIELD].Secret, "No contacts linked.", 'md');
continue;
}
const contactIds = organizationContacts.map(c => c.id);
const allContactsArray = await fibery.getEntitiesByIds(
CONTACT_DB,
contactIds,
['Name', CONTACT_CHILDREN_FIELD, JOB_TITLE_FIELD]
);
const allContactsMap = new Map(allContactsArray.map(c => [c.id, c]));
const visited = new Set();
const nodes = new Set();
const edges = new Set();
// Recursive traversal function
const walk = (contact) => {
if (visited.has(contact.id)) return;
visited.add(contact.id);
nodes.add(node(contact));
const children = contact[CONTACT_CHILDREN_FIELD] ?? [];
for (const childRef of children) {
const child = allContactsMap.get(childRef.id);
if (child) {
edges.add(edge(contact, child));
walk(child);
}
}
};
// Build hierarchy
for (const contact of allContactsArray) {
walk(contact);
}
// Generate Mermaid Block
const diagramLines = [
"```mermaid",
"graph TD",
...Array.from(nodes),
...Array.from(edges),
"```"
];
await fibery.setDocumentContent(
organizationEntity[ORG_DIAGRAM_FIELD].Secret,
diagramLines.join("\n"),
'md'
);
}
1. Configuration Constants
const ORGANIZATION_CONTACTS_FIELD = 'Contacts';
const CONTACT_CHILDREN_FIELD = 'Direct Reports';
const ORG_DIAGRAM_FIELD = 'Org Chart';
const CONTACT_DB = 'Org structure/Contact';
const JOB_TITLE_FIELD = 'Job Title';
These constants act as the "control panel" of the script. They define:
Database Paths: Which specific database holds your people (Org structure/Contact).
Relationship Keys: The field names used to link Organizations to People and People to each other (Direct Reports).
Target Field: The specific Rich Text field where the final visual diagram will be rendered.
Maintainability: By defining these at the top, you can update your database structure in one place without digging through the logic.
2. Helper Functions
The script uses two small functions to translate Fibery data into "Mermaid" language:
node(contact): This creates the box. It grabs the Name and Job Title, adds some HTML styling (<b>, <br>), and wraps it in a unique ID so Mermaid knows exactly which box is which.
edge(parent, child): This creates the arrow. It simply says "Point an arrow from Person A to Person B."
3. Data loading & mapping
const allContactsMap = new Map(allContactsArray.map(c => [c.id, c]));
Before building the chart, the script performs a Batch Load. It grabs every contact linked to the organization in one single request to keep performance high.
4. Recursive Traversal
const walk = (contact) => {
if (visited.has(contact.id)) return;
// ... logic ...
walk(child);
};
This is the most critical part of the logic. The script uses a Recursive "Walk":
It starts with a person and marks them as "Visited" (to prevent infinite loops if the data has a circular reference).
It creates a Node (box) for that person.
It looks at their Direct Reports. For every report found, it creates an Edge (arrow) and then calls itself again to "walk" down that new branch.
It continues until it reaches the very "leaves" of the tree (people with no reports).
5. Building the Diagram block
const diagramLines = [ "```mermaid", "graph TD", ...Array.from(nodes), ...Array.from(edges), "```" ];
Once the "walk" is finished, the script assembles the pieces:
graph TD: Tells Mermaid to draw the chart Top-Down.
Nodes & Edges: It dumps all the boxes and arrows it collected during the walk.
Code Block: It wraps everything in Triple Backticks. This is the signal Fibery needs to stop showing raw text and start showing a visual diagram.
6. Document Writing
The final step uses fibery.setDocumentContent. It sends the completed Mermaid code to the Secret (the background data layer) of your Rich Text field. Because it’s marked as Markdown ('md'), Fibery immediately renders the beautiful visual tree for the user.
Some ending
This solution is a powerful workaround (yeah, still workaround) because, currently, standard relational views in a system like Fibery may lack a native, high-level "Collapse All" or "Expand All" feature for deep hierarchies in Context views, making them difficult to navigate as they grow. And our Whiteboards not yet can update themselves with new data. Furthermore, while standard list views are great for data entry, they are not designed to be visual diagramming tools. By using a script to generate Mermaid.js code, we essentially "borrow" a specialized visualization engine to bypass these UI limitations and create a clear, bird’s-eye view that updates itself automatically.
The logic used in this script - traversing relationships and drawing connections - can be easily adapted for several other high-level use cases, no matter which industry you are from.
Legal & corporate entity structures
Map parent corporations to their various subsidiaries and local holdings. This provides a clear visual for legal teams to track ownership chains and liability across different jurisdictions at a glance.
Biological & taxonomy classifications
Catalog the "tree of life" by visualizing hierarchies from Kingdom down to Species. This allows researchers to see the structural relationships between specimens without navigating nested folders.
Technical Infrastructure & Asset mapping
Visualize how hardware components are logically connected, such as Data Centers to Racks and specific Servers. It helps IT teams identify single points of failure by showing which assets are "hosted on" others.
Cost center & budget hierarchies
Break down "Master Budgets" into nested Cost Centers and specific line items. This helps finance teams visualize the flow of capital from top-level treasury down to departmental expenses.
Educational curriculum & learning paths
Map out how a "Subject" or "Certification" breaks down into specific Modules and Lessons. This allows students to see the prerequisite path and understand how individual units aggregate into a degree.