Company

Company is the central legal-entity relation in the Well financial graph. It represents any organization or counterparty a workspace interacts with — customers, vendors, banks, or the workspace's own operating company — and is the anchor for invoices, transactions, contacts, and connector-sourced enrichment. Every Company belongs to exactly one Workspace (workspace-scoped) and carries both human-readable profile fields and machine-normalized canonical identity fields (S0 tier) used by the invoice-payment scorer and deduplication pipeline. Companies are created by connector sync, invoice extraction, or manual entry, and enriched asynchronously via the @Enrichable decorator pipeline.

NamingValue
ObjectCompany
Resource type (JSON:API type)company
Collection / records rootcompanies
REST base/v1/companies
Entity classCompany

API operations

OperationMethod & pathStatus
ListGET /v1/companies✅ Implemented
RetrieveGET /v1/companies/{id}✅ Implemented
CreatePOST /v1/companies✅ Implemented
UpdatePATCH /v1/companies/{id}✅ Implemented
DeleteDELETE /v1/companies/{id}✅ Implemented
EnrichPOST /v1/companies/enrich✅ Implemented

Data model

Attributes

FieldTypeRequiredConstraintsAllowed valuesDescription
company_idstring, UUID, 🔒 system✅ Yesunique; generated by gen_random_uuid() at INSERTStable public identifier for the company. Used in all API requests and JSON:API payloads. Internal pk is never exposed.
namestring✅ YesDisplay name of the company. May be set from trade_name or registered_name by the enrichment pipeline; this is the primary human-readable label surfaced in the UI.
descriptionstring⚪ Nomax 250 characters; @Enrichable — populated by enrichment pipelineShort textual description of the company's activities or industry. Populated by the enrichment pipeline; editable by the user.
domainstring⚪ Nomax 500 charactersPrimary website domain (e.g. 'anthropic.com'). Used as a Tier-2 identity signal for deduplication and enrichment. User-editable or connector-populated.
localestring (ISO 639-1 enum)⚪ Nodefault 'en'; nativeEnumName: local_enumISO 639-1 two-letter codes: aa, ab, ae, af, ak, am, an, ar … +176 morePreferred display locale for this company's communications. Defaults to English ('en').
tax_id_valuestring⚪ No@Enrichable — populated by enrichment pipelineThe raw tax identifier string (e.g. '88-1234567' for a US EIN, '58879425137' for a French SIREN). Interpreted together with tax_id_type.
tax_id_typestring (TaxIdTypeEnum)⚪ No@Enrichable; nativeEnumName: tax_id_type_enumVAT, VATIN, TVA, IVA, MwSt, BTW, MOSS, OSS … +90 moreClassification of the tax identifier. Paired with tax_id_value to form a globally unambiguous tax identity. Used by the canonical_tax_id normalizer to construct the Tier-1 identity key.
trade_namestring⚪ Nomax 100 characters; CHECK: CHAR_LENGTH(trade_name) >= 1; @EnrichableDBA / trading name distinct from the legal registered_name (e.g. 'Anthropic' vs 'Anthropic PBC'). The CHECK constraint ensures it is never an empty string if set.
registered_namestring⚪ Nomax 255 characters; nullable; @EnrichableFull legal name as registered with the relevant authority (e.g. company registry, chamber of commerce). Used by canonical_legal_name normalizer.
business_typestring (BusinessTypeEnum)⚪ No@Enrichable; nativeEnumName: business_type_enumInc, Corp, LLC, LLP, LP, LLLP, PC, PLLC … +176 moreLegal entity form of the company. Covers legal structures from 40+ jurisdictions. Used by the enrichment pipeline to classify the organization type.
registered_valuestring⚪ Nomax 100 characters; @EnrichableRegistration number in the applicable registry (e.g. a company number from Delaware Division of Corporations, or a SIREN number). Combined with registry_name and registry_country to form the canonical_registry key.
registry_namestring⚪ No@EnrichableHuman-readable name of the registry where the company is registered (e.g. 'Delaware Division of Corporations', 'RCS Paris'). Used in canonical_registry construction.
registry_countrystring (CountryCodeEnum, ISO 3166-1 alpha-2)⚪ No@Enrichable; nativeEnumName: country_code_enumISO 3166-1 alpha-2 country codes (e.g. US, FR, DE, GB, ...)Country of the registry authority. Forms the geographic prefix of canonical_registry and enables registry-system-aware deduplication.
canonical_tax_idstring⚪ Nomax 64 characters; partial index on (workspace_pk, canonical_tax_id) WHERE deleted_at IS NULL; NULL until S2 backfill runsCountry-prefixed, normalized tax identifier. Format: <ISO2><stripped_digits> — e.g. 'FR58879425137'. Tier-1 identity signal used by the invoice-payment scorer and deduplication pipeline (S0/S3+). Populated exclusively by the S1 normalization module and S2 backfill.
canonical_registrystring⚪ Nomax 128 characters; partial index on (workspace_pk, canonical_registry) WHERE deleted_at IS NULL; NULL until S2 backfill runsCountry + registry name + registered_value concatenated. Format: <ISO2>-<REGISTRY>-<VALUE> — e.g. 'FR-RCS-PARIS-879425137'. Tier-1 identity signal for registry-system-aware dedup. Populated by S1 normalization module.
domain_normalizedstring⚪ Nomax 255 characters; partial index on (workspace_pk, domain_normalized) WHERE deleted_at IS NULL; NULL until S2 backfill runsRoot domain, lowercase, no www, no path. Derived from company.domain or the first email domain. Tier-2 identity signal for domain-level deduplication. Populated by S1 normalization module.
email_domainsstring[]⚪ NoPostgreSQL text[] array type; GIN index on email_domains WHERE deleted_at IS NULL; NULL until S2 backfill runsArray of normalized email domains derived from the company_emails pivot table. GIN-indexed for set-containment queries (@>, &&). Tier-2 identity signal for email-domain overlap matching.
canonical_legal_namestring⚪ Nomax 255 characters; partial index on (workspace_pk, canonical_legal_name, registry_country) WHERE deleted_at IS NULL; NULL until S2 backfill runsPunctuation-stripped, case-folded, legal-suffix-dropped company name. Example: 'Anthropic PBC' → 'anthropic'. Tier-3 fuzzy signal used only when Tier-1/2 signals are absent. Populated by S1 normalization module.
identity_completenessnumber (numeric(4,3))⚪ Norange 0.000–1.000; NULL until first S1/S2 computationCompleteness score representing the fraction of canonical identity fields (canonical_tax_id, canonical_registry, domain_normalized, email_domains, canonical_legal_name) that are non-NULL. Recomputed by S1 normalization on every write and by S2 backfill. Used to prioritize enrichment and dedup confidence.
created_atdatetime, 🔒 system✅ Yesset by onCreate lifecycle hook; never writable by API consumerTimestamp when this company record was first created. Set automatically at INSERT.
updated_atdatetime, 🔒 system⚪ Noset by onCreate and onUpdate lifecycle hooksTimestamp of the most recent modification to this company record. Auto-updated on every write.
deleted_atdatetime⚪ Nonullable; NULL = active; non-NULL = soft-deleted. All queries must filter deleted_at IS NULL.Soft-delete timestamp. When set, the company is hidden from all standard queries. Hard deletes are not used for this entity. All partial indexes are scoped to WHERE deleted_at IS NULL.

