###############################################################################
# Leads & Lead Verification — data-door requests (VS Code "REST Client" / JetBrains HTTP).
# Install the REST Client extension, then click "Send Request" above each block.
# Requests are chained: run the "@name" reads/writes in order so the id variables
# below resolve. The cleanest end-to-end path is:
#   #10 intake → #11 assign → #12 reassign → #15 verify (promotes to a Lead).
# Dev trust-headers mode — change @principal to test different callers.
###############################################################################

@baseUrl = http://localhost:4010
@tenant = tnt_demo
@principal = prn_owner

# Shared headers are repeated per request (the format has no header includes).

### Health — see every registered action (crm.lead.* + crm.leadVerification.*)
GET {{baseUrl}}/health

# ─────────────────────────────── LEADS: READS (POST /query) ─────────────────────

### 1. Lead stats  (header stat strip: total + byStatus)
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.lead.stats", "variables": {} }

### 2. Lead search  (RUN THIS — provides @leadId). Supports query/status/priority/source/assigneeId/page/perPage.
# @name leadSearch
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.lead.search", "variables": { "status": "new", "page": 1, "perPage": 50 } }

@leadId = {{leadSearch.response.body.$.items[0].id}}

### 3. Lead get (full detail incl. phones/emails/attachments + resolved names)
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.lead.get", "variables": { "id": "{{leadId}}" } }

# ────────────────────── LEAD VERIFICATION: READS (POST /query) ──────────────────

### 4. Verification stats  (the clickable stat tiles)
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.leadVerification.stats", "variables": {} }

### 5. Verification search  (RUN THIS — provides @verificationId)
# @name vSearch
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.leadVerification.search", "variables": { "page": 1, "perPage": 50 } }

@verificationId = {{vSearch.response.body.$.items[0].id}}

### 6. Verification get (detail + activity timeline)
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.leadVerification.get", "variables": { "id": "{{verificationId}}" } }

### 7. Verification activity list (also embedded in .get)
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.leadVerification.activity.list", "variables": { "id": "{{verificationId}}" } }

# ─────────────── VERIFICATION: WRITES (POST /command + unique key) ───────────────
# Commands require a UNIQUE X-Idempotency-Key each time (reuse ⇒ 409).
# Records "arrive" via intake (§12.11) — start the working flow at #10.

### 10. Intake — seed a verification record  (RUN — provides @seedVerificationId)
# @name intake
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-intake-001
Content-Type: application/json

{ "action": "crm.leadVerification.intake", "variables": {
  "name": "Kevin Hartley",
  "phone": "(972) 413-8902",
  "email": "kevin.hartley@example.com",
  "address": "2814 Ravenswood Dr, Plano, TX 75023",
  "source": "Door Knock"
} }

@seedVerificationId = {{intake.response.body.$.id}}

### 11. Assign (Change Assignee) — pending → assigned
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-assign-001
Content-Type: application/json

{ "action": "crm.leadVerification.assign", "variables": { "id": "{{seedVerificationId}}", "assigneeId": "{{principal}}" } }

### 12. Reassign → In Progress
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-reassign-001
Content-Type: application/json

{ "action": "crm.leadVerification.reassign", "variables": { "id": "{{seedVerificationId}}", "assigneeId": "{{principal}}" } }

### 13. Update status + sub-status (generic; cannot set 'verified' here — use #15)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-updatestatus-001
Content-Type: application/json

{ "action": "crm.leadVerification.updateStatus", "variables": { "id": "{{seedVerificationId}}", "status": "in_progress", "subStatus": "Reviewing Insurance" } }

### 14. Add a verification note (appends + timeline entry)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-note-001
Content-Type: application/json

{ "action": "crm.leadVerification.note.add", "variables": { "id": "{{seedVerificationId}}", "text": "Ownership confirmed via county records." } }

### 15. Verify — PROMOTES to a new Lead (status = new). Returns { verification, lead }.
# @name verify
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-verify-001
Content-Type: application/json

{ "action": "crm.leadVerification.verify", "variables": { "id": "{{seedVerificationId}}", "notes": "Verified — insurance + damage confirmed." } }

@promotedLeadId = {{verify.response.body.$.lead.id}}

### 16. Get the promoted Lead (created by #15)
POST {{baseUrl}}/query
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
Content-Type: application/json

{ "action": "crm.lead.get", "variables": { "id": "{{promotedLeadId}}" } }

### 17. Move to Pending (send an assigned/in_progress record back). Seed a second record first if needed.
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-topending-001
Content-Type: application/json

