Common Use Cases
Practical examples and step-by-step guides for building real applications with the EvangelOS API. Learn through complete implementations of member directories, event systems, and communication workflows.
The EvangelOS API is powerful, but knowing which endpoints to call is only half the battle. This guide shows you how to combine multiple API calls into complete, real-world applications that solve actual church management problems.
🎯 What You'll Build
Each use case in this guide includes complete code examples, error handling, and best practices. Follow along to build:
Member Directory
A searchable directory with profiles, skills, and contact info
Event Registration
Complete event management with RSVPs and check-ins
Email Campaigns
Targeted communications with delivery tracking
AI-Powered Content
Generate personalized messages and event descriptions
🏠 Use Case 1: Building a Member Directory
The Scenario: You want to create a searchable online directory where church members can find each other, see skills/talents, and connect for ministry opportunities.
Step 1: Fetch and Display Members
First, let's get a list of all active members with their basic information:
// Fetch members with their skills included
const fetchMembers = async () => {
try {
const response = await fetch('/api/members?include=skills&status=active', {
credentials: 'include'
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data.members;
} catch (error) {
console.error('Failed to fetch members:', error);
return [];
}
};
// Display members in a directory format
const renderMemberDirectory = (members) => {
const directoryContainer = document.getElementById('member-directory');
members.forEach(member => {
const memberCard = `
<div class="member-card" data-member-id="${member.id}">
<div class="member-header">
<h3>${member.firstName} ${member.lastName}</h3>
<span class="member-type">${member.memberType}</span>
</div>
<div class="member-contact">
<p>📧 ${member.email}</p>
${member.phone ? `<p>📞 ${member.phone}</p>` : ''}
</div>
<div class="member-skills">
<h4>Skills & Talents:</h4>
${member.skills.map(skill =>
`<span class="skill-tag">${skill.name}</span>`
).join('')}
</div>
</div>
`;
directoryContainer.innerHTML += memberCard;
});
};Step 2: Add Search and Filtering
Now let's add search functionality to help people find specific members or skills:
// Search members by name or skill
const searchMembers = async (query) => {
const searchParams = new URLSearchParams({
include: 'skills',
search: query // Searches across names and emails
});
const response = await fetch(`/api/members?${searchParams}`, {
credentials: 'include'
});
return await response.json();
};
// Filter members by specific skills
const filterBySkills = async (skillNames) => {
const skillQuery = skillNames.join(',');
const response = await fetch(`/api/members?include=skills&skills=${skillQuery}`, {
credentials: 'include'
});
return await response.json();
};
// Interactive search implementation
document.getElementById('search-input').addEventListener('input', async (e) => {
const query = e.target.value;
if (query.length >= 3) {
const results = await searchMembers(query);
renderMemberDirectory(results.members);
}
});Expected API Response
{
"members": [
{
"id": "mem_12345",
"firstName": "Sarah",
"lastName": "Johnson",
"email": "[email protected]",
"phone": "+1-555-0123",
"memberType": "active_member",
"skills": [
{
"id": "skill_67890",
"name": "Piano",
"category": "Music",
"proficiencyLevel": "advanced"
},
{
"id": "skill_67891",
"name": "Children's Ministry",
"category": "Ministry",
"proficiencyLevel": "intermediate"
}
]
}
],
"pagination": {
"total": 247,
"page": 1,
"limit": 50
}
}
Pro TipUse the
include=skillsparameter to get member skills in a single request rather than making separate API calls for each member.
📅 Use Case 2: Event Registration System
The Scenario: Build a complete event management system where members can view upcoming events, RSVP, and church staff can track attendance.
Step 1: Display Upcoming Events
// Fetch upcoming events with RSVP counts
const fetchUpcomingEvents = async () => {
const today = new Date().toISOString().split('T')[0];
const response = await fetch(`/api/events?startDate=${today}&include=rsvps`, {
credentials: 'include'
});
const data = await response.json();
return data.events;
};
// Render event cards with RSVP functionality
const renderEvents = (events) => {
const eventsContainer = document.getElementById('events-list');
events.forEach(event => {
const attendeeCount = event.rsvps.filter(rsvp => rsvp.status === 'yes').length;
const eventDate = new Date(event.startDateTime).toLocaleDateString();
const eventTime = new Date(event.startDateTime).toLocaleTimeString();
const eventCard = `
<div class="event-card" data-event-id="${event.id}">
<div class="event-header">
<h3>${event.title}</h3>
<div class="event-meta">
<span class="event-date">📅 ${eventDate}</span>
<span class="event-time">🕐 ${eventTime}</span>
<span class="event-location">📍 ${event.location}</span>
</div>
</div>
<div class="event-description">
<p>${event.description}</p>
</div>
<div class="event-footer">
<span class="attendance-count">${attendeeCount} attending</span>
<button class="rsvp-button" onclick="handleRSVP('${event.id}', 'yes')">
RSVP Yes
</button>
<button class="rsvp-maybe" onclick="handleRSVP('${event.id}', 'maybe')">
Maybe
</button>
</div>
</div>
`;
eventsContainer.innerHTML += eventCard;
});
};Step 2: Handle RSVP Responses
// Create or update an RSVP
const handleRSVP = async (eventId, status) => {
try {
const response = await fetch(`/api/events/${eventId}/rsvps`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
status: status, // 'yes', 'no', or 'maybe'
notes: document.getElementById('rsvp-notes')?.value || ''
})
});
if (response.ok) {
const rsvp = await response.json();
showNotification(`RSVP updated to "${status}"`, 'success');
// Update the UI to reflect the new RSVP status
updateRSVPButton(eventId, status);
} else {
throw new Error(`Failed to RSVP: ${response.statusText}`);
}
} catch (error) {
console.error('RSVP Error:', error);
showNotification('Failed to update RSVP. Please try again.', 'error');
}
};
// Update button states after RSVP
const updateRSVPButton = (eventId, status) => {
const eventCard = document.querySelector(`[data-event-id="${eventId}"]`);
const buttons = eventCard.querySelectorAll('.rsvp-button, .rsvp-maybe');
buttons.forEach(button => {
button.classList.remove('active');
});
const activeButton = eventCard.querySelector(`[onclick*="'${status}'"]`);
activeButton.classList.add('active');
};Step 3: Event Check-In System
For staff managing events, add a check-in system:
// Get all RSVPs for an event (staff only)
const getEventRSVPs = async (eventId) => {
const response = await fetch(`/api/events/${eventId}/rsvps?include=member`, {
credentials: 'include'
});
return await response.json();
};
// Check in an attendee
const checkInAttendee = async (eventId, memberId) => {
const response = await fetch(`/api/events/${eventId}/rsvps/${memberId}/checkin`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
checkedIn: true,
checkInTime: new Date().toISOString()
})
});
if (response.ok) {
showNotification('Member checked in successfully', 'success');
}
};
// Render check-in interface (for staff)
const renderCheckInList = (rsvps) => {
const checkInContainer = document.getElementById('checkin-list');
rsvps.forEach(rsvp => {
const checkedInClass = rsvp.checkedIn ? 'checked-in' : '';
const checkInTime = rsvp.checkInTime ?
new Date(rsvp.checkInTime).toLocaleTimeString() : '';
const rsvpItem = `
<div class="rsvp-item ${checkedInClass}">
<span class="member-name">${rsvp.member.firstName} ${rsvp.member.lastName}</span>
<span class="rsvp-status status-${rsvp.status}">${rsvp.status}</span>
${rsvp.checkedIn ?
`<span class="checkin-time">Checked in at ${checkInTime}</span>` :
`<button onclick="checkInAttendee('${rsvp.eventId}', '${rsvp.memberId}')">
Check In
</button>`
}
</div>
`;
checkInContainer.innerHTML += rsvpItem;
});
};📧 Use Case 3: Email Campaign Management
The Scenario: Send targeted email communications to specific member groups and track their performance with analytics.
Step 1: Send Targeted Communications
// Send email to all active members
const sendWelcomeEmail = async () => {
const emailData = {
subject: "Welcome to Our Church Community!",
content: `
<h2>Welcome to First Church!</h2>
<p>We're thrilled to have you join our community...</p>
<a href="https://firstchurch.org/get-involved">Get Involved</a>
`,
recipientCriteria: {
memberType: 'active_member',
status: 'active'
},
templateData: {
churchName: "First Church",
pastorName: "Pastor John Smith"
}
};
const response = await fetch('/api/communications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(emailData)
});
if (response.ok) {
const communication = await response.json();
console.log(`Email sent to ${communication.recipientCount} members`);
return communication;
}
};Step 2: Track Email Performance
// Get analytics for a specific communication
const getEmailAnalytics = async (communicationId) => {
const response = await fetch(`/api/communications/${communicationId}/analytics`, {
credentials: 'include'
});
const analytics = await response.json();
return analytics;
};
// Display email performance dashboard
const renderEmailAnalytics = (analytics) => {
const analyticsContainer = document.getElementById('email-analytics');
const analyticsHTML = `
<div class="analytics-dashboard">
<div class="metric-card">
<h3>📧 Sent</h3>
<div class="metric-value">${analytics.totalSent}</div>
</div>
<div class="metric-card">
<h3>📬 Delivered</h3>
<div class="metric-value">${analytics.delivered}</div>
<div class="metric-percent">${(analytics.deliveryRate * 100).toFixed(1)}%</div>
</div>
<div class="metric-card">
<h3>👀 Opens</h3>
<div class="metric-value">${analytics.opens}</div>
<div class="metric-percent">${(analytics.openRate * 100).toFixed(1)}%</div>
</div>
<div class="metric-card">
<h3>🖱️ Clicks</h3>
<div class="metric-value">${analytics.clicks}</div>
<div class="metric-percent">${(analytics.clickRate * 100).toFixed(1)}%</div>
</div>
${analytics.bounces > 0 ? `
<div class="metric-card warning">
<h3>⚠️ Bounces</h3>
<div class="metric-value">${analytics.bounces}</div>
<div class="metric-note">Check email addresses</div>
</div>
` : ''}
</div>
<div class="analytics-details">
<h4>Performance Details</h4>
<p><strong>Subject Line:</strong> ${analytics.subject}</p>
<p><strong>Sent Date:</strong> ${new Date(analytics.sentAt).toLocaleString()}</p>
<p><strong>Average Open Time:</strong> ${analytics.averageOpenTime} hours after send</p>
${analytics.topClickedLinks?.length > 0 ? `
<div class="top-links">
<h5>Most Clicked Links:</h5>
<ul>
${analytics.topClickedLinks.map(link =>
`<li>${link.url} - ${link.clicks} clicks</li>`
).join('')}
</ul>
</div>
` : ''}
</div>
`;
analyticsContainer.innerHTML = analyticsHTML;
};
// Usage
const analytics = await getEmailAnalytics('comm_12345');
renderEmailAnalytics(analytics);Expected Analytics Response
{
"id": "comm_12345",
"subject": "Welcome to Our Church Community!",
"sentAt": "2024-12-03T14:30:00Z",
"totalSent": 247,
"delivered": 243,
"opens": 156,
"uniqueOpens": 142,
"clicks": 47,
"uniqueClicks": 43,
"bounces": 4,
"deliveryRate": 0.984,
"openRate": 0.584,
"clickRate": 0.303,
"averageOpenTime": 2.5,
"topClickedLinks": [
{
"url": "https://firstchurch.org/get-involved",
"clicks": 32
},
{
"url": "https://firstchurch.org/events",
"clicks": 15
}
]
}🤖 Use Case 4: AI-Powered Content Generation
The Scenario: Use EvangelOS's AI features to generate personalized welcome messages, event descriptions, and communication content.
Step 1: Generate Welcome Messages
// Generate personalized welcome message for new member
const generateWelcomeMessage = async (memberId, templateType = 'welcome') => {
const response = await fetch('/api/ai/generate-content', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
type: templateType,
targetMember: memberId,
parameters: {
tone: 'warm_and_welcoming',
length: 'medium',
includeNextSteps: true
}
})
});
const aiContent = await response.json();
return aiContent.generatedContent;
};
// Generate and send personalized welcome email
const sendAIWelcomeEmail = async (memberId) => {
try {
// Generate the content
const welcomeContent = await generateWelcomeMessage(memberId);
// Send the email
const emailData = {
subject: "Welcome to Our Church Family!",
content: welcomeContent,
recipients: [memberId],
personalized: true
};
const response = await fetch('/api/communications', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(emailData)
});
if (response.ok) {
console.log('AI-generated welcome email sent successfully');
}
} catch (error) {
console.error('Failed to send AI welcome email:', error);
}
};Step 2: Generate Event Descriptions
// Generate compelling event descriptions
const generateEventDescription = async (eventType, eventDetails) => {
const response = await fetch('/api/ai/generate-content', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
type: 'event_description',
parameters: {
eventType: eventType, // 'worship', 'fellowship', 'service', etc.
title: eventDetails.title,
date: eventDetails.date,
location: eventDetails.location,
targetAudience: eventDetails.audience, // 'all_ages', 'adults', 'youth'
tone: 'engaging_and_inviting',
includeCallToAction: true
}
})
});
const aiContent = await response.json();
return aiContent.generatedContent;
};
// Create event with AI-generated description
const createEventWithAI = async (eventData) => {
try {
// Generate description with AI
const aiDescription = await generateEventDescription(
eventData.type,
eventData
);
// Create the event
const eventResponse = await fetch('/api/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
title: eventData.title,
description: aiDescription,
startDateTime: eventData.startDateTime,
endDateTime: eventData.endDateTime,
location: eventData.location,
eventType: eventData.type
})
});
const newEvent = await eventResponse.json();
console.log('Event created with AI description:', newEvent.id);
return newEvent;
} catch (error) {
console.error('Failed to create event with AI:', error);
}
};
// Usage example
const eventData = {
title: "Christmas Community Potluck",
type: "fellowship",
date: "2024-12-20",
location: "Fellowship Hall",
startDateTime: "2024-12-20T18:00:00Z",
endDateTime: "2024-12-20T21:00:00Z",
audience: "all_ages"
};
const newEvent = await createEventWithAI(eventData);Step 3: Generate Communication Content
// Generate announcement content for various occasions
const generateAnnouncement = async (announcementType, details) => {
const contentTypes = {
'service_project': {
tone: 'inspiring_and_urgent',
focus: 'community_impact'
},
'fundraising': {
tone: 'encouraging_and_transparent',
focus: 'mission_and_need'
},
'new_ministry': {
tone: 'exciting_and_informative',
focus: 'opportunities_and_benefits'
},
'holiday_greeting': {
tone: 'warm_and_celebratory',
focus: 'community_and_gratitude'
}
};
const response = await fetch('/api/ai/generate-content', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
type: 'announcement',
parameters: {
announcementType: announcementType,
...contentTypes[announcementType],
details: details,
length: 'medium',
includeScripture: details.includeScripture || false
}
})
});
const aiContent = await response.json();
return aiContent.generatedContent;
};
// Example: Generate and send service project announcement
const announceServiceProject = async () => {
const projectDetails = {
title: "Holiday Food Drive",
goal: "Collect 500 meals for local families",
deadline: "December 15th",
dropoffLocation: "Church lobby",
includeScripture: true
};
const announcementContent = await generateAnnouncement(
'service_project',
projectDetails
);
// Send to all active members
const emailData = {
subject: "🎄 Holiday Food Drive - Help Us Serve Our Community",
content: announcementContent,
recipientCriteria: {
memberType: 'active_member',
status: 'active'
}
};
await fetch('/api/communications', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(emailData)
});
};🔧 Error Handling Best Practices
Every use case should include proper error handling:
// Centralized error handling for API calls
const makeAPIRequest = async (url, options = {}) => {
try {
const response = await fetch(url, {
credentials: 'include',
...options
});
// Handle different error types
if (response.status === 401) {
// Session expired
window.location.href = '/login';
return;
}
if (response.status === 403) {
throw new Error('You don\'t have permission for this action');
}
if (response.status === 422) {
const errorData = await response.json();
throw new Error(`Validation error: ${errorData.message}`);
}
if (!response.ok) {
throw new Error(`API error: ${response.status} ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('API Request failed:', error);
throw error;
}
};
// Show user-friendly error messages
const showNotification = (message, type = 'info') => {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 5000);
};🚀 Next Steps
Now you've seen how to build complete applications with the EvangelOS API! Here's what to explore next:
API Reference
Dive deeper into specific endpoints and parameters
Integration Guides
Connect with Planning Center, SendGrid, and social platforms
Advanced Patterns
Learn about pagination, rate limiting, and optimization
Authentication Details
Review session management and security best practices
💡 Development Tips
- Start Simple: Begin with basic member listings before adding complex filtering
- Test with Real Data: Use your actual church data to ensure your code handles real-world scenarios
- Handle Edge Cases: Empty member lists, events with no RSVPs, failed email sends
- Monitor Performance: Track email analytics to improve your communication strategy
- Stay Secure: Always validate user permissions before displaying sensitive data
RememberThese examples show complete workflows, but you can mix and match the patterns to build exactly what your church needs. The EvangelOS API is designed to be flexible and composable!
Ready to build something amazing? Pick a use case and start coding! 🚀
Updated 2 days ago