Relationships

NameTypeRequiredDescription
workspaceto-one (workspace)⚪ NoThe Workspace this company belongs to. Provides tenant isolation — all company queries filter by workspace_pk. Nullable to support global/catalog companies in future patterns, but in practice every workspace-scoped company has this set. The partial indexes include workspace_pk in every WHERE clause.
source_workspace_connectorto-one (workspace_connector)⚪ NoThe WorkspaceConnector instance (connector sync) that originally created this company row. NULL means the row was created manually, by invoice extraction, or by enrichment fallback. Used by the counterparty-bank discovery skill to distinguish 'connector-confirmed' from 'inferred' banks.
relationsto-many (company_relation)CompanyRelation pivot rows where this company is the source. Represents inter-company relationships (e.g. parent/subsidiary, affiliated entity). Maps to CompanyRelation.source_company.
peopleto-many (company_person)CompanyPerson junction rows linking this company to Person entities. Carries role/title metadata. Maps to CompanyPerson.company.
social_linksto-many (company_web_link)@Enrichable. Web link entries (LinkedIn, Twitter/X, website, etc.) attached to the company. Populated by the enrichment pipeline.
locationsto-many (company_location)@Enrichable. Physical address/location entries for the company (HQ, branch offices, etc.). Populated by the enrichment pipeline.
emailsto-many (company_email)@Enrichable. CompanyEmail pivot rows carrying email address, is_primary, is_verify, and label metadata. Used as Tier-2 identity signal via email_domains array. Populated by enrichment pipeline and connector sync.
phonesto-many (company_phone)@Enrichable. CompanyPhone pivot rows with phone number, is_primary, is_verify, and label. Populated by enrichment pipeline.
categoriesto-many (company_category)CompanyCategory pivot rows classifying the company into industry/sector taxonomy. Not @Enrichable — assigned programmatically or by users.
mediato-many (company_media)@Enrichable. CompanyMedia entries (logos, cover images) attached to this company. The logo is the primary visual identifier used in composite cells on the records table.
company_financialto-one (company_financial)⚪ NoOne-to-one CompanyFinancial record carrying aggregated financial summary data for this company (e.g. balance, open invoices). Owned by the CompanyFinancial side. Nullable — only present once financial data has been computed.
company_workspace_connectorsto-many (company_workspace_connector)CompanyWorkspaceConnector junction rows recording which connector sync instances have touched this company, including the direction (input/output). Used to distinguish connector-confirmed banks from inferred ones in the counterparty-bank discovery skill, and to render the composite_sourced_from / composite_routed_to virtual fields on the records table.

