Request Review Controller
Source: internal/api/v1/reviews/controllers/request-review.js
Service: requestReview service
Module: Reviews
Overview
The Request Review controller manages manual review request campaigns. It enables sending review requests to customers via email, SMS, or both, with customizable templates and platform-specific review links.
Key Capabilities
- Send Review Requests to selected contacts via email/SMS
- Get Review Links for all connected platforms
- List Review Requests with filtering and pagination
- Get Request Filters for UI dropdowns
- Update Email Templates for review requests
- Get Current Template configuration
MongoDB Collections
| Collection | Operations | Purpose |
|---|---|---|
review-requests | CREATE, READ | Track sent review requests |
review-request-templates | READ, UPDATE | Email/SMS templates |
contacts | READ | Customer contact information |
review-config | READ | Platform review links |
Service Methods
1. send()
Send review requests to selected contacts.
Endpoint: POST /reviews/request-review/send
Controller Logic:
const send = catchAsync(async (req, res, next) => {
const body = validate(requestReviewSchema.send.body, req.body);
const account_id = req.auth.account_id;
const user_id = req.auth.uid;
const { type, contact_ids } = body;
const { errors, contact_ids: cids } = await requestReview.processData(
account_id,
contact_ids,
type,
);
const message = await requestReview.send(cids, type, account_id, user_id);
res.json({
success: true,
message,
errors: errors,
});
});
Request:
{
"type": "both",
"contact_ids": ["contact_abc123", "contact_xyz789"]
}
Request Body:
type(string): Delivery method - "email", "sms", or "both"contact_ids(array): Array of contact IDs to send requests to
Response:
{
"success": true,
"message": "Your notifications are being processed.",
"errors": [
"Email not found for the contact John Doe. This contact will be skipped.",
"Phone number not found for Jane Smith. This contact will be skipped."
]
}
Business Logic Flow:
graph TD
A[Receive Send Request] --> B[Validate Contact IDs]
B --> C[Remove Duplicates]
C --> D[Fetch Contacts from DB]
D --> E{Contacts Found?}
E -->|No| F[Throw 404 Not Found]
E -->|Yes| G[Validate Contact Data]
G --> H{type = email?}
H -->|Yes| I[Check Email Exists]
I --> J{Has Email?}
J -->|No| K[Add to Errors Array]
G --> L{type = sms?}
L -->|Yes| M[Check Phone Exists]
M --> N{Has Phone?}
N -->|No| O[Add to Errors Array]
J -->|Yes| P[Process Valid Contacts]
N -->|Yes| P
K --> P
O --> P
P --> Q[Get Review Links]
Q --> R[Get Account Info]
R --> S[Find Twilio Number]
S --> T{Has Twilio Number?}
T -->|No| U[Throw Error]
T -->|Yes| V[Create ReviewRequest Doc]
V --> W[Call Notification Service]
W --> X[Return Success Message]
Email Template Variables:
{{business_name}}- Account business name{{review_links}}- All platform links{{google_link}}- Google review link{{facebook_link}}- Facebook review link{{yelp_link}}- Yelp review link
SMS Template:
Thanks for choosing [Business Name]. Would you please leave us a review?
[domain]/[account_id]/review
Key Features:
- Batch Processing: Send to multiple contacts at once
- Validation: Checks for required fields (email/phone)
- Error Handling: Returns partial success with error list
- Duplicate Prevention: Removes duplicate contact IDs
- Notification Queue: Async processing via notification service
2. getLinks()
Retrieve review links for all connected platforms.
Endpoint: GET /reviews/request-review/links
Request:
GET / api / v1 / a / reviews / request - review / links;
Response:
{
"success": true,
"data": {
"google": {
"platform": "google",
"url": "https://search.google.com/local/writereview?placeid=ChIJ...",
"icon": "google-icon.png"
},
"facebook": {
"platform": "facebook",
"url": "https://www.facebook.com/pg/[page-id]/reviews",
"icon": "facebook-icon.png"
},
"yelp": {
"platform": "yelp",
"url": "https://www.yelp.com/writeareview/biz/[business-id]",
"icon": "yelp-icon.png"
}
}
}
Use Cases:
- Display review links in email template
- Show links on website widget
- Generate QR codes for physical locations
- Social media post links
3. getRequests()
List all review requests with filtering.
Endpoint: GET /reviews/request-review/requests
Request:
GET /api/v1/v1/reviews/request-review/requests?filter=sent&page=1&limit=25&search=john
Query Parameters:
filter(string): Status filter - "sent", "opened", "clicked", "completed"page(number): Page numberlimit(number): Items per pagesearch(string): Search contact name/email
Response:
{
"success": true,
"data": [
{
"_id": "req_abc123",
"account_id": "acc_123",
"user_id": "user_456",
"type": "both",
"source": "reputation",
"receivers": ["contact_abc"],
"request_count": 1,
"status": "sent",
"sent_at": "2024-12-08T10:00:00Z",
"opened_at": null,
"clicked_at": null,
"completed_at": null
}
],
"pagination": {
"page": 1,
"limit": 25,
"total": 150,
"pages": 6
}
}
Status Definitions:
sent: Request sent successfullyopened: Recipient opened emailclicked: Recipient clicked review linkcompleted: Review submitted on platform
4. getRequestsFilters()
Get available filter options for requests list.
Endpoint: GET /reviews/request-review/filters
Response:
{
"success": true,
"data": {
"statuses": [
{ "value": "sent", "label": "Sent", "count": 150 },
{ "value": "opened", "label": "Opened", "count": 120 },
{ "value": "clicked", "label": "Clicked", "count": 75 },
{ "value": "completed", "label": "Completed", "count": 45 }
],
"types": [
{ "value": "email", "label": "Email", "count": 80 },
{ "value": "sms", "label": "SMS", "count": 50 },
{ "value": "both", "label": "Both", "count": 20 }
],
"sources": [
{ "value": "reputation", "label": "Manual", "count": 100 },
{ "value": "automated", "label": "Automated", "count": 50 }
]
}
}
5. updateTemplate()
Update the review request email/SMS template.
Endpoint: PUT /reviews/request-review/template
Request:
{
"subject": "We'd love your feedback!",
"email_body": "<p>Dear {{contact_name}},</p><p>Thank you for choosing {{business_name}}!</p>",
"sms_body": "Thanks {{contact_name}}! Please review us: {{review_link}}",
"from_name": "Acme Corp Team",
"from_email": "reviews@acmecorp.com"
}
Response:
{
"success": true,
"data": {
"_id": "template_abc",
"account_id": "acc_123",
"subject": "We'd love your feedback!",
"updated_at": "2024-12-08T15:00:00Z"
}
}
6. getTemplate()
Retrieve current template configuration.
Endpoint: GET /reviews/request-review/template
Response:
{
"success": true,
"data": {
"_id": "template_abc",
"account_id": "acc_123",
"subject": "We'd love your feedback!",
"email_body": "<p>Dear {{contact_name}}...</p>",
"sms_body": "Thanks! Review us: {{review_link}}",
"from_name": "Acme Corp Team",
"from_email": "reviews@acmecorp.com",
"created_at": "2024-12-01T10:00:00Z",
"updated_at": "2024-12-08T15:00:00Z"
}
}
Edge Cases & Error Handling
1. No Valid Contact IDs:
throw badRequest('No valid contact id is provided.');
2. Contacts Not Found:
throw notFound('No contact found with the provided ids');
3. Missing Email for Email Type:
errors.push(`Email not found for the contact ${contact.name}. This contact will be skipped.`);
4. Missing Phone for SMS Type:
errors.push(
`Phone number not found for the contact ${contact.name}. This contact will be skipped.`,
);
5. No Twilio Number:
throw notFound('No active phone number on the account');
6. Notification Service Error:
throw internalError(resp.data.message);
Authorization
Authentication: Required (JWT Bearer token)
Multi-Tenant Pattern:
const account_id = req.auth.account_id;
const user_id = req.auth.uid;
// Contacts must belong to same account
await Contact.find({ parent_account: account_id, _id: { $in: contact_ids } });
Integration Points
Dependencies:
- CRM Contacts: Fetch contact email/phone
- Review Config: Get platform review links
- Twilio Numbers: SMS sending capability
- Notification Service: Async email/SMS delivery
Triggers:
- Review Request Created: Fires notification jobs
- Link Clicked: Tracks engagement
- Review Submitted: Marks as completed
Important Notes
Delivery Methods
-
Email Only (
type: "email"):- Uses SendGrid/email service
- Includes all platform links
- HTML template support
- Track opens and clicks
-
SMS Only (
type: "sms"):- Uses Twilio
- Plain text format
- Character limit (160)
- Universal review page link
-
Both (
type: "both"):- Sends email + SMS
- Double engagement chance
- Higher response rate
- More costly
Review Link Generation
// Universal review page (all platforms)
const reviewPageUrl = `${domain}/${account_id}/review`;
// Platform-specific links from config
const googleLink = reviewConfig.google.url;
const facebookLink = reviewConfig.facebook.url;
Rate Limiting
- Max 100 contacts per request: Prevent spam
- Cooldown Period: 7 days between requests to same contact
- Daily Limit: 500 requests per account per day
Related Documentation
- Auto Review Request - Automated requests
- Config Controller - Platform links
- Response Templates - Template management
Version: 1.0
Last Updated: December 2024
Status: ✅ Production Ready