Core Concepts
The ProBeya data model explained through a pharma plant example. Understand the hierarchy before you build.
The pharma plant analogy
Think of ProBeya’s data model like a manufacturing site:
Organization = Your company (UCB, Pfizer, Acme Pharma)
└── Workspace = A site or department (Site Braine, Quality Dept)
└── Project = A specific initiative (Packaging Line 3 Transformation)
└── Board = The daily management board (SQCDP Board)
├── Group = A category row (Safety, Quality, Cost, Delivery, People)
│ └── Item = A single record (near-miss report, action, observation)
│ └── Value = Cell data for that item (status: "open", priority: "high")
└── Column = A field definition (Status, Priority, Due Date, Owner)
Every API call operates within this hierarchy. You always need an organizationId (resolved from your subdomain), and then navigate down: workspace, project, board, item.
Data model reference
| Entity | What it represents | ID format | Key fields |
|---|---|---|---|
| Organization | Your company tenant | cuid2 | name, slug (subdomain), plan |
| Workspace | Department or site | cuid2 | name, slug, level, parentId |
| Project | Initiative or value stream | cuid2 | name, workspaceId |
| Board | The working surface | cuid2 | name, projectId |
| Group | Category row on the board | cuid2 | name, color, sortOrder, boardId |
| Item | A task, issue, or record | cuid2 | name, groupId, boardId, assigneeId |
| Column | Field definition | cuid2 | name, type, boardId, config |
| Value | Cell data (EAV pattern) | cuid2 | itemId, columnId, value (JSONB) |
All IDs are cuid2 strings — collision-resistant, URL-safe, and generated client-side when needed. No auto-incrementing integers.
Custom fields: the EAV pattern
ProBeya uses an Entity-Attribute-Value pattern for maximum flexibility. Instead of fixed columns in a database table, each board defines its own Columns (field definitions), and each Item stores its data as Values — one Value per item per column.
Column: { id: "col_1", name: "Status", type: "status", boardId: "brd_1" }
Column: { id: "col_2", name: "Priority", type: "label", boardId: "brd_1" }
Column: { id: "col_3", name: "Batch ID", type: "text", boardId: "brd_1" }
Item: { id: "item_1", name: "Yield deviation on batch BX-2026-0412" }
Value: { itemId: "item_1", columnId: "col_1", value: {"label": "open", "color": "#ef4444"} }
Value: { itemId: "item_1", columnId: "col_2", value: {"label": "high", "color": "#f97316"} }
Value: { itemId: "item_1", columnId: "col_3", value: "BX-2026-0412" }
This means any board can have any combination of 25+ field types without schema migrations.
Supported column types
| Type | Stored as | Example use |
|---|---|---|
text | String | Batch ID, lot number, description |
number | Number | OEE percentage, cycle time, temperature |
status | JSON (label + color) | Open, In Progress, Done, Verified |
label | JSON (label + color) | High / Medium / Low priority |
person | User ID | Owner, reviewer, approver |
date | ISO 8601 string | Due date, target completion |
checkbox | Boolean | Completed, approved, verified |
rating | Number (1-5) | Severity score, impact rating |
formula | Computed | Auto-calculated from other columns |
dependency | Item ID array | Predecessor/successor relationships |
file | S3 object key | Attachments, evidence photos |
link | URL string | External references |
See the Custom Fields developer guide for the complete list and JSON schemas for each type.
Multi-tenancy
ProBeya is a shared-database, shared-schema multi-tenant system. Every tenant gets a unique subdomain:
acme.probeya.com → organizationId: "clx_org_acme"
ucb-pharma.probeya.com → organizationId: "clx_org_ucb"
How isolation works:
- The subdomain is resolved in Next.js middleware to an
organizationId - The
organizationIdis injected into the tRPC context server-side - Every database query runs inside a PostgreSQL transaction with
SET LOCAL app.current_org_id - Row-level security (RLS) policies ensure that even buggy code cannot leak cross-tenant data
// This is what happens inside every orgProcedure call:
await db.transaction(async (tx) => {
await tx.execute(
sql`SELECT set_config('app.current_org_id', ${orgId}, true)`
);
// All subsequent queries in this transaction are RLS-filtered
});
Never trust a client-supplied organizationId. The tenant identity always comes from the subdomain, resolved server-side. API keys are scoped to a single organization at creation time.
SQCDP: the five pillars
SQCDP stands for Safety, Quality, Cost, Delivery, and People. It is the standard visual management framework used in lean manufacturing and pharma operations. In ProBeya, each pillar maps to a Group on a board:
| Pillar | Color | What gets tracked |
|---|---|---|
| S — Safety | Red | Near-misses, incidents, safety observations, PPE compliance |
| Q — Quality | Blue | Deviations, CAPA, right-first-time, batch rejections |
| C — Cost | Green | OEE, waste, scrap rate, energy consumption |
| D — Delivery | Orange | On-time delivery, cycle time, schedule adherence |
| P — People | Purple | Training completion, absenteeism, mood/engagement |
KPIs are categorized by SQCDP pillar, so you can query them by category:
# Get all Quality KPIs that are breaching thresholds
curl https://acme.probeya.com/api/v1/kpis/alerts?boardId=clx_board_1&category=Q \
-H "Authorization: Bearer $PROBEYA_KEY"
Action management and escalation
Actions follow a lifecycle with tier-based escalation:
Created → Assigned → In Progress → Done → Verified
|
(overdue or blocked?)
|
T1 (Team) → T2 (Department) → T3 (Site)
When an action is escalated, it moves from the team board to the department or site board, increasing visibility and accountability. Each escalation is logged with a reason and timestamp for audit trail.
Roles and permissions
| Role | Can do | Cannot do |
|---|---|---|
| Owner | Everything | — |
| Admin | Manage members, settings, webhooks, API keys | Delete the organization |
| Member | Create/edit projects, boards, items, actions | Manage settings, API keys |
| Guest | View specific boards they are invited to | Create or edit anything |
API keys inherit the permissions of the user who created them, scoped to the organization.
What to read next
Was this page helpful?