PPactDocs
Administration

Surveys

NPS, CSAT, and CES surveys with hosted public responses, closed-loop automation that turns detractors into save tasks, and lifecycle triggers that fire surveys at the right moment.

Surveys

Pact surveys measure customer sentiment with the three standard instruments — NPS, CSAT, and CES — and then act on the results. Responses are collected through hosted public pages, scored automatically, and fed into closed-loop workflows. Admin CRUD lives at /v1/surveys; public responses come in through /public/surveys. Backing tables are surveys and survey_responses (alembic 0146).

Survey types

Each type has a fixed scoring scale baked into SURVEY_TYPES (api/routes/surveys.py):

TypeScaleDefault question
nps0–10"How likely are you to recommend us to a friend or colleague?"
csat1–5"How satisfied are you with your experience?"
ces1–7"How satisfied are you with your experience?" (effort framing)
code
GET /v1/surveys/types

returns the catalog with each type's score_min, score_max, default question, and default follow-up prompt — the data behind the "+ New Survey" type picker.

Create and manage

code
GET    /v1/surveys                 → list (filter by status, type, q)
POST   /v1/surveys                 → create from a type template
GET    /v1/surveys/{survey_id}     → get one
PATCH  /v1/surveys/{survey_id}     → update name/question/follow-up/status/channels
DELETE /v1/surveys/{survey_id}     → soft delete

A survey has a name, a question, an optional open-text follow_up_prompt, a status, and a list of channels (defaults to ["email"]). Creating from a type pre-fills the question and follow-up from the template. Each survey gets a public_id (UUIDv4) — that is what the public response endpoint resolves against, never the internal integer id.

Collecting responses

Respondents answer through a hosted page at /public/surveys/{public_id}, with IP rate-limiting and a respond endpoint that records the score, optional comment, and a scoring band (promoter/passive/detractor for NPS). Response identifiers are hashed for privacy.

Closed-loop automation

The real value is what happens after a response lands. Rules in core/surveys/closed_loop.py fire immediately on submit, and are toggled at /v1/survey-rules:

  • Detractor → CS task (detractor_to_task) — an NPS score of 0–6 with an attributed account auto-creates a pending save task for the account owner, with the verbatim comment attached as evidence.
  • Promoter → referral CTA (promoter_to_referral) — an NPS score of 9–10 queues a referral event for marketing follow-up.

Rules default ON

Every rule kind is enabled by default (DEFAULT_ENABLED), so a new tenant gets the closed loop without discovering the toggle first. A rule kind with no row in survey_rules is treated as enabled. Toggle any rule at PATCH /v1/survey-rules/.

Lifecycle triggers

Beyond the reactive rules, Pact ships lifecycle triggers that decide when to send a survey (core/surveys/lifecycle.py), also default-on:

  • post_onboarding_t7 — 7 days after onboarding
  • post_close_won_t14 — 14 days after a deal closes won
  • pre_renewal_t30 — 30 days before renewal
  • post_support_case_close — after a support case closes

Together these give a new tenant a standard NPS/CSAT cadence out of the box.