# Data model, identity and property design

Facts checked against HubSpot's live docs on 2026-10-17. Limits and entitlements change; confirm in the portal before quoting them.

## Start from the work, not the database

Write the operational question first: "Which active workspaces need an onboarding intervention, and which contact administers each one?" The question fixes the grain, the relationships and the fields. A table existing in the product database is not a reason to create a CRM object.

Separate four entities before mapping anything:

- Person: a contact.
- Commercial account: a company. Decide the account boundary first; subsidiaries and operating units can share one email domain.
- Product workspace or installation: often stays in the product or warehouse, with summaries on the company.
- Commercial opportunity: a deal. A deal per product event changes what the pipeline means and breaks forecasts.

Write how each source ID maps to a HubSpot Record ID, and where that crosswalk lives.

## Decision rules: property, standard object or custom object

Apply in order and stop at the first that fits.

1. One maintained fact about a record that already exists (latest refresh date, active workspace count, current plan): a property on that record.
2. A process moving through states for a commercial opportunity: a deal in a pipeline.
3. A support or service case: a ticket.
4. A sales prospecting unit worked in the sales workspace, on Sales Hub Professional or Enterprise: the leads object. A lead must have a primary association with a contact or a company.
5. Appointments, courses, listings or services: check whether HubSpot's own activatable objects fit before building a custom one. Activate them in the data model; some objects cannot be deactivated afterwards.
6. Subscriptions: check the native subscription route and what your Stripe or billing integration already writes before modelling subscriptions yourself. See [integrations.md](integrations.md).
7. A custom object, only when all of these hold:
   - A named person must act on each instance separately (different administrator, state or renewal per instance).
   - None of the above fits.
   - You can maintain a stable source key and the associations from day one.
   - The entitlement and remaining capacity are confirmed in the portal.
8. Event-grain history (every login, every API call): analytics or warehouse, with summaries synced to the CRM.

Smells that mean the model is wrong:

- Numbered property sets (`workspace_1_status`, `workspace_2_status`). Ask what happens at the fourth workspace.
- A custom "Customer" object that duplicates companies. If the difference is a lifecycle state, keep it on the company.
- A deal created to make a product event visible on a timeline.

Capacity arithmetic before any snapshot object. Example with fictional volumes: 2,000 workspaces with 20 events each per day for 30 days is 1,200,000 records; one current record per workspace is 2,000; monthly snapshots for a year add 24,000. Compare with the remaining capacity in the Limits tab.

Standard-object behaviour to preserve: bulk marketing email goes only to contacts, and pre-built reports such as deal attribution and forecasts connect only to their original objects. Keep custom-object context attached to contacts and deals through associations.

## Entitlements to confirm

Read these in the portal (More > Data Management > Data Model > Limits tab) and on the order form. As documented on 2026-10-17:

- Custom objects, Hub-based subscriptions: the knowledge base lists Enterprise subscriptions only. HubSpot's product catalog lists Smart CRM Enterprise at 10 object definitions and 1 million custom object records, with none on Starter or Professional. Other Enterprise products carry their own figures in the catalog's product pages.
- Custom objects, flexible seats-and-credits: the pricing supplement lists up to 200 object definitions in its Smart CRM table, with no Starter entry, and a single record-capacity pool per account (25,000 Starter, 250,000 Professional, 15,000,000 Enterprise; the Nordic pilot lists 5,000,000 for Enterprise). Marketing contacts count inside that pool. The supplement applies only to customers on that programme.
- Treat every number above as "confirm in the Limits tab and on the order form".
- Downgrade: when all Enterprise subscriptions are removed, HubSpot documents that custom object records and properties stay viewable but cannot be created or edited, new associations stop, custom object workflows stop and Salesforce sync of those records stops. Before committing an essential process to a custom object, record in `decisions.md` what happens to it if Enterprise is not renewed.
- Property view and edit restrictions: Enterprise only, set by Super Admins. They do not stop API writes, and users without edit access can still set the value when creating a record by hand. HubSpot says not to use them as a security measure. Below Enterprise, protect a property with a register owner and a change request, not a setting.
- Calculated properties and custom association labels: Professional or Enterprise.
- Custom pipelines: a single limit shared across all objects, shown per object in the Limits tab.
- Custom properties: the count depends on the subscription.
- Conditional property logic: Professional or Enterprise.
- Owner rotation: Sales Hub or Service Hub Professional or Enterprise, with paid seats.
- Data sync custom property mappings: a paid Data Hub subscription per the data sync article (the Stripe Data Sync app listing still names Operations Hub Starter or Professional). Without it, plan on the app's default mappings.
- Stripe Data Sync: the app listing says syncing subscriptions needs Enterprise (custom objects). Payment transactions also land in custom objects.
- Native subscriptions object: the commerce subscriptions API needs the account set up to collect payments through HubSpot payments or Stripe payment processing. A Stripe Billing setup that only syncs data does not qualify on that basis.
- Deal create and deal revenue attribution reports: Marketing Hub Enterprise. Contact create attribution: Marketing Hub or Content Hub Professional or Enterprise.
- Duplicate management: the duplicates manager needs Professional or Enterprise; custom duplicate rules and the duplicate audit log need Data Hub Professional or Enterprise.
- Restore CRM changes: Super Admin, Starter or above (Smart CRM Professional or Enterprise), 14-day window.
- HubSpot Credits: some AI and data features consume credits and pause by default when included credits run out and no capacity pack was bought. Check usage at Account & Billing > Usage & Limits > HubSpot Credits.

