# HubSpot implementation brief (template)
# A planning document, not an import file or executable HubSpot configuration.
# Replace every <...> value. Leave unresolved items visible under open_decisions.

release: <name, e.g. CRM foundation v1>
portal: <portal name as recorded in martech-context/stack.md; never a credential>
business_owner: <role or person>
hubspot_admin: <role or person>
integration_owner: <role or person>
privacy_owner: <role or person>
target_launch: <date or "after all gates pass">

first_release_workflows:
  - id: wf1
    statement: <a named person needs to ...>
    trigger: <event that starts it>
    population: <who is eligible>
    exclusions: <existing customers, partners, test records; a running import or backfill batch by its batch tag, never by Record Source>
    owner: <who acts>
    required_data: [<property internal names>]
    expected_response: <what the owner does and by when>
    failure_path: <where it goes if anything is missing>
    must_not_happen: <for example, existing customer enters a new-prospect sequence>

capability_register:
  pricing_programme: <hub-based seats | flexible seats-and-credits | other, from the order form>
  checked_on: <YYYY-MM-DD>
  checked_where: <portal Limits tab, order form, Account & Billing>
  items:
    - outcome: <what the release needs>
      feature: <HubSpot feature>
      requires: <hub, edition, seat type>
      confirmed: <yes | no>
      fallback: <simpler process and what it cannot do>

account_settings:
  time_zone: <zone>
  company_currency: <ISO 4217 code>
  additional_currencies: [<codes or none>]
  external_tracking_domains: [<domain>]
  standard_sandbox: <available | not entitled>
  company_auto_association: <off | on with excluded shared domains; off when companies are keyed on a source ID or Salesforce is connected>
  subscription_types: [<types agreed with the privacy owner>]
  sending_domain: <domain and authentication status before the first marketing send>
  permission_sets: <sets per role; one allowed and one denied record tested per set>
  super_admins: [<roles>]
  seat_map_file: <path to seat map>
  integration_credentials:
    - integration: <name>
      credential_type: <service key | app OAuth | legacy private app | connector OAuth>
      holder: <role>
      api_version: <date version such as 2026-09, or legacy path>

records:
  contact: <what one contact represents>
  company: <what one company represents; account boundary rule>
  deal: <what one deal represents>
  other_objects:
    - name: <leads | tickets | custom object name>
      reason: <decision rule that justified it>
  kept_outside_hubspot:
    - entity: <workspace, event history>
      where: <warehouse, product database>
      summaries_synced: [<property internal names>]

identity:
  contact_key: <email plus unique person ID property>
  company_key: <unique account ID property; never domain alone when integrations create companies>
  custom_object_keys: [<object: property>]
  contact_company_association: <how contacts get their company (multi-file import, integration, form field)>
  company_summary_rule: <how several subscriptions or workspaces roll up to one company value>
  crosswalk_location: <where source ID to Record ID mapping lives; updated after every merge>
  unresolved_match: <review queue owner; no speculative merge>

property_register_file: <path to property-register.csv>

stages:
  lifecycle_settings:
    set_on_create: <stage or off>
    sync_to_primary_company: <on | off>
    set_on_deal_created: <on | off>
    set_on_deal_won: <on | off>
    set_on_lead_associated: <on | off>
  follow_up_state: <lead status property | leads object>
  correction_path: <workflow name that clears then sets, and who may enrol>
  pipelines:
    - name: <pipeline>
      stages_file: <path or inline list with evidence, required properties, probability, internal ID>
      missing_required_view: <saved view listing deals with required stage properties empty>

import_plan:
  order: <companies, then contacts with their company key, then opt-outs, then any marketing update>
  files:
    - name: <file>
      object: <object, or objects in a multi-file import with the common column>
      mode: <create and update | create only | update existing only, per object>
      identifier: <Record ID | email | unique property>
      marketing_status: <non-marketing | marketing, with reason>
  import_tag_property: <property that marks each batch>
  before_export: <path>
  restore_window: <date each import's 14-day Restore CRM changes window closes; named Super Admin>
  workflows_paused: [<workflow names>]
  rehearsal_cases_file: <path to import-rehearsal-cases.csv>
  reconciliation: <created + updated + rejected equals rows per file; replay creates zero records>
  opt_out_check: <opted-out count in source, in HubSpot, and how many are marketing>
  marketing_contacts:
    tier: <number>
    current: <number>
    expected_after_import: <number>
    next_update_date: <date>
    maximum_cap: <number or none>

forms:
  - name: <form>
    purpose: <demo | newsletter | support>
    marketing_switch: <on | off>
    marketing_note: <with the switch on, existing non-marketing submitters become marketing; expected effect on tier and cap>
    subscription_type: <type, or none>
    default_lifecycle: <stage or none>
    hidden_fields: [<internal names>]
    existing_customer_rule: <what happens>

integrations:
  - name: <integration>
    direction: <one-way source to HubSpot | two-way>
    match_key: <property>
    create_if_no_match: <true | false>
    permitted_fields: [<internal names>]
    blank_input: <preserve | clear>
    stale_update: <rule>
    failure_queue_owner: <role>

workflows:
  - name: <workflow>
    serves: <wf id>
    enrol_existing_on_activation: <false unless approved backfill>
    re_enrolment: <off | triggers>
    overwrite_existing_owner: <true | false>
    fallback_owner: <role>
    credit_consuming_steps: [<step or none>]

reports:
  - name: <report>
    unit: <contacts | companies | deals | requests>
    request_key: <for request counts, what identifies one request>
    timestamp: <property>
    exclusions: <test and internal records, same rule as analytics>
    reconciles_to: <record list or query>

launch_evidence:
  - Capability register has no unconfirmed first-release item
  - Rehearsal import reconciled by record and association
  - Opt-out count in HubSpot equals the source
  - Marketing contact count reviewed against tier and update date, after the import and after each form went live
  - Each integration traced one change and survived a replayed delivery
  - Routing and exception cases passed with outbound limited to test recipients
  - Dashboard numbers reconcile to record IDs
  - Users completed their assigned workflow

observation:
  length: <30 days by default, extended while defects stay open>
  owner: <role>
  daily: [<fallback queue, failed syncs, unexpected enrolments, marketing contact count, deals missing required stage properties, report discrepancies>]
  restore_window_closes: <per import, integration and workflow>
  change_control: <new workflows and properties go through the register and controlled-record tests>
  training_records: <how they are labelled and excluded>
  historical_activity: <migrated or not, and what users will not see>

recovery:
  pause_point: <how new workflow enrolment is paused>
  review_queues: [<queue names>]
  evidence: [before-state export, import logs, workflow history, saved CLI output, CLI history for destructive operations]
  restore: <Restore CRM changes filters to use inside 14 days, and who runs it>

open_decisions:
  - decision: <what is undecided>
    owner: <who decides>
    blocks: <which gate>