System-computed

  • company_id is generated by PostgreSQL gen_random_uuid() at INSERT via defaultRaw. It is immutable after creation and used as the public JSON:API id.
  • created_at is set by an onCreate MikroORM lifecycle hook to new Date() at INSERT time. Never updated thereafter.
  • updated_at is set by both onCreate and onUpdate lifecycle hooks to new Date(). Reflects the most recent flush of any column on the entity.
  • deleted_at is NULL on active records. Set to a non-null timestamp to soft-delete. All active queries filter WHERE deleted_at IS NULL. Hard deletes are not used for this entity.
  • canonical_tax_id is computed by the S1 normalisation module from tax_id_value and registry_country. Format: <ISO2><stripped_digits>. Written only by the normalizer — never by connector sync or user PATCH. NULL until the S2 backfill runs.
  • canonical_registry is computed by S1 from registry_country + registry_name + registered_value. Format: <ISO2>-<REGISTRY>-<VALUE>. NULL until S2 backfill runs.
  • domain_normalized is derived from company.domain or the first email domain: root domain, lowercase, no www, no trailing path. NULL until S2 backfill runs.
  • email_domains is a text[] array populated by the S1 normalizer by aggregating all CompanyEmail rows for this company and extracting their domain parts. GIN-indexed. Kept in sync on CompanyEmail writes.
  • canonical_legal_name is produced by stripping punctuation, case-folding, and dropping common legal suffixes from registered_name or trade_name. E.g. 'Anthropic PBC' → 'anthropic'. NULL until S2 backfill runs.
  • identity_completeness is a numeric(4,3) score in [0.000, 1.000] computed by S1 as the fraction of {canonical_tax_id, canonical_registry, domain_normalized, email_domains, canonical_legal_name} that are non-NULL. Recomputed on every S1 write and by S2 backfill. NULL until first computation.
  • source_workspace_connector (FK workspace_connector_pk) records which connector sync originally created this company row. NULL means the row was created by invoice extraction, manual entry, or enrichment fallback — not by a connector. Presence in company_workspace_connectors.direction='input' is the 'connector-confirmed' signal used by the counterparty-bank discovery scorer.
  • Fields decorated with @Enrichable (description, tax_id_value, tax_id_type, trade_name, registered_name, business_type, registered_value, registry_name, registry_country, social_links, locations, emails, phones, media) are eligible for population by the asynchronous enrichment pipeline (Cloud Tasks). The decorator marks the field for inclusion in the enrichment delta computation.
  • locale defaults to LocalEnum.EN ('en') via the ORM default. Represents ISO 639-1 locale for the company's communications context.

Example

{
  "data": {
    "type": "company",
    "id": "3fa1c2d4-87ae-4b10-a9f3-ec5d1234abcd",
    "attributes": {
      "company_id": "3fa1c2d4-87ae-4b10-a9f3-ec5d1234abcd",
      "name": "Anthropic PBC",
      "description": "AI safety company building reliable, interpretable AI systems.",
      "domain": "anthropic.com",
      "locale": "en",
      "tax_id_value": "88-1234567",
      "tax_id_type": "EIN",
      "trade_name": "Anthropic",
      "registered_name": "Anthropic PBC",
      "business_type": "PBC",
      "registered_value": "7543821",
      "registry_name": "Delaware Division of Corporations",
      "registry_country": "US",
      "canonical_tax_id": "US881234567",
      "canonical_registry": "US-DELAWARE-7543821",
      "domain_normalized": "anthropic.com",
      "email_domains": ["anthropic.com"],
      "canonical_legal_name": "anthropic",
      "identity_completeness": 1.000,
      "created_at": "2025-03-15T10:22:00.000Z",
      "updated_at": "2026-01-04T08:11:45.000Z",
      "deleted_at": null
    },
    "relationships": {
      "workspace": {
        "data": { "type": "workspace", "id": "a1b2c3d4-0000-4000-8000-000000000001" }
      },
      "source_workspace_connector": {
        "data": { "type": "workspace_connector", "id": "c9d8e7f6-1111-4111-8111-000000000002" }
      },
      "emails": {
        "data": [{ "type": "company_email", "id": "e1f2a3b4-2222-4222-8222-000000000003" }]
      },
      "phones": { "data": [] },
      "locations": {
        "data": [{ "type": "company_location", "id": "d5e6f7a8-3333-4333-8333-000000000004" }]
      },
      "social_links": {
        "data": [{ "type": "company_web_link", "id": "b9c0d1e2-4444-4444-8444-000000000005" }]
      },
      "people": {
        "data": [{ "type": "company_person", "id": "f3a4b5c6-5555-4555-8555-000000000006" }]
      },
      "categories": { "data": [] },
      "media": {
        "data": [{ "type": "company_media", "id": "a7b8c9d0-6666-4666-8666-000000000007" }]
      },
      "company_financial": {
        "data": { "type": "company_financial", "id": "e1e2e3e4-7777-4777-8777-000000000008" }
      },
      "relations": { "data": [] }
    }
  }
}
Source: apps/api/src/database/entities/Company.ts · domain: financial-graph · tier: Main