{
  "id": "fix-3472",
  "title": "Salesforce-origin first_campaign HubSpot echo is skipped before person matching",
  "kind": "hubspot_sync",
  "optional": true,
  "targetedAcceptance": {
    "schemaVersion": "hubspot-targeted-acceptance/v1",
    "reason": "Signed-webhook regression for the production first_campaign echo incident."
  },
  "caseTimeoutMs": 300000,
  "linkedIssues": [3472],
  "provenance": {
    "businessScenario": "FIX-3472 — a HubSpot contact propertychange echo with propertyName=first_campaign and changeSource=SALESFORCE must be accepted and marked processed without entering Lead/Contact person matching. The setup creates two active NexusCRM Leads with the same email as the HubSpot contact so an unguarded email fallback would be ambiguous; the signed webhook event must still finish processed.",
    "reportedBy": "Prod incident 2026-06-29: HUBSPOT_CONTACT_AMBIGUOUS_PERSON on Salesforce-origin first_campaign HubSpot echo",
    "capturedAt": "2026-06-29"
  },
  "steps": [
    {
      "id": "create-dup-lead-a",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "POST",
      "path": "/services/data/v63.0/sobjects/Lead",
      "body": {
        "LastName": "Fx3472A{{__runId}}",
        "Company": "Fx3472",
        "Email": "fx3472-{{__runId}}@example.com",
        "HubSpot_Sync_Enabled__c": false
      },
      "expectStatusRange": [200, 299],
      "assertions": [
        { "path": "response.body.id", "exists": true }
      ]
    },
    {
      "id": "create-dup-lead-b",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "POST",
      "path": "/services/data/v63.0/sobjects/Lead",
      "body": {
        "LastName": "Fx3472B{{__runId}}",
        "Company": "Fx3472",
        "Email": "fx3472-{{__runId}}@example.com",
        "HubSpot_Sync_Enabled__c": false
      },
      "expectStatusRange": [200, 299],
      "assertions": [
        { "path": "response.body.id", "exists": true }
      ]
    },
    {
      "id": "create-contact",
      "system": "hubspot",
      "method": "POST",
      "path": "/crm/v3/objects/contacts",
      "body": {
        "properties": {
          "email": "fx3472-{{__runId}}@example.com",
          "firstname": "Fx3472",
          "lastname": "Echo{{__runId}}"
        }
      },
      "expectStatusRange": [200, 299],
      "assertions": [
        { "path": "response.body.id", "exists": true }
      ]
    },
    {
      "id": "send-salesforce-first-campaign-echo",
      "system": "connector_webhook",
      "method": "POST",
      "path": "/webhooks/hubspot-crm-connector/webhook",
      "body": [
        {
          "portalId": "{{portalId || \"51280875\"}}",
          "appId": 44166359,
          "subscriptionId": 6723583,
          "eventId": "fix-3472-{{__runId}}",
          "occurredAt": "1782734624058",
          "subscriptionType": "contact.propertyChange",
          "attemptNumber": 0,
          "objectId": "{{steps.create-contact.response.body.id}}",
          "propertyName": "first_campaign",
          "propertyValue": "701RC00000O01PmYAJ",
          "changeSource": "SALESFORCE"
        }
      ],
      "expectStatus": 202,
      "assertions": [
        { "path": "response.body", "exists": true }
      ]
    },
    {
      "id": "assert-echo-processed",
      "system": "tidb",
      "method": "GET",
      "sql": "SELECT Id, Status, ProcessedAt, COALESCE(JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.payload.propertyName')), JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.propertyName'))) AS PropertyName, COALESCE(JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.payload.changeSource')), JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.changeSource'))) AS ChangeSource, LEFT(COALESCE(LastError, ''), 200) AS LastErrorSample FROM `_System_ConnectorEvent` WHERE Namespace = 'hubspot-crm-connector' AND EventKind = 'contact.propertychange' AND SourceObjectID = '{{steps.create-contact.response.body.id}}' AND COALESCE(JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.payload.propertyName')), JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.propertyName'))) = 'first_campaign' AND COALESCE(JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.payload.changeSource')), JSON_UNQUOTE(JSON_EXTRACT(Payload, '$.changeSource'))) = 'SALESFORCE' ORDER BY CreatedDate DESC LIMIT 1",
      "expectStatus": 200,
      "poll": { "budgetMs": 180000, "intervalMs": 6000 },
      "onTimeout": "fail",
      "assertions": [
        { "path": "response.body.records", "lengthAtLeast": 1 },
        { "path": "response.body.records[0].PropertyName", "equals": "first_campaign" },
        { "path": "response.body.records[0].ChangeSource", "equals": "SALESFORCE" },
        { "path": "response.body.records[0].Status", "equals": "processed" },
        { "path": "response.body.records[0].ProcessedAt", "exists": true }
      ]
    }
  ]
}
