Overview
The Notification API enables merchant admins to respond contextually to customer support tickets and questions. It implements a bidirectional notification system where:
- Customers trigger notifications (support requests, questions)
- Merchant admins receive campaign messages with instructions
- Admins respond via
AnswerNotificationToolwith structured answers - System sends localized responses back to customers
Architecture
The notification system consists of three main components:
1. Request Flow (Customer → Admin)
- Customer initiates a notification (via Voyager tools or direct triggers)
- System creates
Notificationrecord with answer schema - Campaign message sent to appropriate merchant admin(s)
- Assistant instruction message provides context and answer format
2. Answer Flow (Admin → Customer)
- Admin responds using
AnswerNotificationToolin their conversation - Answer validated against notification's JSON schema
- Response localized to customer's language using AI
- 24-hour policy determines delivery method
3. Delivery Methods
>24 Hours Since Last Customer Message (Campaign Template): - Uses WhatsApp-approved campaign templates - Populates template variables with localized response - Required due to WhatsApp 24-hour messaging window policy
≤24 Hours Since Last Customer Message (Voyager): - Uses Voyager to generate contextual response - Incorporates admin's answer while maintaining conversation tone - Sends as normal WhatsApp/Instagram message
Notification Types
user_question_request
Purpose: Handle customer questions that Cosmo cannot answer from existing knowledge base.
Triggered by: RequestNotificationTool when customer asks unanswerable question
Request Context:
{
"question": str, # The customer's question
"lead_phone": str, # Auto-populated
"document_id": int, # Auto-created Document ID
}
Answer Schema:
{
"answer": str # Admin's answer to the question
}
Campaigns Used:
- Request: merchant_user_question_request
- Answer: merchant_user_question_answer (>24h only)
Special Behavior:
- Creates a Document (category: UserQuestion) to store Q&A
- Document title = question, content = answer
- Document becomes part of merchant's knowledge base
user_support_request
Purpose: Route support tickets to appropriate merchant admins and enable contextual responses.
Triggered by: Customer requesting live agent or escalating an issue
Request Context:
{
"support_request_reason": str, # Why customer needs support
"support_request_type": str, # AI-classified type (Sales, Returns, etc.)
"lead_phone": str, # Auto-populated
}
Answer Schema:
{
"agent_response_to_customer": str, # Required response text
"resolution_notes": str, # Optional internal notes
}
Campaigns Used:
- Request: merchant_user_override_request
- Answer: customer_support_response (>24h only)
Special Behavior: - AI classification determines target merchant user type - Falls back to 'admin' type if no users found for classified type - Supports optional internal resolution notes (not sent to customer)
Code Flow Details
Request Notification Flow
- Validate Input
- Check notification type exists in
NOTIFICATION_CONFIG -
Verify all
requested_fieldspresent in context -
Type-Specific Processing
- Question: Create
Documentwith question as title -
Support: AI-classify to determine target merchant user type
-
Create Notification Record
- Store in database with
status=requested - Include
answer_model_json_schemafor validation -
Add
allow_multiple_answersflag -
Build Assistant Instruction
- Extract notification content from
requested_fields - Generate prefixed notification ID (e.g.,
NOTIF_123) - Include JSON schema and usage instructions
-
Store notification IDs in
other_data -
Send Campaign to Admins
- Route to
merchant_userorconversation_userper config - Create
ConversationUserrecords for merchant phone numbers if needed - Send via send_service_campaign_messages
-
Insert assistant instruction message per recipient
-
Persist Recipients
- Store
merchant_user_conversation_user_idsin notification context - Used later to notify admins when notification is closed
Answer Notification Flow
- Load Notification
- Fetch by
notification_idor create new answered notification -
Merge answer context into notification context
-
Validate Answer
- Convert
notification_typestring to Enum - Check all
answered_fieldspresent in context -
Enforce single-answer policy (if configured)
-
Type-Specific Processing
- Question: Update Document content with answer
-
Support: Extract admin response text
-
Localize Response
- AI rewrites admin answer for customer's language
- Generates
continuation_fragment(for templates) andfull_sentence(for Voyager) -
Uses GPT-4.1 Mini model
-
Determine Delivery Method
- Check last inbound
usermessage timestamp -
use_campaign = (now - last_user_message_time) > 24 hours -
Send Response to Customer
If use_campaign=True (>24h):
- Populate campaign variables with localized text
- Send via send_notification_messages
- Uses customer_support_response or merchant_user_question_answer campaign
If use_campaign=False (≤24h):
- Create assistant message with notification context + admin answer
- Build Voyager with conversation, merchant, user, billing_plan
- Generate response via voyager.navigate()
- Send directly via Twilio or Instagram (no template)
- Mark as Answered
- Update notification status to
answered -
Update
updated_attimestamp -
Notify Other Admins (Optional)
- If
allow_multiple_answers=False - Send closure message to other recipient admins
- Message: "Notification NOTIF_XXX has been answered and is now closed."
Integration with Other Modules
Dependencies
- Campaign: Sends notification campaigns to admins and customers
- Document: Stores questions and answers as knowledge base entries
- Merchant: Fetches merchant users by type for routing
- Conversation: Message storage and conversation management
- ML/Tools:
AnswerNotificationToolfor admin responses - AI Client: Support classification and response localization
Used By
- Voyager: Customer agent can trigger notifications via
RequestNotificationTool - Merchant Admins: Answer notifications via
AnswerNotificationToolin their conversations - SAQ Jobs: Async processing of notification requests and answers
Key Features
1. AI-Powered Support Classification
For user_support_request notifications:
- Reads merchant's support classification documents
- Determines appropriate merchant user type (sales, technical, returns, etc.)
- Falls back to 'admin' type if no match found
- Extracts readable support request type for display
2. Localization
All customer-facing responses are localized:
- Detects customer's language from conversation.language
- Rewrites admin answers professionally and concisely
- Generates two variants:
- Continuation fragment: Fits within campaign template variables
- Full sentence: Standalone response for direct messaging
3. 24-Hour Messaging Window
WhatsApp enforces a 24-hour window for non-template messages: - >24h: Must use approved campaign templates - ≤24h: Can send free-form messages via Voyager
System automatically determines which path to use based on last customer message timestamp.
4. Single-Answer Enforcement
Prevents duplicate responses:
- Each notification can only be answered once (by default)
- Other admins notified when someone answers
- Configurable via allow_multiple_answers flag
5. Notification ID Prefixing
Uses NOTIFICATION_PREFIX (defined in app/ml/prefix_utils.py):
- Prevents confusion with other prefixed entities (orders, products, etc.)
- Validated for correct format and int32 range
- Example: NOTIF_123
6. Template Response in Voyager Context
- Notification campaign messages (with
whatsapp_payload.type == "template") are now included in Voyager's message history - Provides context when merchant admin replies
- Helps Voyager determine when to use
AnswerNotificationTool
Configuration
All notification types are configured in NOTIFICATION_CONFIG dictionary:
NOTIFICATION_CONFIG = {
NotificationType.user_question_request: {
"requested_fields": ["question"],
"requested_campaign": CommerceDefaultCampaigns.merchant_user_question_request,
"requested_to": "merchant_user",
"answered_fields": ["answer"],
"answered_campaign": CommerceDefaultCampaigns.merchant_user_question_answer,
"answered_to": "conversation_user",
"answer_schema": UserQuestionAnswerSchema,
"allow_multiple_answers": False,
},
NotificationType.user_support_request: {
"requested_fields": ["support_request_reason"],
"requested_campaign": CommerceDefaultCampaigns.merchant_user_override_request,
"requested_to": "merchant_user",
"answered_fields": ["agent_response_to_customer"],
"answered_campaign": CommerceDefaultCampaigns.customer_support_response,
"answered_to": "conversation_user",
"answer_schema": UserSupportRequestAnswerSchema,
"allow_multiple_answers": False,
},
}
Error Handling
Validation Errors
At Request Time: - Missing required fields in context - Invalid notification type - Conversation not found
At Answer Time: - Notification not found - Missing answered fields - Notification already answered (if single-answer) - Document not found (for questions) - Invalid answer JSON format - Answer doesn't match JSON schema
Edge Cases
- No merchant users found for classified type: Falls back to 'admin' type
- No ConversationUser for merchant phone: Automatically created
- Empty message list in Voyager:
DBClient.db_update_rowsno-ops safely - Notification ID overflow: Validated to int32 range before querying
- String/Enum mismatch:
notification_typeconverted to Enum before use
Performance Considerations
Async Processing
- Notification requests/answers run in SAQ background jobs
- Prevents blocking customer-facing API calls
- Tool returns "processing" status (actual send happens async)
Database Queries
- Uses
DBClientto avoid greenlet/session issues - Explicit queries for relationships to prevent DetachedInstanceError
- Minimal queries per notification (3-4 total)
AI Usage
- Support classification: GPT-4.1 with 100 token limit
- Response localization: GPT-4.1 Mini for cost efficiency
- Cached results where possible via logging params
Usage Examples
Triggering a Question Notification
from app.api.notification.service import request_notification_job
from app.api.notification.models import NotificationType
await request_notification_job(
notification_type=NotificationType.user_question_request,
conversation_id=123,
context={"question": "What are your business hours?"}
)
Triggering a Support Request
await request_notification_job(
notification_type=NotificationType.user_support_request,
conversation_id=456,
context={"support_request_reason": "Product arrived damaged, need refund"}
)
Answering via Tool (Merchant Admin)
# In merchant admin conversation, Voyager calls:
AnswerNotificationTool(
notification_id="NOTIF_123",
answer_json='{"answer": "We are open Mon-Fri 9am-5pm EST"}'
)
Future Enhancements
- Streaming Responses: Stream Voyager responses as they're generated (≤24h path)
- Answer Templates: Pre-defined templates for common questions
- Auto-Close: Close notifications after configurable timeout
- Answer History: Track all answer attempts for analytics
- Priority Routing: Route urgent tickets to on-call admins first
- SLA Tracking: Monitor response times and SLA compliance
- Customer Satisfaction: Collect feedback on admin responses