## TL;DR
Survey spam happens when the send logic has no memory: ticket updates re-trigger the survey, retries double-send, and reopened tickets survey twice. Dedupe on ticket plus survey type, enforce a per-customer cooldown, and make the send idempotent so retries never duplicate. One ticket, one survey, ever.

## The query

```text
csat survey agent spamming follow-ups: dedupe guard
```

## Use this when

- Customers get duplicate CSAT surveys
- Reopened tickets trigger second surveys
- You are building survey automation
- Survey complaints spike

## Not for

- Writing the survey questions
- Deciding when to send surveys
- Analyzing CSAT scores
- NPS program design

## Steps

### 1. Dedupe on ticket plus survey type

Record every survey sent keyed by ticket id and survey type. Before sending, check the key. A ticket that already got its CSAT survey never gets another, no matter how many updates or reopens follow.

Expected output: sent-survey registry checked before every send.

### 2. Enforce a per-customer cooldown

Even across tickets, cap survey frequency: no customer surveyed more than once per 30 days. Frequent ticket openers should not become frequent survey takers. The cooldown is per customer, not per ticket.

Expected output: a 30-day per-customer survey cap enforced.

### 3. Make the send idempotent

Retries, double-triggers, and race conditions should never produce two surveys. Use idempotency keys on the send call or check-then-send atomically. Idempotent sends make the dedupe bulletproof.

Expected output: duplicate triggers collapse to one send.

### 4. Suppress on reopen within the window

If a ticket reopens within a few days of close, suppress the survey from the first close and send one survey at the final resolution. The customer experiences one ticket; they should get one survey.

Expected output: reopen suppression with a single final survey.

### 5. Monitor sends per customer

Track survey sends per customer per month and alert on outliers. The metric catches logic bugs before customers complain: a customer with five surveys in a week is a bug, not a super-user.

Expected output: per-customer send monitoring with alerts.

## Template: the checklist

```text
CSAT survey dedupe:
[ ] Sent registry keyed by ticket plus survey type
[ ] Per-customer cooldown enforced (30 days)
[ ] Idempotent sends: retries never duplicate
[ ] Reopen suppression within the window
[ ] Per-customer send counts monitored
```

## Variant phrasings

### duplicate csat surveys same ticket

Steps 1 and 3. Registry plus idempotency.

### customer surveyed too often

Step 2. The per-customer cooldown.

### survey sent twice on reopen

Step 4. Suppress, then survey once at final close.

## Why it happens

Survey triggers attach to ticket events (close, resolve), and tickets generate many such events: updates after close, reopens, merges. Each event looks like a fresh survey opportunity to trigger logic with no memory. The spam is not a bug in any single trigger; it is the absence of a shared send memory.

## Edge cases

- Merged tickets: two tickets with two surveys merge into one. Dedupe across the merge or suppress the loser's survey.
- Multiple survey types: CSAT plus a follow-up survey need separate dedupe keys but a shared cooldown.
- Test tickets: internal test closes should never send surveys. Exclude test requesters.
- The customer replies to the survey with a new issue: route it as a ticket, and do not count it as survey engagement for cooldown purposes.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_EgpOnY8Ib2eW-1ApWCecjw
