# Integration Guide for Yaltopia Backend This email service is designed to be called by your Yaltopia backend with the data you already have. No complex API integrations needed! ## 🚀 Quick Integration ### 1. Deploy the Email Service ```bash # Using Docker (recommended) docker-compose up -d # Or Node.js npm run build && npm start ``` ### 2. Configure Environment ```env RESEND_API_KEY=re_your_resend_api_key FROM_DOMAIN=yaltopia.com FROM_EMAIL=noreply@yaltopia.com ``` ### 3. Call from Your Backend ## 📧 Available Endpoints ### Send Event Invitation **Endpoint**: `POST /api/emails/invitation` **When to use**: When user registers for an event or you want to send event invitations ```javascript // Example from your Yaltopia backend async function sendEventInvitation(user, event) { const emailData = { to: user.email, eventName: event.name, dateTime: formatEventDateTime(event.startDate), location: event.location, ctaUrl: `https://yaltopia.com/events/${event.id}/rsvp`, ctaLabel: "RSVP Now", company: { name: "Yaltopia Ticket", logoUrl: "https://yaltopia.com/logo.png", primaryColor: "#f97316" } } const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/invitation`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) return response.json() } ``` ### Send Team Member Invitation **Endpoint**: `POST /api/emails/team-invitation` **When to use**: When inviting users to join a team ```javascript async function sendTeamInvitation(inviter, recipient, team, invitationToken) { const emailData = { to: recipient.email, recipientName: recipient.name, inviterName: inviter.name, teamName: team.name, invitationLink: `https://yaltopia.com/teams/join?token=${invitationToken}`, customMessage: "Join our team and let's build something amazing together!", company: { name: "Yaltopia Ticket", logoUrl: "https://yaltopia.com/logo.png", primaryColor: "#10b981" } } const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/team-invitation`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) return response.json() } ``` ### Send Invoice Share **Endpoint**: `POST /api/emails/invoice-share` **When to use**: When sharing invoices with clients or stakeholders ```javascript async function sendInvoiceShare(sender, recipient, invoice, shareToken) { const emailData = { to: recipient.email, recipientName: recipient.name, senderName: sender.name, invoiceNumber: invoice.number, customerName: invoice.customer.name, amount: invoice.totalAmount, currency: invoice.currency, status: invoice.status, // 'DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED' shareLink: `https://yaltopia.com/invoices/shared/${shareToken}`, expirationDate: "March 31, 2026", accessLimit: 5, customMessage: "Please review this invoice and let me know if you have any questions.", company: { name: "Yaltopia Ticket", logoUrl: "https://yaltopia.com/logo.png" } } const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/invoice-share`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) return response.json() } ``` ### Send Enhanced Payment Request **Endpoint**: `POST /api/emails/enhanced-payment-request` **When to use**: When requesting payment with detailed line items and automatic status updates ```javascript async function sendEnhancedPaymentRequest(customer, paymentRequest) { const emailData = { to: customer.email, recipientName: customer.name, paymentRequestNumber: paymentRequest.number, amount: paymentRequest.totalAmount, currency: paymentRequest.currency, description: paymentRequest.description, dueDate: formatDate(paymentRequest.dueDate), lineItems: paymentRequest.items.map(item => ({ description: item.description, quantity: item.quantity, unitPrice: item.unitPrice, total: item.quantity * item.unitPrice })), notes: "Please ensure payment is made by the due date to avoid late fees.", company: { name: "Yaltopia Ticket", bankDetails: { bankName: "Your Bank", accountName: "Yaltopia Ticket Ltd", accountNumber: "123456789", routingNumber: "021000021", referenceNote: `Payment for ${paymentRequest.number}` } } } const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/enhanced-payment-request`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) // Automatically update payment request status to "SENT" if (response.ok) { await updatePaymentRequestStatus(paymentRequest.id, 'SENT') } return response.json() } ``` ### Send Payment Request **Endpoint**: `POST /api/emails/payment-request` **When to use**: When payment is due for tickets or services ```javascript async function sendPaymentRequest(customer, ticket, event) { const emailData = { to: customer.email, amount: ticket.totalPrice, currency: ticket.currency || "USD", description: `Ticket for ${event.name}`, dueDate: formatDate(ticket.paymentDueDate), company: { name: "Yaltopia Ticket", paymentLink: `https://yaltopia.com/pay/${ticket.id}`, bankDetails: { bankName: "Your Bank", accountName: "Yaltopia Ticket Ltd", accountNumber: "123456789" } } } const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/payment-request`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) return response.json() } ``` ### Send Password Reset **Endpoint**: `POST /api/emails/password-reset` **When to use**: When user requests password reset ```javascript async function sendPasswordReset(user, resetToken) { const emailData = { to: user.email, resetLink: `https://yaltopia.com/reset-password?token=${resetToken}`, recipientName: user.firstName || user.name, company: { name: "Yaltopia Ticket", logoUrl: "https://yaltopia.com/logo.png" } } const response = await fetch(`${EMAIL_SERVICE_URL}/api/emails/password-reset`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) return response.json() } ``` ## 🔧 Integration Patterns ### 1. Event-Driven Integration ```javascript // In your event handlers eventEmitter.on('user.registered', async (user, event) => { await sendEventInvitation(user, event) }) eventEmitter.on('payment.due', async (customer, ticket, event) => { await sendPaymentRequest(customer, ticket, event) }) eventEmitter.on('password.reset.requested', async (user, resetToken) => { await sendPasswordReset(user, resetToken) }) ``` ### 2. Direct Integration ```javascript // In your API endpoints app.post('/api/events/:eventId/register', async (req, res) => { // Your registration logic const registration = await registerUserForEvent(userId, eventId) // Send invitation email await sendEventInvitation(user, event) res.json({ success: true, registration }) }) ``` ### 3. Background Job Integration ```javascript // Using a job queue (Bull, Agenda, etc.) queue.add('send-invitation-email', { userId: user.id, eventId: event.id }) queue.process('send-invitation-email', async (job) => { const { userId, eventId } = job.data const user = await getUserById(userId) const event = await getEventById(eventId) await sendEventInvitation(user, event) }) ``` ## 📊 Response Format All endpoints return consistent responses: **Success Response**: ```json { "success": true, "messageId": "abc123-def456", "duration": "1.2s" } ``` **Error Response**: ```json { "success": false, "error": "Validation error: Invalid email address", "code": "VALIDATION_ERROR" } ``` ## 🛡️ Error Handling ```javascript async function sendEmailSafely(emailData, endpoint) { try { const response = await fetch(`${EMAIL_SERVICE_URL}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) const result = await response.json() if (!result.success) { console.error('Email sending failed:', result.error) // Handle error (retry, log, notify admin, etc.) return false } console.log('Email sent successfully:', result.messageId) return true } catch (error) { console.error('Email service unreachable:', error) // Handle network errors return false } } ``` ## 🚀 Deployment Options ### Option 1: Same Server Deploy the email service on the same server as your Yaltopia backend: ```javascript const EMAIL_SERVICE_URL = 'http://localhost:3001' ``` ### Option 2: Separate Container Deploy as a separate Docker container: ```javascript const EMAIL_SERVICE_URL = 'http://email-service:3001' ``` ### Option 3: Cloud Service Deploy to a cloud platform: ```javascript const EMAIL_SERVICE_URL = 'https://your-email-service.herokuapp.com' ``` ## 🔍 Health Check Monitor the email service health: ```javascript async function checkEmailServiceHealth() { try { const response = await fetch(`${EMAIL_SERVICE_URL}/health`) const health = await response.json() return health.status === 'healthy' } catch (error) { return false } } ``` ## 📝 Best Practices 1. **Use environment variables** for the email service URL 2. **Implement retry logic** for failed email sends 3. **Log email sending attempts** for debugging 4. **Handle errors gracefully** - don't let email failures break your main flow 5. **Monitor email delivery** through Resend dashboard 6. **Test with real email addresses** in development ## 🎯 Example Integration Here's a complete example of integrating with your user registration flow: ```javascript // In your Yaltopia backend class EventService { constructor() { this.emailServiceUrl = process.env.EMAIL_SERVICE_URL || 'http://localhost:3001' } async registerUserForEvent(userId, eventId) { // Your existing registration logic const user = await User.findById(userId) const event = await Event.findById(eventId) const registration = await Registration.create({ userId, eventId }) // Send invitation email try { await this.sendInvitationEmail(user, event) } catch (error) { console.error('Failed to send invitation email:', error) // Don't fail the registration if email fails } return registration } async sendInvitationEmail(user, event) { const emailData = { to: user.email, eventName: event.name, dateTime: this.formatEventDateTime(event.startDate), location: event.location, ctaUrl: `https://yaltopia.com/events/${event.id}/rsvp`, company: { name: "Yaltopia Ticket", logoUrl: "https://yaltopia.com/logo.png" } } const response = await fetch(`${this.emailServiceUrl}/api/emails/invitation`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(emailData) }) if (!response.ok) { throw new Error(`Email service responded with ${response.status}`) } return response.json() } formatEventDateTime(startDate) { return new Date(startDate).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } } ``` This approach gives you beautiful, professional emails with minimal integration effort!