{
  "info": {
    "name": "be-crm — Leads & Lead Verification (data door)",
    "description": "All Leads (crm.lead.*) and Lead Verification (crm.leadVerification.*) data-door actions — for local testing against be-crm on :4010 (trust-headers mode).\n\nHOW TO USE:\n1. Import this file into Postman.\n2. Run 'Verification writes / 10. intake' FIRST — its test script captures the new record id into {{seedVerificationId}}, which the assign/verify chain references. The cleanest end-to-end path is: 10 intake → 11 assign → 12 reassign → 15 verify (promotes to a Lead, captured into {{promotedLeadId}}).\n3. The read folders ('Leads reads' / 'Verification reads') capture {{leadId}} / {{verificationId}} from search results when data exists.\n\nVERIFICATION STATE MACHINE (enforced server-side, §3.1): a record flows pending → assigned → in_progress, and ends at ONE terminal outcome — either verified (via 15. verify, which promotes a Lead) OR unverified (via 17. markUnverified). 'verified' is TERMINAL: markUnverified / moveToPending / updateStatus against a verified record return 409 invalid_state_transition. So run ONE terminal action per record. To exercise markUnverified or moveToPending, run 10. intake to seed a FRESH pending record (repoints {{verificationId}}) and act on it BEFORE verifying — e.g. intake → assign → moveToPending, or intake → markUnverified. Valid: markUnverified from pending/assigned/in_progress; moveToPending from assigned/in_progress.\n\nNOTE: everything is POST except GET /health. Reads hit /query, writes hit /command (action+data in the JSON body). Commands auto-generate a fresh X-Idempotency-Key ({{$guid}}). In dev, the identity headers stand in for what the Shell BFF injects in real environments — never ship them from the frontend. Both modules are gated by the leads.manage permission.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "baseUrl", "value": "http://localhost:4010" },
    { "key": "tenant", "value": "tnt_demo" },
    { "key": "principal", "value": "prn_owner" },
    { "key": "leadId", "value": "" },
    { "key": "verificationId", "value": "" },
    { "key": "seedVerificationId", "value": "" },
    { "key": "promotedLeadId", "value": "" },
    { "key": "createdLeadId", "value": "" },
    { "key": "attachmentId", "value": "" }
  ],
  "item": [
    {
      "name": "0. Health (the only GET)",
      "request": {
        "method": "GET",
        "header": [],
        "url": { "raw": "{{baseUrl}}/health", "host": ["{{baseUrl}}"], "path": ["health"] },
        "description": "Liveness + the full list of registered actions (crm.lead.* + crm.leadVerification.*). Works in a browser."
      }
    },
    {
      "name": "Leads reads (POST /query)",
      "item": [
        {
          "name": "1. lead.stats (header stat strip)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.stats\",\n  \"variables\": {}\n}" },
            "url": { "raw": "{{baseUrl}}/query", "host": ["{{baseUrl}}"], "path": ["query"] },
            "description": "Returns { total, byStatus: { new, contacted, appointed, closed } }."
          }
        },
        {
          "name": "2. lead.search  (captures leadId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "if (j.items && j.items[0]) pm.collectionVariables.set('leadId', j.items[0].id);",
                  "pm.test('has paging meta', () => pm.expect(j.meta).to.have.property('total'));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.search\",\n  \"variables\": { \"status\": \"new\", \"page\": 1, \"perPage\": 50 }\n}" },
            "url": { "raw": "{{baseUrl}}/query", "host": ["{{baseUrl}}"], "path": ["query"] },
            "description": "Board list + search + status/priority/source/assigneeId filters. Returns { items: LeadCardDTO[], meta }."
          }
        },
        {
          "name": "3. lead.get (full detail)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "if (j.attachments && j.attachments[0]) pm.collectionVariables.set('attachmentId', j.attachments[0].id);"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.get\",\n  \"variables\": { \"id\": \"{{leadId}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/query", "host": ["{{baseUrl}}"], "path": ["query"] },
            "description": "Full record incl. phones/emails/attachments + resolved assignee/canvasser/creator names + storm/property/job/insurance groupings."
          }
        }
      ]
    },
    {
      "name": "Verification reads (POST /query)",
      "item": [
        {
          "name": "4. leadVerification.stats (stat tiles)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.stats\",\n  \"variables\": {}\n}" },
            "url": { "raw": "{{baseUrl}}/query", "host": ["{{baseUrl}}"], "path": ["query"] },
            "description": "Returns { verified, in_progress, assigned, pending, unverified }."
          }
        },
        {
          "name": "5. leadVerification.search  (captures verificationId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "if (j.items && j.items[0]) pm.collectionVariables.set('verificationId', j.items[0].id);",
                  "pm.test('has paging meta', () => pm.expect(j.meta).to.have.property('total'));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.search\",\n  \"variables\": { \"page\": 1, \"perPage\": 50 }\n}" },
            "url": { "raw": "{{baseUrl}}/query", "host": ["{{baseUrl}}"], "path": ["query"] },
            "description": "Queue table + search + status/source/assignee filters. Returns { items: VerificationRowDTO[], meta }."
          }
        },
        {
          "name": "6. leadVerification.get (detail + timeline)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.get\",\n  \"variables\": { \"id\": \"{{verificationId}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/query", "host": ["{{baseUrl}}"], "path": ["query"] }
          }
        },
        {
          "name": "7. leadVerification.activity.list",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.activity.list\",\n  \"variables\": { \"id\": \"{{verificationId}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/query", "host": ["{{baseUrl}}"], "path": ["query"] }
          }
        }
      ]
    },
    {
      "name": "Verification writes (POST /command)",
      "item": [
        {
          "name": "10. leadVerification.intake  (RUN FIRST — captures seedVerificationId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "if (j && j.id) { pm.collectionVariables.set('seedVerificationId', j.id); pm.collectionVariables.set('verificationId', j.id); }",
                  "pm.test('starts pending', () => pm.expect(j.status).to.eql('pending'));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.intake\",\n  \"variables\": {\n    \"name\": \"Kevin Hartley\",\n    \"phone\": \"(972) 413-8902\",\n    \"email\": \"kevin.hartley@example.com\",\n    \"address\": \"2814 Ravenswood Dr, Plano, TX 75023\",\n    \"source\": \"Door Knock\"\n  }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Records 'arrive' via intake (§12.11) — the ingestion path outside the two UI screens. Seeds the queue and captures {{seedVerificationId}} for the working flow below."
          }
        },
        {
          "name": "11. leadVerification.assign (pending → assigned)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.assign\",\n  \"variables\": { \"id\": \"{{seedVerificationId}}\", \"assigneeId\": \"{{principal}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Change Assignee. A pending record moves to 'assigned'; otherwise only the assignee changes."
          }
        },
        {
          "name": "12. leadVerification.reassign (→ In Progress)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.reassign\",\n  \"variables\": { \"id\": \"{{seedVerificationId}}\", \"assigneeId\": \"{{principal}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] }
          }
        },
        {
          "name": "13. leadVerification.updateStatus (generic; not 'verified')",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.updateStatus\",\n  \"variables\": { \"id\": \"{{seedVerificationId}}\", \"status\": \"in_progress\", \"subStatus\": \"Reviewing Insurance\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Generic status + sub-status set. Setting 'verified' here → 422 (use 15. verify, which promotes a Lead). Disallowed transitions → 409."
          }
        },
        {
          "name": "14. leadVerification.note.add",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.note.add\",\n  \"variables\": { \"id\": \"{{seedVerificationId}}\", \"text\": \"Ownership confirmed via county records.\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] }
          }
        },
        {
          "name": "15. leadVerification.verify  (PROMOTES to Lead — captures promotedLeadId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "if (j.lead && j.lead.id) pm.collectionVariables.set('promotedLeadId', j.lead.id);",
                  "pm.test('verification is verified', () => pm.expect(j.verification.status).to.eql('verified'));",
                  "pm.test('promoted lead is new', () => pm.expect(j.lead.status).to.eql('new'));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.verify\",\n  \"variables\": { \"id\": \"{{seedVerificationId}}\", \"notes\": \"Verified — insurance + damage confirmed.\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "The critical cross-module command: flips the record to verified and creates a new Lead (status=new) in one transaction, linking both records. Returns { verification, lead }. A second call → 409 already_verified."
          }
        },
        {
          "name": "16. leadVerification.moveToPending",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.moveToPending\",\n  \"variables\": { \"id\": \"{{verificationId}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Send an assigned/in_progress record back to pending (keeps the assignee). Point at a non-verified {{verificationId}}."
          }
        },
        {
          "name": "17. leadVerification.markUnverified",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.markUnverified\",\n  \"variables\": { \"id\": \"{{verificationId}}\", \"reason\": \"Could not confirm ownership.\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Terminal outcome. Allowed from pending/assigned/in_progress; a verified record → 409. Point {{verificationId}} at a NON-verified record (run 10. intake to seed a fresh pending one)."
          }
        }
      ]
    },
    {
      "name": "Leads writes (POST /command)",
      "item": [
        {
          "name": "20. lead.create (Full Form — captures createdLeadId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "if (j && j.id) { pm.collectionVariables.set('createdLeadId', j.id); pm.collectionVariables.set('leadId', j.id); }",
                  "pm.test('priority normalized to lowercase', () => pm.expect(j.priority).to.eql('high'));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.create\",\n  \"variables\": {\n    \"firstName\": \"John\",\n    \"lastName\": \"Martinez\",\n    \"phones\": [\n      { \"number\": \"(469) 500-1000\", \"type\": \"Mobile\", \"primary\": true },\n      { \"number\": \"(469) 500-2000\", \"type\": \"Home\" }\n    ],\n    \"emails\": [ { \"address\": \"john.martinez@example.com\", \"primary\": true } ],\n    \"property\": { \"address\": \"4821 Spring Creek Pkwy\", \"city\": \"Plano\", \"state\": \"TX\", \"zip\": \"75023\", \"type\": \"Single Family\" },\n    \"job\": { \"source\": \"Door Knock\", \"canvasserId\": \"{{principal}}\", \"leadType\": \"Insurance\", \"workType\": \"Roof Replacement\", \"tradeType\": \"Roofing\", \"urgency\": \"High\", \"notes\": \"Significant granule loss on the south slope.\" },\n    \"insurance\": { \"company\": \"State Farm\", \"claimStatus\": \"Filed\", \"claimNumber\": \"CLM-2026-1000\", \"policyNumber\": \"POL-080000\", \"adjusterName\": \"Marcus Powell\", \"adjusterPhone\": \"(972) 700-3000\" },\n    \"assignment\": { \"assigneeId\": \"{{principal}}\", \"priority\": \"High\", \"followUp\": \"2026-06-04\" }\n  }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Quick + Full form intake. Only a non-empty name (firstName OR lastName) is required (else 422 name_required). Title-case priority is normalized to lowercase; the first phone is marked primary when none is flagged. DUPLICATE GUARD: if an active (non-closed) lead in the tenant has BOTH the same normalized primary phone AND the same address, the create is REJECTED with 409 duplicate_lead (the response's duplicateOf lists the existing lead) and no lead is inserted."
          }
        },
        {
          "name": "21. lead.update (partial patch)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.update\",\n  \"variables\": { \"id\": \"{{createdLeadId}}\", \"patch\": { \"priority\": \"Medium\", \"jobNotes\": \"Adjuster meeting scheduled.\" } }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] }
          }
        },
        {
          "name": "22. lead.updateStatus (audited)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.updateStatus\",\n  \"variables\": { \"id\": \"{{createdLeadId}}\", \"status\": \"contacted\", \"note\": \"Spoke with homeowner.\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Appends a row to lead_status_history."
          }
        },
        {
          "name": "23. lead.assign (rep; null for Unassigned)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.assign\",\n  \"variables\": { \"id\": \"{{createdLeadId}}\", \"assigneeId\": \"{{principal}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] }
          }
        },
        {
          "name": "24. lead.attachment.add (site photo — captures attachmentId)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const j = pm.response.json();",
                  "if (j.attachments && j.attachments.length) pm.collectionVariables.set('attachmentId', j.attachments[j.attachments.length - 1].id);"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.attachment.add\",\n  \"variables\": { \"id\": \"{{createdLeadId}}\", \"attachment\": { \"contentRef\": \"obj/site-photo-1.jpg\", \"mimeType\": \"image/jpeg\", \"sizeBytes\": 1048576, \"filename\": \"roof-south.jpg\" } }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "contentRef is the object key returned by crm.media.presignUpload after the browser PUTs the bytes (§11.5). Files over 25 MB → 413 file_too_large."
          }
        },
        {
          "name": "25. lead.attachment.remove",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.attachment.remove\",\n  \"variables\": { \"id\": \"{{createdLeadId}}\", \"attachmentId\": \"{{attachmentId}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Run 24 first so {{attachmentId}} is captured."
          }
        }
      ]
    },
    {
      "name": "Negative / guard checks",
      "item": [
        {
          "name": "Verify an already-verified record (expect 409 already_verified)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.verify\",\n  \"variables\": { \"id\": \"{{seedVerificationId}}\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "Run 15. verify first — a second verify on the same record is rejected (no duplicate Lead)."
          }
        },
        {
          "name": "Create with no name (expect 422 name_required)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.create\",\n  \"variables\": { \"firstName\": \"\", \"lastName\": \"  \" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] }
          }
        },
        {
          "name": "Create with a bad email (expect 400 validation)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.lead.create\",\n  \"variables\": { \"firstName\": \"Jane\", \"emails\": [ { \"address\": \"not-an-email\" } ] }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] }
          }
        },
        {
          "name": "Verify via generic updateStatus (expect 422 — use verify)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer dev" },
              { "key": "X-App-ID", "value": "crm-web" },
              { "key": "X-Tenant-ID", "value": "{{tenant}}" },
              { "key": "X-Principal-ID", "value": "{{principal}}" },
              { "key": "X-Idempotency-Key", "value": "{{$guid}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"action\": \"crm.leadVerification.updateStatus\",\n  \"variables\": { \"id\": \"{{verificationId}}\", \"status\": \"verified\" }\n}" },
            "url": { "raw": "{{baseUrl}}/command", "host": ["{{baseUrl}}"], "path": ["command"] },
            "description": "The generic status setter refuses 'verified' — verification must go through crm.leadVerification.verify (which promotes a Lead)."
          }
        }
      ]
    }
  ]
}
