---
id: WF-PFM-OPS-014
title: Turn a HubSpot notification into queued company reads
product_area: platform
module: operations
revision: 1
status: draft
design_intent: confirmed
implementation_evidence: partially-observed
inference: []
origin: >-
  services-platform:docs/proposals/application-access-and-entitlements.md#4.3 (built and accepted by Andrew
  2026-10-15)
trigger: HubSpot posts a signed delivery describing company or contract changes.
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
implements:
  - CAP-PFM-OPS-012
moves: []
crosses:
  - SVC-PFM-OPS-licensing
idempotency: >-
  Within a delivery, company requests are deduplicated. Repeat or reordered deliveries request complete reads
  instead of applying deltas, with concurrent-read ordering still subject to the company-sync limit.
concurrency: >-
  Deliveries add queue work consumed under bounded concurrency; they do not serialize all snapshots for a
  company.
hops:
  - 'n': 1
    actor_or_service: Licensing API (public)
    action: Require a configured webhook signing secret before inspecting delivery content.
    anchor: 'services-platform:modules/licensing/aws/api/licensing.routes.ts:receiveHubspotWebhook'
    seam: sync
    on_failure: Reject unverifiable configuration as service unavailable.
    left_inconsistent: No company change is applied by the rejected delivery.
    observer: Warning log line is the observation surface.
    provenance: observed
    source: 'services-platform:modules/licensing/aws/api/licensing.routes.ts:receiveHubspotWebhook'
  - 'n': 2
    actor_or_service: Licensing API (public)
    action: 'Verify request bytes, address and timestamp against the provider signature.'
    anchor: 'services-platform:modules/licensing/hubspot/webhook-signature.ts'
    seam: sync
    on_failure: Return an empty unauthorized refusal and log verification detail.
    left_inconsistent: No licensing change is made by invalid authentication.
    observer: Log is the observation surface.
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/e2e/hubspot-licensing.e2e.spec.js#refuses a delivery signed
      for a different URI, so a signature cannot be replayed at another route
  - 'n': 3
    actor_or_service: Licensing service
    action: Map supported events to companies using stored contract ownership where needed.
    anchor: 'services-platform:modules/licensing/services/sync/accept-webhook-delivery.ts:acceptWebhookDelivery'
    seam: sync
    on_failure: Count unknown contracts and unsupported events for later reconciliation.
    left_inconsistent: A contract absent from the copy may not trigger immediate company work.
    observer: Delivery result and log is the observation surface.
    provenance: test
    source: >-
      services-platform:modules/licensing/tests/integration/services/sync/accept-webhook-delivery.integration.spec.js#reports
      an event about a contract it has never seen as unresolved, for the nightly pass to pick up
  - 'n': 4
    actor_or_service: Licensing service
    action: Publish one company-read message per deduplicated company directly to the queue.
    anchor: 'services-platform:modules/licensing/services/sync/sync-queue.ts:publishLicenceSyncItems'
    seam: queue
    on_failure: Report failed publication in the acknowledged delivery result.
    left_inconsistent: An unpublished change waits for a later successful reconciliation trigger.
    observer: Delivery result is the observation surface.
    provenance: observed
    source: 'services-platform:modules/licensing/services/sync/sync-queue.ts:publishLicenceSyncItems'
branches: []
waits: []
evidence:
  - >-
    services-platform at ad3f6a6: modules/licensing (aws/api/licensing.routes.ts:receiveHubspotWebhook,
    hubspot/webhook-signature.ts, hubspot/webhook-events.ts, services/sync/accept-webhook-delivery.ts,
    services/sync/sync-queue.ts); docs/licensing.md §12; docs/queued-work.md §1, §2.
  - >-
    codex examination 2026-10-23: checked the pinned source anchors, planning references and enforcement
    boundaries; unresolved limits are recorded in module findings.
controls: [TC-AVL-001, TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CHG-005, TC-CHG-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-003, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Turn a HubSpot notification into queued company reads

## What It Does

The webhook treats an authenticated notification as a request to reread a company. It verifies the original request before interpreting supported events, then publishes company work. Notifications do not change coverage directly.

## The Flow

```mermaid
flowchart TD
  H0["Require a configured webhook signing secret before inspecting delivery content."]
  H1["Verify request bytes, address and timestamp against the provider signature."]
  H0 --> H1
  H2["Map supported events to companies using stored contract ownership where needed."]
  H1 --> H2
  H3["Publish one company-read message per deduplicated company directly to the queue."]
  H2 --> H3
```

## When Something Fails

Absent signing configuration is refused, and invalid signatures disclose no diagnostic detail to the caller. Unknown contract ownership and failed publication rely on later reconciliation. Failed publication still returns an acknowledged delivery result. Direct queue publication lacks the durable queue-row dispatcher required by the cited standard.

## Open Design Questions

The queued-work departure needs explicit resolution or a recorded exception; it is not silently accepted by this design.
