{
  "id": "k-camp-14",
  "title": "Inbound Contact then Set Campaign on the same email keeps exactly one Lead and adds its CampaignMember",
  "kind": "hubspot_sync",
  "caseTimeoutMs": 720000,
  "linkedIssues": [
    2863
  ],
  "provenance": {
    "businessScenario": "hubspot 端新建一个 Contact，入站 NexusCRM，确认正常落库；然后以 set campaign 的方式，基于同一个邮箱，把这个 Contact 加入到某一个 Campaign，确认在 NexusCRM 中不会额外新建一个 lead，但 lead 关联的 campaign 中有对应的参会记录。\n\n[clarified] 落库=按 HubSpot_Id__c 物化出恰好 1 个 Lead（先于 Set Campaign 断言，保证时序是先入站后动作）；参会记录=该 Campaign 下 CampaignMember COUNT=1 且 LeadId 指向同一个 Lead；不额外建 Lead=动作收敛后按邮箱与 HubSpot_Id__c 计数仍为 1。",
    "reportedBy": "Frecklesdsn@null.net (via Claude session)",
    "capturedAt": "2026-07-13",
    "selfContained": "Fresh Campaign per run (staging restarts wipe data) and the real 'Set NexusCRM Campaign' workflow Action replayed via a signed connector_action POST to /webhooks/hubspot-crm-connector/workflow-action — same pattern as k-camp-05/k-camp-13, so no HubSpot list/workflow dependency and no crm.lists scope. The single contact is created via batch-create count=1 because connector_action fans out over a forEach array. Ordering note: this is the ordered complement of k-camp-09 (out-of-order convergence) and covers the 'already-synced Lead + Set Campaign does not duplicate' behavior that list-dependent k-camp-03 leaves optional."
  },
  "steps": [
    {
      "id": "s0-create-campaign",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "POST",
      "path": "/services/data/v63.0/sobjects/Campaign",
      "body": {
        "Name": "IT K-CAMP-14 {{__runId}}",
        "IsActive": true
      },
      "expectStatusRange": [
        200,
        299
      ],
      "assertions": [
        {
          "path": "response.body.id",
          "exists": true
        }
      ]
    },
    {
      "id": "s1-create-contact",
      "system": "hubspot",
      "method": "POST",
      "path": "/crm/v3/objects/contacts/batch/create",
      "generate": {
        "count": 1,
        "into": "inputs",
        "itemTemplate": {
          "properties": {
            "email": "kcamp14-{{__runId}}-{{i}}@example.com",
            "firstname": "KCamp14",
            "lastname": "InboundFirst-{{i}}",
            "company": "IT InboundFirst Co",
            "country_picklist__sfdc_": "Japan",
            "lifecyclestage": "lead"
          }
        }
      },
      "expectStatusRange": [
        200,
        299
      ],
      "assertions": [
        {
          "path": "response.body.results",
          "lengthAtLeast": 1
        }
      ]
    },
    {
      "id": "s2-lead-materialized",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT COUNT() FROM Lead WHERE HubSpot_Id__c = '{{steps.s1-create-contact.response.body.results[0].id}}'",
      "expectStatus": 200,
      "poll": {
        "budgetMs": 180000,
        "intervalMs": 10000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.totalSize",
          "numericEquals": 1
        }
      ]
    },
    {
      "id": "s3-set-campaign",
      "system": "connector_action",
      "path": "/webhooks/hubspot-crm-connector/workflow-action",
      "forEach": "steps.s1-create-contact.response.body.results",
      "contactIdField": "id",
      "emailField": "properties.email",
      "campaignId": "{{steps.s0-create-campaign.response.body.id}}",
      "status": "Sent",
      "concurrency": 1,
      "assertions": [
        {
          "path": "response.body.failedCount",
          "numericEquals": 0
        },
        {
          "path": "response.body.acceptedCount",
          "numericEquals": 1
        }
      ]
    },
    {
      "id": "s4-campaignmember-count",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT COUNT() FROM CampaignMember WHERE CampaignId='{{steps.s0-create-campaign.response.body.id}}'",
      "expectStatus": 200,
      "poll": {
        "budgetMs": 180000,
        "intervalMs": 10000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.totalSize",
          "numericEquals": 1
        }
      ]
    },
    {
      "id": "s5-member-links-same-lead",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT COUNT() FROM Lead WHERE Id IN (SELECT LeadId FROM CampaignMember WHERE CampaignId='{{steps.s0-create-campaign.response.body.id}}') AND HubSpot_Id__c = '{{steps.s1-create-contact.response.body.results[0].id}}'",
      "expectStatus": 200,
      "poll": {
        "budgetMs": 60000,
        "intervalMs": 5000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.totalSize",
          "numericEquals": 1
        }
      ]
    },
    {
      "id": "s6-no-extra-lead",
      "system": "nexuscrm",
      "authRole": "admin",
      "method": "GET",
      "path": "/services/data/v63.0/query?q=SELECT COUNT() FROM Lead WHERE Email = 'kcamp14-{{__runId}}-1@example.com' OR HubSpot_Id__c = '{{steps.s1-create-contact.response.body.results[0].id}}'",
      "expectStatus": 200,
      "poll": {
        "budgetMs": 60000,
        "intervalMs": 5000
      },
      "onTimeout": "fail",
      "assertions": [
        {
          "path": "response.body.totalSize",
          "numericEquals": 1
        }
      ]
    }
  ]
}
