This guide explains how Operating organizes data and why entities relate the way they do. Understanding the data model is essential for building integrations, writing efficient queries, and avoiding common mistakes.
Overview: The Core Entity Chain
At the heart of Operating is a chain of entities that represent work, from high-level planning to detailed execution:
Planning work:
Project → Position (optionally: Person + Role) → Allocation
Tracking work:
Person + Project → Time Entry
The key insights:
- Allocations (planned work) flow through positions. You create a position on a project (optionally assigning a person and role), then create allocations on that position.
- Time entries (actual work) are independent. They link a person directly to a project and are not connected to allocations or positions.
Why This Structure Exists
Consulting businesses have complex needs:
- The same person might play multiple roles on the same project (e.g., both Developer and Tech Lead)
- Billing rates often depend on role, seniority, and location
- Plans change constantly (tentative vs confirmed work)
- Reporting needs to distinguish between planned work and actual work
Operating's data model handles these realities by separating planning (allocations) from execution (time entries) and using positions as the layer where roles and rates are determined.
Core Entities Explained
Organization
Your Operating instance. All data belongs to one organization.
In the API:
- Not exposed as an entity (implicit in all requests)
- All entities are scoped to your organization
Company
A legal entity within your organization. Multi-company organizations (e.g., holding companies with multiple subsidiaries) use this to separate invoicing and accounting.
In the API:
GET /companies— List companiesGET /companies/{id}— Get company details
Key relationships:
- Each Project belongs to one Company
- Each Person belongs to one Company
When to use: Most organizations have one company. Use multiple companies if you need separate invoicing configurations for different legal entities or for reporting.
Site
A physical or logical location (office, region, etc.). Sites are used for:
- Organizational reporting
- Filtering people and projects
- Managing default working time and holiday calendars of persons
In the API:
GET /sites— List sitesPOST /sites— Create site
Key relationships:
- Each Project belongs to one Site
- Each Person belongs to one Site
Client
A customer company purchasing services. Clients can be external (real customers) or internal (for internal projects).
In the API:
GET /clients— List clientsPOST /clients— Create clientPATCH /clients/{id}— Update client
Key relationships:
- Each Project optionally has one Client (required for billable projects)
Internal clients: Mark a client as internal (isInternal: true) to define internal projects that shouldn't appear in client-facing reports.
Project
The central entity. Projects organize work, track budgets, and determine billing.
In the API:
GET /projects— List projectsPOST /projects— Create projectPATCH /projects/{id}— Update project
Three project types:
- Client projects (client is an external client) — Work for external clients can be billable or non-billable
- Internal projects (client is an internal client) — Internal work, no revenue
- Time off project (
isTimeOff: true) — Special system project for vacation/leave
Key relationships:
- Projects have one Client
- Projects have many Positions
- Projects optionally have many Budgets (especially for fixed-price projects)
- Projects optionally have many Tasks
Person
An individual in your organization (employee or external contractor).
In the API:
GET /persons— List personsPOST /persons— Create personPATCH /persons/{id}— Update person
Key relationships:
- Persons have many Positions (assignments to projects)
- Persons have many Competence Role Assignments (what roles they can play)
- Persons have many Person Costs (cost rates for different date ranges)
- Persons have many Time Entries (actual work done)
Position
This is the critical layer. A position represents an assignment on a project, optionally with a person and role.
Why positions exist:
- Open positions: Can exist without a person assigned (unfilled staffing needs)
- Same person, multiple roles: Maria can be both "Senior Developer" and "Tech Lead" on the same project (two positions)
- Organizational clarity: Clear assignment of who is working on what project in what capacity
In the API:
GET /positions— List positionsPOST /positions— Create positionPATCH /positions/{id}— Update position
Key relationships:
- Each Position has many Allocations (planned work)
Note: Time entries are not linked to positions. They are tracked directly on person + project.
Creating positions: Always create the position first, then create allocations on it. You cannot create allocations directly on a person+project.
Allocation
Planned work. An allocation says "this position is planned to work X% of their time from date A to date B."
In the API:
GET /allocations— List allocationsPOST /allocations— Create allocationPATCH /allocations/{id}— Update allocation
Percentage encoding:
- 100% = 1,000,000
- 50% = 500,000
- 25% = 250,000
Confirmed vs Tentative:
- Confirmed: Committed work, treated as certain in forecasts
- Tentative: Planned but not locked in (e.g., for pipeline deals that might not close)
Overlapping allocations: The API allows creating allocations that sum to >100% for a person (intentional for overbooking scenarios). Validation is up to the client. Allocations for a single position cannot overlap.
Key relationships:
- Each Allocation belongs to one Position
Time Entry
Actual work done. A time entry records "person X worked Y hours on project Z on date D."
In the API:
GET /time-entries— List time entriesPOST /time-entries— Create time entryPOST /time-entries/bulk— Create multiple time entriesPATCH /time-entries/{id}— Update time entryDELETE /time-entries/{id}— Delete time entry
Key relationships:
- Each Time Entry belongs to one Person
- Each Time Entry belongs to one Project
- Each Time Entry optionally belongs to one Task
Budget
A monetary value assigned to a project or project phase. Particularly important for fixed-price projects.
In the API:
GET /budgets— List budgetsPOST /budgets— Create budgetPATCH /budgets/{id}— Update budget
Key constraints:
- Budgets for the same project cannot overlap in time
- A project can have zero, one, or many budgets
Task
Optional granular work items within a project. Tasks allow tracking work at a more detailed level than just project-level.
In the API:
GET /tasks— List tasksPOST /tasks— Create taskPATCH /tasks/{id}— Update task
When to use: Tasks are optional. Use them when:
- You need detailed tracking for invoicing
- Project management requires task-level visibility
- Reporting needs to break down work by deliverable
Time entry relationship: Time entries can optionally reference a task for granular tracking.