Skip to main content
The Reputation API is the programmatic surface behind Reputation Studio. It has two layers:
  • Authenticated routes under /api/reputation/* — bearer-token, org-scoped, used by the in-app Reputation Studio UI and your own integrations.
  • Public routes under /api/public/{survey,reviews,referral}/* — unauthenticated, CORS-enabled where appropriate, used by tokenized survey pages, the embeddable reviews widget, and the public referral capture form.
All authenticated endpoints follow the same conventions as the rest of the Winnerr API. See API Introduction for base URLs, bearer-token forwarding, and the standard 401 behavior when userId or orgId are missing.

Authentication

Authenticated routes expect the standard bearer token forwarded by apps/app:
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/reputation/testimonials`, {
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
});
Every authenticated route runs inside the caller’s organization scope — you can only read and write reputation data for your own org. Public routes (/api/public/...) require no auth. The capability is the survey accessToken or the published profile slug, and the server resolves the owning org server-side.

Testimonials

GET /api/reputation/testimonials

List testimonials in the caller’s org. Optional query params:
ParamValues
statusPENDING, APPROVED, PUBLISHED, REJECTED
visibilityPRIVATE, INTERNAL, PUBLIC
agentUserIdFilter to a specific agent
categoryBUYER, SELLER, INVESTOR, …
Returns { testimonials: Testimonial[] }, ordered by sortOrder asc then createdAt desc.

POST /api/reputation/testimonials

Create a testimonial manually. Body fields include agentUserId, authorPersonId, dealId, source, category, rating, headline, body, display fields, and the three permission* flags. New entries always start PENDING + PRIVATE — they must still flow through approve → publish before they are public.

GET /api/reputation/testimonials/:id

Fetch one testimonial including its syndications.

PATCH /api/reputation/testimonials/:id

Partial update. Useful for editing copy, toggling isFeatured, reordering via sortOrder, or demoting visibility back to PRIVATE after publish.

POST /api/reputation/testimonials/:id/approve

Transition PENDINGAPPROVED. Records approvedByUserId and approvedAt and emits REVIEW_APPROVED. Still not public.

POST /api/reputation/testimonials/:id/publish

Transition APPROVEDPUBLISHED, set visibility = PUBLIC, stamp publishedAt, emit REVIEW_PUBLISHED, and recompute the agent’s ReputationAggregate synchronously. Requires the testimonial to already be APPROVED — the call fails closed if the testimonial is still PENDING. See Testimonials for the lifecycle in context.

Surveys

GET /api/reputation/surveys

List surveys in the caller’s org. Optional query params: status, agentUserId.

POST /api/reputation/surveys

Issue a new survey manually. Body:
{
  "templateId": "tmpl_123",
  "recipientPersonId": "person_456",
  "agentUserId": "user_789",
  "dealId": "deal_abc",
  "channel": "EMAIL"
}
The server generates a unique accessToken, schedules the send, and emits SURVEY_SCHEDULED. The default expiry window is 30 days from creation. Auto-scheduled surveys (from DEAL_WON) use the same code path — they do not require a client call.

Survey templates

MethodPathPurpose
GET/api/reputation/surveys/templatesList org-scoped SurveyTemplate rows.
POST/api/reputation/surveys/templatesCreate a template. sendDelayHours defaults to 168 (7 days).
See Surveys for template fields and lifecycle.

Referrals

GET /api/reputation/referrals

List referrals in the caller’s org. Optional query params: status, agentUserId.

POST /api/reputation/referrals

Log a referral manually. Body fields include referrerPersonId, agentUserId, channel, referredName, referredContact, notes, and optional reward tracking (rewardType, rewardStatus, rewardValue). When public capture creates a referral instead, the server emits REFERRAL_RECEIVED and the downstream workflow turns the referral into a Person with LeadSource.REFERRAL. See Referrals.

Profile

GET /api/reputation/profile

Fetch an AgentPublicProfile. Optional ?agentUserId=<id> returns that agent’s profile; otherwise returns the profile owned by the calling user.

PUT /api/reputation/profile

Upsert the calling user’s profile (or agentUserId if provided). Edits write into draftConfig — the live public page does not change until publish.

POST /api/reputation/profile/publish

Validate draftConfig against the approved-block schema, copy it into publishedConfig, flip status to PUBLISHED, and stamp publishedAt. Body is optional { "agentUserId": "..." }; defaults to the caller’s own profile.

Aggregate

GET /api/reputation/aggregate

Read the denormalized ReputationAggregate rollup.
  • ?agentUserId=<id> returns that agent’s aggregate (scope AGENT).
  • With no agentUserId, returns the org-wide aggregate row (scope ORG, where agentUserId is null).
The response includes total reviews, average rating, NPS, response rate, review velocity, and rating distribution. Aggregates recompute automatically when a testimonial is published or unpublished.

Public endpoints

The public routes are the surfaces that don’t require auth. The capability is the survey accessToken or the published profile slug, and the org is resolved server-side.

GET /api/public/survey/:token

Resolve a tokenized survey, return its question schema and minimal branding (brokerage name only — never agent PII, never other surveys), mark the survey OPENED, and emit SURVEY_OPENED. Returns a uniform 404 ({ "state": "gone" }) for unknown, expired, completed, or skipped tokens so that guessing learns nothing.

POST /api/public/survey/:token

Submit a SurveyResponse. The body is validated against the template’s Zod schema, size-capped at 64 KB, and rate-limited. On success the server persists the response, flips the survey to COMPLETED, and emits SURVEY_COMPLETED. The reputation response consumer then runs sentiment analysis and (if the client opted in and rated highly) promotes the response into a PENDING Testimonial.

GET /api/public/reviews/:agentSlug

The JSON feed the embeddable Reviews Widget and the public profile read. Returns:
  • Published, public testimonials for the agent (consent-allowed public fields only — display name, location, photo).
  • The agent’s ReputationAggregate.
  • Minimal profile branding.
CORS-enabled (Access-Control-Allow-Origin: *) and cached at the edge for one minute. Anything other than a PUBLISHED, non-deleted profile returns a uniform 404. Embed:
<script>
  fetch("https://api.winnerr.ai/api/public/reviews/jane-doe-realty")
    .then((r) => r.json())
    .then((data) => renderWidget(data));
</script>

POST /api/public/referral/:agentSlug

Capture a referral from the public profile or widget CTA. Body example:
{
  "referrerName": "Pat Jones",
  "referrerEmail": "pat@example.com",
  "referredName": "Sam Lee",
  "referredEmail": "sam@example.com",
  "notes": "Sam is selling their condo this spring."
}
The server records the referrer as a Person, creates a Referral (status: RECEIVED, channel: PUBLIC_PROFILE), and emits REFERRAL_RECEIVED so the lead re-enters the pipeline. Rate-limited; body is capped at 32 KB.

Events emitted

The reputation routes emit the following system events on the standard event bus. Workflows in apps/api consume them.
EventFired when
DEAL_WON(Upstream) The reputation survey workflow listens for this to auto-schedule a survey.
SURVEY_SCHEDULEDA new Survey is created.
SURVEY_OPENEDThe tokenized public page is opened.
SURVEY_COMPLETEDA SurveyResponse is submitted.
REVIEW_APPROVEDA testimonial is moved to APPROVED.
REVIEW_PUBLISHEDA testimonial is published to the public surface.
REFERRAL_RECEIVEDA referral is captured (public or manual).