A custom object's internal name cannot be edited, and a deleted custom object cannot be restored. Design names once.

## Identity keys

- Contacts deduplicate on Email. Keep the internal person ID too, in a unique-value property, when people change addresses or share inboxes.
- Companies: manual creation and automatic dedupe use the primary Company domain name; imports use primary and secondary domains unless a custom unique-value property is the identifier. Companies created through the API, including third-party sync apps, are not deduplicated by domain. Any integration that creates companies needs a unique-value property holding the source account ID and a create-or-update step that searches it first.
- When companies are keyed on a source ID, turn off "Create and associate companies with contacts" (Settings > Data Management > Objects > Companies > Automation), or exclude shared domains (up to 1,000). Left on, it creates a company per unknown email domain and attaches contacts on a shared domain to one company you cannot choose. Associate contacts by source ID instead (multi-file import, integration).
- Custom objects: a unique-value property holding the source key, set before the first backfill.
- HubSpot allows up to ten custom unique-value properties per object. They are enforced on manual create, edit and import, and are not supported in forms.
- Namespace keys when two sources or environments can emit the same ID: `product:ws_001`, `billing:cus_001`.
- Keep a crosswalk: source system, source ID, HubSpot object, HubSpot Record ID, first seen, last matched.

Uniqueness protects the key. It does not prove the incoming value is current or the association is right.

## Property specification

For every property the first release reads or writes, fill one row of `assets/property-register.csv`:

- Business meaning and the entity it describes.
- Label and internal name. Labels can change; internal names cannot, and integrations use internal names.
- Field type and allowed values. For enumerations, record the internal value of each option, not just the label; forms, imports and query strings must match options exactly.
- Unit, currency or time zone.
- Source of truth and the single writer (an integration, a team, a workflow).
- Other processes allowed to propose a change, and how.
- Blank, zero and explicitly cleared: what each means and which the writer may send.
- Freshness: `*_observed_at` (source time) and `*_synced_at` (delivery time) for synced facts.
- Change control (`change_control` column). Property view and edit restrictions need Enterprise, and even then do not stop API writes or manual record creation. Record the process control instead: the register owner and how a change is requested.
- Consumers: workflows, reports, lists, integrations that read it.
- Default value, if any. Defaults are possible only for text, number and enumeration fields.

Rules:

- One meaning per property. If `plan`, `current_plan` and `stripe_plan` mean the same thing, keep one and fix the writer. If they differ, rename them to say how.
- Separate observations from judgements: `last_active_at` (product) and `account_risk_reason` (customer success) are two fields.
- Manual override pattern: `calculated_x`, `x_override` with reason and expiry, `effective_x`. The override must have an owner and an end date.
- Prefer a small maintained set. Every property is a contract someone has to keep.

## Field ownership

- Assign ownership per property, not per application. "HubSpot is the source of truth" is too broad when billing writes subscription state and product writes usage.
- Start with fields that trigger customer contact, route work, control access or appear in revenue reports: opt-outs, owner, lifecycle, deal stage, renewal date, subscription status.
- Two writers on one field need a written conflict rule (which wins, on what timestamp or version) and a test. Last-write-wins is only acceptable when arrival order equals authority.
- A custom integration writes from a permitted-field list. It never serialises the whole source object.
- Delayed data: compare the source's effective time or version before writing, so a late snapshot of a trial cannot overwrite a later paid state.
