# Quickstart: Validating Ticket Creation, Messages & Attachments Prerequisites: 002-saas-integration's inbound trust boundary working (a seeded/registered `ProductIntegration`), MinIO running locally (research.md), migrations applied. ## Scenario 1 — a trusted request creates a ticket immediately (User Story 1) 1. Send a valid inbound request through `POST /v1/support/requests`. 2. **Expected**: `202` with a `ticketId`/`code`/`status: NEW`; the `Ticket` and its `Problem` exist in the database immediately — no polling needed. ## Scenario 2 — idempotency key prevents duplicate tickets (User Story 1) 1. Send the same request twice with the same `idempotencyKey`. 2. **Expected**: both responses reference the *same* `ticketId`; only one `Ticket` row exists. ## Scenario 3 — recurring problem links to the existing Problem (User Story 1) 1. Create a ticket, note its `problemId`. 2. Send a second, distinct request whose `referenceIds` includes that same problem's reference. 3. **Expected**: the new ticket has the *same* `problemId` as the first — no second `Problem` created. ## Scenario 4 — internal notes never leak to a customer-scoped read (User Story 2) 1. Post one message of each type (`CUSTOMER_MESSAGE`, `AI_MESSAGE`, `AGENT_MESSAGE`, `INTERNAL_NOTE`, `SYSTEM_EVENT`, `INVESTIGATION_NOTE`, `SOLUTION_NOTE`) to a ticket. 2. Read the ticket's messages through a customer-scoped call. 3. **Expected**: only `CUSTOMER_MESSAGE`, `AI_MESSAGE`, `AGENT_MESSAGE`, `SYSTEM_EVENT` appear — the other three are absent entirely, not present-but-flagged. 4. Read the same ticket's messages through an agent-scoped call. 5. **Expected**: all seven messages appear. ## Scenario 5 — an attachment is unusable until it clears scanning (User Story 3) 1. Request an upload URL, PUT a file, confirm the upload. 2. Immediately request a download URL. 3. **Expected**: `409` — `scanStatus` is `pending`. 4. Wait for the scan job to run (with the placeholder scanner, research.md — it fails closed to `infected`). 5. Request a download URL again. 6. **Expected**: still refused — `scanStatus: infected` — confirming the pipeline correctly gates on a real (even if placeholder) scan result rather than defaulting to available. ## Scenario 6 — a stale ticket-status update is rejected, not overwritten (Edge Cases) 1. Read a ticket's current `status`/`version`. 2. In two separate calls, attempt two different valid status transitions using the *same* `expectedVersion`. 3. **Expected**: exactly one succeeds; the other receives `409 CONFLICT` and must re-read the ticket to retry. ## What "done" looks like All six scenarios pass, and together they demonstrate every functional requirement and success criterion in `spec.md` without needing to read the implementation to know what "correct" means.