> ## Documentation Index
> Fetch the complete documentation index at: https://docs.winnerr.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Testimonials

> The Pending → Approved → Published lifecycle, with consent capture and visibility controls.

A `Testimonial` is the single persisted source of truth for a client review in Winnerr — its rating, body, who it is about, who it is from, which transaction it came from, its approval status, its visibility, and the explicit permissions the client granted.

Testimonials are **agent-governed**: nothing becomes public until the agent explicitly approves and then publishes it.

## Where testimonials come from

| Source   | How it's created                                                            |
| -------- | --------------------------------------------------------------------------- |
| `SURVEY` | Auto-promoted from a high-rated `SurveyResponse` where the client opted in. |
| `MANUAL` | Created in-app by the agent (e.g. transcribed from a handwritten card).     |
| `IMPORT` | Bulk-imported from a prior platform.                                        |

All sources land in the **approval inbox** with status `PENDING` and visibility `PRIVATE`. The lifecycle from there is the same regardless of source.

## Lifecycle

```text theme={null}
PENDING ──approve──▶ APPROVED ──publish──▶ PUBLISHED
                                              │
                                              ▼
                                  ReputationAggregate recomputes
                                  Public profile + widget update
                                  Syndication can be proposed
```

| Status      | Meaning                                                                  |
| ----------- | ------------------------------------------------------------------------ |
| `PENDING`   | Awaiting agent review. Not visible anywhere public.                      |
| `APPROVED`  | Agent has approved the content but it is not yet on the public surface.  |
| `PUBLISHED` | Visible on the public profile, the widget, and eligible for syndication. |
| `REJECTED`  | Agent declined; archived but retained for audit.                         |

Approval and publish are **two separate steps on purpose**. Approve confirms the content; publish is the explicit "make it public" action that also triggers aggregate recompute and syndication eligibility.

### Approve

`POST /api/reputation/testimonials/:id/approve` transitions a testimonial from `PENDING` to `APPROVED`, records `approvedByUserId` and `approvedAt`, and emits `REVIEW_APPROVED`. The testimonial is still private.

### Publish

`POST /api/reputation/testimonials/:id/publish` requires the testimonial to be `APPROVED` first (the gate prevents jumping straight from `PENDING` to public). On success it:

* Sets `status = PUBLISHED` and `visibility = PUBLIC`.
* Stamps `publishedAt`.
* Emits `REVIEW_PUBLISHED`.
* Recomputes the agent's `ReputationAggregate` synchronously so the freshly published review is reflected immediately on the public profile.

See the [API reference](/api-reference/reputation#testimonials) for full request and response shapes.

## Visibility

Even after publication, a testimonial's reach is bounded by its `visibility`:

| Visibility | Surface                                                                           |
| ---------- | --------------------------------------------------------------------------------- |
| `PRIVATE`  | In-app only; never on the public profile or widget.                               |
| `INTERNAL` | Org-wide visibility (e.g. broker dashboards) but not public.                      |
| `PUBLIC`   | Eligible for the public profile, the embeddable widget, and outbound syndication. |

Publishing automatically sets visibility to `PUBLIC`. Agents can demote a published testimonial back to `PRIVATE` at any time via `PATCH /api/reputation/testimonials/:id` — the next aggregate recompute will exclude it.

## Consent and permissions

Each testimonial carries three explicit permission flags captured at survey time:

| Flag                 | What it gates                                                   |
| -------------------- | --------------------------------------------------------------- |
| `permissionUseName`  | Whether the agent can show the client's name (vs. "Jane D.").   |
| `permissionUsePhoto` | Whether `authorPhotoR2Key` can be rendered publicly.            |
| `permissionPublic`   | Whether the testimonial can be displayed on any public surface. |

`permissionCapturedAt` and a full `consentSnapshot` JSON blob preserve exactly what the client agreed to, when. The public endpoints honor these flags automatically — a published testimonial with `permissionUsePhoto: false` will simply omit the photo when the embeddable widget renders it.

## Approval inbox

The Reputation Studio app routes pending testimonials to the **review approval inbox**, ranked by rating, recency, and source. From there the agent can:

* Read the body and metadata.
* Edit headline, body, or display name (AI grammar cleanup is allowed but voice must be preserved; `aiEdited` is set when AI touched the content).
* Approve, publish, reject, or save as draft.
* Feature a published testimonial on the profile via `isFeatured`.
* Reorder published testimonials via `sortOrder`.

## Creating a testimonial manually

If a client sent a thank-you note outside of a survey, you can capture it directly:

```bash theme={null}
curl -X POST https://api.winnerr.ai/api/reputation/testimonials \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "agentUserId": "user_789",
    "authorPersonId": "person_456",
    "dealId": "deal_abc",
    "source": "MANUAL",
    "category": "BUYER",
    "rating": 5,
    "headline": "Bought our dream home in 3 weeks",
    "body": "...",
    "authorDisplayName": "Jane D.",
    "authorLocation": "Mississauga, ON",
    "permissionUseName": true,
    "permissionUsePhoto": false,
    "permissionPublic": true
  }'
```

Manual entries always start `PENDING` + `PRIVATE`; they still flow through the approve → publish gate before reaching the public profile.

## Syndication

Once a testimonial is `PUBLISHED`, Winnerr automatically generates outbound "leave us a review" links for the platforms you have configured — **Google Business**, **Facebook**, **Yelp** (opt-in with compliance warning), and any **custom platforms** you add. Each is recorded as a `ReviewSyndication` row tied to the testimonial with direction `OUTBOUND` and status `REQUESTED`. These links are what Winnerr surfaces to satisfied survey responders via the Smart Review Composer on the survey thank-you screen; Winnerr never posts on your behalf.

Inbound syndication (importing your existing Google reviews) is handled separately — see [Review syndication](/reputation/syndication).

## Related

* [Surveys](/reputation/surveys) — the upstream source for most testimonials.
* [Public profile](/reputation/public-profile) — what published testimonials render into.
* [Reviews widget](/reputation/widgets) — embeds the same published testimonials on your website.
* [Review syndication](/reputation/syndication) — outbound review-request links and Google Business import.
* [Reputation API — testimonials](/api-reference/reputation#testimonials) — list, create, patch, approve, publish.
