Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
Developer Handover Pack
Review and Reputation Management
[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]
This document is the primary technical reference for the FullSpec team building the Review and Reputation Management automation. It covers the current-state process map, agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. Everything required to configure, wire, and test each agent is contained here. The owner-facing documents cover rollout, training, and SOP; this pack covers construction.
01Process snapshot
02Agent specifications
Reads each incoming review record and assigns a sentiment category (positive, neutral, or negative), a confidence score between 0 and 1, and a concise one-line topic summary. Its output fields determine which downstream branch the workflow follows: positive and neutral reviews proceed directly to the Response Drafting Agent, while negative reviews (two stars or below) trigger the Slack escalation path. This agent must handle all supported platform formats and gracefully degrade when review text is very short (under 10 words) or in a non-English language.
// Input platform: string // 'google_business_profile' | 'trustpilot' reviewer_name: string // May be 'Anonymous' on some platforms star_rating: integer // 1-5 review_text: string // Raw review body review_id: string // Platform-native unique ID (dedup key) review_timestamp: string // ISO 8601 // Output sentiment: string // 'positive' | 'neutral' | 'negative' confidence_score: float // 0.00-1.00 topic_summary: string // Max 20 words, plain English is_negative_flag: boolean // true if star_rating <= 2 notion_row_id: string // ID of newly created Notion tracker row classification_timestamp: string // ISO 8601
- Google Business Profile API access requires a verified Business Profile account and the 'My Business API' scope enabled. Confirm this before build; some accounts are restricted to the Business Profile UI only.
- Trustpilot access requires a Business account on at least the Lite paid tier to enable API read access. Free accounts do not expose review data via API. Confirm account tier with the owner before build.
- Use review_id as the deduplication key. Before creating a Notion row, check the tracker for an existing row with the same review_id and platform combination. If found, skip and log a duplicate_skipped event.
- If review_text is fewer than 10 words, set confidence_score to 0.5 and topic_summary to 'Short review, manual check recommended'. Do not block downstream flow.
- If review_text is detected as non-English (use a lightweight language detection call), append lang_detected: '<code>' to the Notion row and set confidence_score to 0.4. Still pass downstream.
- The Notion tracker database must have the following properties pre-created: Platform (select), ReviewerName (text), StarRating (number), ReviewText (text), Sentiment (select: positive/neutral/negative), ConfidenceScore (number), TopicSummary (text), IsNegative (checkbox), ResponseStatus (select: pending/approved/posted), ReviewID (text), CreatedAt (date). Confirm database ID before build.
- Polling interval is every 15 minutes. Use the platform's 'updated_since' or 'start_date' parameter where available to avoid re-processing old reviews on each poll.
Generates a contextually appropriate reply for each review using the sentiment classification, star rating, review text, and brand voice guidelines stored in the agent's system prompt. Positive and neutral reviews receive an auto-approved draft that proceeds immediately to posting. Negative reviews (is_negative_flag: true) receive a draft that is packaged into a Slack message to the designated manager channel along with an approval link. The agent must not post any response autonomously for negative reviews under any circumstance. Brand voice guidelines must be loaded into the system prompt from a versioned source (a Notion page or a static prompt file in the credential store) so they can be updated without a code change.
// Input sentiment: string // From Review Classifier Agent output star_rating: integer // 1-5 review_text: string // Raw review body topic_summary: string // From classifier is_negative_flag: boolean // Routing gate notion_row_id: string // For status update after action platform: string // Determines posting method reviewer_name: string // Used in response personalisation // Output (positive/neutral path) draft_response: string // Brand-voice reply, max 300 characters post_status: string // 'posted' | 'failed' posted_at: string // ISO 8601 notion_row_id: string // Passed to CRM and Reporting Agent // Output (negative path — Slack escalation) draft_response: string // Draft included in Slack message slack_message_ts: string // Slack message timestamp (for threading) approval_link: string // Deep link to approval action approval_status: string // 'pending' | 'approved' | 'rejected' // On approval (negative path, after manager action) post_status: string // 'posted' | 'failed' posted_at: string // ISO 8601 approved_by: string // Slack user ID of approving manager notion_row_id: string // Passed to CRM and Reporting Agent
- Brand voice guidelines must be stored in a dedicated Notion page (or a versioned prompt config file) and loaded into the system prompt at runtime. Never hardcode the brand voice text in the workflow definition. Confirm the owner has prepared this document before build begins; prompt tuning cannot proceed without it.
- Allow at least one week of prompt tuning after initial build before enabling auto-posting for positive reviews. Run in draft-only mode first, reviewing outputs manually against the brand voice document.
- The Slack escalation message must include: reviewer name, star rating, platform, review text (truncated to 500 characters), the AI draft response, and a single-click approve button and reject button using Slack interactive components (Block Kit). Slack interactivity requires a Request URL webhook configured in the Slack App manifest.
- If the manager clicks Reject, the workflow must update the Notion row to ResponseStatus: 'manual_required' and send a follow-up Slack thread message prompting the manager to post a manual reply. Do not post the draft.
- For Google Business Profile responses, use the My Business API v4.9 'accounts.locations.reviews.updateReply' endpoint. Confirm OAuth 2.0 credentials with the business_management scope are available.
- For platforms accepting email-based replies (where GBP API is not available), use Gmail via the Gmail API 'users.messages.send' method. The reply-to address must be the platform-registered business email. Confirm this address with the owner.
- Implement a retry with exponential backoff (3 attempts, 30s/60s/120s) on any posting failure. After three failures, set post_status to 'failed', update the Notion row, and send a Slack alert to the operations channel.
- The draft_response character limit must respect each platform's reply limit: Google Business Profile allows up to 4,096 characters; set a soft cap of 300 characters for quality and readability. Trustpilot allows 3,000 characters.
Matches the reviewer to an existing HubSpot contact using name and email where available, then logs the review outcome as a timeline activity on the matched contact record. For anonymous reviews or platforms that mask reviewer details, the agent creates a partial record flagged for manual enrichment rather than skipping the step entirely. Additionally, every Monday at 08:00 local time this agent queries the Notion tracker for the prior week's data, compiles a reputation summary, and posts it to the designated Slack channel. The reporting function is time-triggered and independent of the per-review flow.
// Input (per-review trigger)
notion_row_id: string // Completed review row
reviewer_name: string // Used for HubSpot contact search
reviewer_email: string // May be null for anonymous reviews
platform: string
star_rating: integer
sentiment: string
post_status: string // Must be 'posted' to proceed
posted_at: string // ISO 8601
approved_by: string // Slack user ID or null for auto-posted
// Input (weekly schedule trigger)
report_week_start: string // ISO 8601 date, Monday
report_week_end: string // ISO 8601 date, Sunday
// Output (per-review)
hubspot_contact_id: string // Matched contact ID or 'partial_record_created'
hubspot_activity_id: string // Timeline activity ID
notion_row_updated: boolean // True when ResponseStatus set to 'posted'
// Output (weekly report)
slack_report_ts: string // Slack message timestamp
report_summary: object // { total_reviews, avg_rating, response_rate_pct,
// sentiment_breakdown: {positive, neutral, negative},
// top_complaint_themes: string[] }- HubSpot contact search uses the CRM Search API v3 endpoint 'crm/v3/objects/contacts/search'. Search by email first; if null or no match, search by firstname and lastname combined. Require a confidence threshold of 90% name match before associating to avoid false positives.
- If no HubSpot contact is matched, create a new contact with available fields (reviewer_name split into firstname/lastname, platform as a custom property 'review_source') and set a custom property 'review_match_status' to 'partial_unverified'. Do not block the workflow.
- HubSpot timeline activity is created as a Note on the contact record using the Engagements API v1 'engagements/v1/engagements'. Include: platform, star_rating, sentiment, topic_summary, posted_at, and a link to the Notion row. HubSpot CRM API requires a private app token with contacts.read, contacts.write, and crm.objects.contacts.write scopes.
- The weekly Slack report must cover: total reviews detected in the week, average star rating, response rate as a percentage, sentiment breakdown (count per category), and up to three recurring complaint themes derived from topic_summary fields. Use a Slack Block Kit formatted message with section headers for readability.
- Notion query for the weekly report uses the database filter: CreatedAt is on or after report_week_start AND on or before report_week_end. Sort descending by CreatedAt. Page through results if count exceeds 100 (Notion API page size limit).
- Confirm the HubSpot account tier supports the CRM API and timeline activity creation. Operations Hub Starter or above is required for some timeline features. Confirm with the owner before build.
03End-to-end data flow
// ============================================================
// REVIEW AND REPUTATION MANAGEMENT: END-TO-END DATA FLOW
// ============================================================
// TRIGGER: Scheduled polling (every 15 minutes)
// Source: Google Business Profile API + Trustpilot API
// Fields captured at trigger:
platform = 'google_business_profile' | 'trustpilot'
reviewer_name = string // e.g. 'Jane Smith' | 'Anonymous'
star_rating = integer // 1-5
review_text = string // Raw review body
review_id = string // Platform-native UID (dedup key)
review_timestamp = string // ISO 8601
// DEDUP CHECK: Query Notion tracker for existing review_id + platform
// If match found -> log duplicate_skipped, end workflow
// If no match -> continue
// ============================================================
// AGENT HANDOFF 1: Trigger -> Review Classifier Agent
// Fields passed: platform, reviewer_name, star_rating,
// review_text, review_id, review_timestamp
// ============================================================
// REVIEW CLASSIFIER AGENT processing:
sentiment = 'positive' | 'neutral' | 'negative'
confidence_score = float // 0.00-1.00
topic_summary = string // Max 20 words
is_negative_flag = boolean // true if star_rating <= 2
// ACTION: Write classified record to Notion tracker
// Notion DB properties written:
notion_row_id = string // Returned by Notion API on create
Platform = platform
ReviewerName = reviewer_name
StarRating = star_rating
ReviewText = review_text
Sentiment = sentiment
ConfidenceScore = confidence_score
TopicSummary = topic_summary
IsNegative = is_negative_flag
ResponseStatus = 'pending'
ReviewID = review_id
CreatedAt = review_timestamp
// ============================================================
// AGENT HANDOFF 2: Review Classifier Agent -> Response Drafting Agent
// Fields passed: sentiment, star_rating, review_text,
// topic_summary, is_negative_flag, notion_row_id,
// platform, reviewer_name
// ============================================================
// RESPONSE DRAFTING AGENT processing:
// Load brand voice guidelines from Notion config page at runtime
draft_response = string // Max 300 chars, platform-appropriate tone
// BRANCH A: is_negative_flag == false (positive or neutral)
// -> Post response directly to platform
// -> GBP: My Business API v4.9 'accounts.locations.reviews.updateReply'
// -> Gmail API: 'users.messages.send' for email-reply platforms
post_status = 'posted' | 'failed'
posted_at = string // ISO 8601
// -> On failure: retry x3 (30s/60s/120s), then alert ops Slack channel
// -> Update Notion row: ResponseStatus = 'posted'
// BRANCH B: is_negative_flag == true (2 stars or below)
// -> Send Slack Block Kit message to manager channel
slack_message_ts = string // For threading
approval_link = string // Interactive component action URL
approval_status = 'pending'
// -> Manager clicks Approve:
approved_by = string // Slack user ID
approval_status = 'approved'
post_status = 'posted' | 'failed'
posted_at = string // ISO 8601
// -> Update Notion row: ResponseStatus = 'posted'
// -> Manager clicks Reject:
approval_status = 'rejected'
// -> Update Notion row: ResponseStatus = 'manual_required'
// -> Send Slack thread: prompt manager to post manually
// ============================================================
// AGENT HANDOFF 3: Response Drafting Agent -> CRM and Reporting Agent
// Triggered only when post_status == 'posted'
// Fields passed: notion_row_id, reviewer_name, reviewer_email,
// platform, star_rating, sentiment,
// post_status, posted_at, approved_by
// ============================================================
// CRM AND REPORTING AGENT processing (per-review):
// Step 1: Search HubSpot by reviewer_email (primary)
// Fallback: search by firstname + lastname (>=90% match required)
hubspot_contact_id = string // Matched ID or 'partial_record_created'
// Step 2: Create HubSpot timeline activity (Engagements API v1)
hubspot_activity_id = string
// Activity fields: platform, star_rating, sentiment,
// topic_summary, posted_at, notion_row link
// Step 3: Update Notion row
// ResponseStatus = 'posted'
notion_row_updated = boolean
// ============================================================
// WEEKLY SCHEDULE TRIGGER: Every Monday 08:00 local time
// -> CRM and Reporting Agent (reporting function)
// ============================================================
// Query Notion tracker: CreatedAt >= report_week_start
// AND CreatedAt <= report_week_end
report_summary = {
total_reviews: integer,
avg_rating: float,
response_rate_pct: float,
sentiment_breakdown: { positive: int, neutral: int, negative: int },
top_complaint_themes: string[] // Max 3 items from topic_summary
}
// Post Slack Block Kit report to #marketing channel
slack_report_ts = string // Slack message timestamp
// ============================================================
// END OF FLOW
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Review & Reputation Management.