{ "action": "crm.leadVerification.moveToPending", "variables": { "id": "{{verificationId}}" } }

### 18. Mark Unverified (any non-terminal record)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lv-unverified-001
Content-Type: application/json

{ "action": "crm.leadVerification.markUnverified", "variables": { "id": "{{verificationId}}", "reason": "Could not confirm ownership." } }

# ─────────────────────── LEADS: WRITES (POST /command + unique key) ──────────────

### 20. Create Lead — Full Form payload  (RUN — provides @createdLeadId)
# @name createLead
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lead-create-001
Content-Type: application/json

{ "action": "crm.lead.create", "variables": {
  "firstName": "John",
  "lastName": "Martinez",
  "phones": [
    { "number": "(469) 500-1000", "type": "Mobile", "primary": true },
    { "number": "(469) 500-2000", "type": "Home" }
  ],
  "emails": [ { "address": "john.martinez@example.com", "primary": true } ],
  "property": { "address": "4821 Spring Creek Pkwy", "city": "Plano", "state": "TX", "zip": "75023", "type": "Single Family" },
  "job": { "source": "Door Knock", "canvasserId": "{{principal}}", "leadType": "Insurance", "workType": "Roof Replacement", "tradeType": "Roofing", "urgency": "High", "notes": "Significant granule loss on the south slope." },
  "insurance": { "company": "State Farm", "claimStatus": "Filed", "claimNumber": "CLM-2026-1000", "policyNumber": "POL-080000", "adjusterName": "Marcus Powell", "adjusterPhone": "(972) 700-3000" },
  "assignment": { "assigneeId": "{{principal}}", "priority": "High", "followUp": "2026-06-04" }
} }

@createdLeadId = {{createLead.response.body.$.id}}

### 21. Update Lead (partial patch; priority title-case is normalized to lowercase)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lead-update-001
Content-Type: application/json

{ "action": "crm.lead.update", "variables": { "id": "{{createdLeadId}}", "patch": { "priority": "Medium", "jobNotes": "Adjuster meeting scheduled." } } }

### 22. Update Status (audited → lead_status_history)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lead-status-001
Content-Type: application/json

{ "action": "crm.lead.updateStatus", "variables": { "id": "{{createdLeadId}}", "status": "contacted", "note": "Spoke with homeowner." } }

### 23. Assign a rep (or null for Unassigned)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lead-assign-001
Content-Type: application/json

{ "action": "crm.lead.assign", "variables": { "id": "{{createdLeadId}}", "assigneeId": "{{principal}}" } }

### 24. Attachment add (site photo) — contentRef comes from crm.media.presignUpload (§11.5)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lead-attach-add-001
Content-Type: application/json

{ "action": "crm.lead.attachment.add", "variables": { "id": "{{createdLeadId}}", "attachment": { "contentRef": "obj/site-photo-1.jpg", "mimeType": "image/jpeg", "sizeBytes": 1048576, "filename": "roof-south.jpg" } } }

### 25. Attachment remove (attachmentId from the lead's attachments[] in #3/#16)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: lead-attach-remove-001
Content-Type: application/json

{ "action": "crm.lead.attachment.remove", "variables": { "id": "{{createdLeadId}}", "attachmentId": "REPLACE_WITH_ATTACHMENT_ID" } }

# ─────────────────────────── NEGATIVE / GUARD CHECKS ────────────────────────────

### Verify an already-verified record (expect 409 already_verified)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: guard-alreadyverified-001
Content-Type: application/json

{ "action": "crm.leadVerification.verify", "variables": { "id": "{{seedVerificationId}}" } }

### Create with no name (expect 422 name_required)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: guard-noname-001
Content-Type: application/json

{ "action": "crm.lead.create", "variables": { "firstName": "", "lastName": "  " } }

### Create with a bad email (expect 400 validation)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: guard-bademail-001
Content-Type: application/json

{ "action": "crm.lead.create", "variables": { "firstName": "Jane", "emails": [ { "address": "not-an-email" } ] } }

### Invalid state transition — verified is terminal via generic updateStatus (expect 409 / 422)
POST {{baseUrl}}/command
Authorization: Bearer dev
X-App-ID: crm-web
X-Tenant-ID: {{tenant}}
X-Principal-ID: {{principal}}
X-Idempotency-Key: guard-transition-001
Content-Type: application/json

{ "action": "crm.leadVerification.updateStatus", "variables": { "id": "{{seedVerificationId}}", "status": "pending" } }
