CompanyPerson
CompanyPerson is a soft-deletable pivot entity that links a Company to a People record within the financial graph, capturing the nature of the relationship between the two (contact, employee, owner, or other). It is written exclusively by connector sync and enrichment pipelines โ no public resource PATCH route exists. Because Company is workspace-scoped, every CompanyPerson row is implicitly tenant-isolated through its company foreign key. The entity carries no UUID public identifier of its own; callers reference it only through its parent associations.
| Naming | Value |
|---|---|
| Object | CompanyPerson |
Resource type (JSON:API type) | company_person |
| Collection / records root | โ (not a records root) |
| REST base | /v1/company-people |
| Entity class | CompanyPerson |
Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.
API operations
| Operation | Method & path | Status |
|---|---|---|
| List | GET /v1/company-people | ๐ก Planned |
| Retrieve | GET /v1/company-people/{id} | ๐ก Planned |
| Create | POST /v1/company-people | ๐ก Planned |
| Update | PATCH /v1/company-people/{id} | ๐ก Planned |
| Delete | DELETE /v1/company-people/{id} | ๐ก Planned |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|---|---|---|---|---|
| relationship_type | enum (relationship_type_enum โ PostgreSQL native enum) | โ Yes | DEFAULT 'other'; native enum stored in core_api schema; NOT NULL | contact | employee | owner | other | Describes how the person is related to the company. Stored as the enum value string ('contact', 'employee', 'owner', 'other') in the database via the native PostgreSQL enum relationship_type_enum. Defaults to 'other' when the connector does not provide a more specific classification. |
| created_at | ๐ system โ timestamptz | โ Yes | NOT NULL; set by MikroORM onCreate hook; never updated | โ | Timestamp recording when this company-person link was first persisted. Set automatically on insertion; never modified. |
| deleted_at | timestamptz | null | โช No | Nullable; NULL means active; non-NULL means soft-deleted | โ | Soft-delete timestamp. All queries must include a WHERE deleted_at IS NULL predicate. The partial indexes idx_company_people_person and idx_company_people_company both filter on deleted_at IS NULL, so deleted rows are excluded from indexed lookups automatically. |
Relationships
| Name | Type | Required | Description |
|---|---|---|---|
| company | to-one (ManyToOne โ Company) | Yes โ NOT NULL FK | The Company this person is associated with. Foreign key company_pk references core_api.companies.pk ON UPDATE CASCADE. A partial index idx_company_people_company on company_pk WHERE deleted_at IS NULL ensures efficient lookups of all active contacts/employees for a given company. |
| person | to-one (ManyToOne โ People) | Yes โ NOT NULL FK | The People record this company link targets. Foreign key person_pk references core_api.peoples.pk ON UPDATE CASCADE. A partial index idx_company_people_person on person_pk WHERE deleted_at IS NULL enables fast enumeration of all active company memberships for a given person. |
System-computed
- created_at โ set by MikroORM onCreate: () => new Date() hook; written once on insertion, never updated by the application layer.
- deleted_at โ set to the current timestamp by the soft-delete logic; never hard-deleted in normal operation. Cascades from parent soft-delete (Company or People) are handled at the service layer, not via database CASCADE.
- company_pk (FK) โ resolved from the company relation by MikroORM before flush; not exposed as a standalone API attribute.
- person_pk (FK) โ resolved from the person relation by MikroORM before flush; not exposed as a standalone API attribute.
- pk โ auto-increment serial primary key; internal join key only, never exposed in API responses.
- Partial indexes โ idx_company_people_company and idx_company_people_person are defined on the entity class via @Index({ expression: 'CREATE INDEX ... WHERE deleted_at IS NULL' }) decorators; they are owned by the migration layer, not runtime logic.
- No updated_at column โ CompanyPerson is insert-and-soft-delete only; there is no updated_at hook or column on this entity.
Example
{
"data": {
"type": "company_person",
"attributes": {
"relationship_type": "employee",
"created_at": "2025-09-14T10:22:00.000Z",
"deleted_at": null
},
"relationships": {
"company": {
"data": { "type": "company", "id": "b3f2a1c4-8d47-4e2b-9f35-1a2b3c4d5e6f" }
},
"person": {
"data": { "type": "people", "id": "e7a0f912-3b56-41dc-a8f1-0c1d2e3f4a5b" }
}
}
}
}apps/api/src/database/entities/CompanyPerson.ts ยท domain: financial-graph ยท tier: Supporting