Comprehensive Walkthroughs
This guide demonstrates complex real-world scenarios showing how different protection levels interact in various communication workflows. Each walkthrough includes detailed step-by-step implementations with actual API calls.
💡 Prerequisites: Familiarity with Core Concepts, Protected User Management, and Authentication is recommended before following these walkthroughs.
📚 For guardian management workflows, including protected user creation, sharing guardianship, and approval processes, see the Protected User Management Guide.
Overview
These walkthroughs cover:
- Cross-Guardian Communication: How different guardians coordinate approvals
- Protection Level Interactions: How GuardianFullyManaged, GuardianFullyModerated, and Trusted users communicate
- Approval Workflows: Sequential approval patterns and optimizations
- Dynamic Protection Changes: How protection level updates affect existing channels
Basic API Integration Patterns
Before diving into complex scenarios, let's understand the fundamental communication patterns in ChatMinder.
Channel-Based Communication Model
ChatMinder uses a streamlined channel creation approach that enables immediate conversation initiation with safety through member approval workflows.
Contacts are mutually exclusive from channels (not dependence on each other) and serve as a personal address book for assigning custom names (e.g. setting names to personal relationships such as Mum or Dad) to users.
Non-Protected User Communication Flow
This example shows the typical flow for a non-protected user:
// 1. Non-protected user creates direct channel
const channelResponse = await fetch('/api/channels/direct/target-user-id', {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
'Content-Type': 'application/json'
}
});
const { channelId } = await channelResponse.json();
// 2. Target user receives invitation based on their protection level
// - Non-protected/Trusted: Direct ChannelInvite they can accept/decline
// - GuardianFullyModerated/GuardianFullyManaged: ChannelInvite requiring guardian approval first
// 3. Send message - protection level determines approval workflow
const result = await fetch(`/api/messages/channel/${channelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ content: 'Hello!', messageType: 'text' })
});
const response = await result.json();
// Protection level combinations determine approval workflow
const { pendingMessageId, message } = response;
if (pendingMessageId) {
// Message requires guardian approval (most common case with protected users)
showPendingApprovalStatus(pendingMessageId);
// Guardian approval workflow
await fetch(`/api/guardian/pending-messages/${pendingMessageId}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${guardianToken}` }
});
} else {
// Immediate delivery (when both users have compatible protection levels)
showMessageSent(message);
}
Protected User Communication Flow
This example shows the typical flow for a protected user:
// 1. Protected user creates direct channel
const channelResponse = await fetch('/api/channels/direct/target-user-id', {
method: 'POST',
headers: {
'Authorization': `Bearer ${protectedUserToken}`,
'Content-Type': 'application/json'
}
});
const { channelId } = await channelResponse.json();
// 2. The protected user request to add a member to a direct channel must first be approved
// by their own guardian before the invite is sent to the target Guardian/User
// 3. Target user receives invitation based on their protection level
// - Non-protected/Trusted: Direct ChannelInvite they can accept/decline
// - GuardianFullyModerated/GuardianFullyManaged: ChannelInvite requiring guardian approval first
// 4. Send message - protection level determines approval workflow
const result = await fetch(`/api/messages/channel/${channelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${protectedUserToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ content: 'Hello!', messageType: 'text' })
});
const response = await result.json();
// Protection level combinations determine approval workflow
const { pendingMessageId, message } = response;
if (pendingMessageId) {
// Message requires guardian approval (most common case with protected users)
showPendingApprovalStatus(pendingMessageId);
// Guardian approval workflow
await fetch(`/api/guardian/pending-messages/${pendingMessageId}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${guardianToken}` }
});
} else {
// Immediate delivery (when both users have compatible protection levels)
showMessageSent(message);
}
Key Pattern Insights
- Channel Creation: Always succeeds immediately for the initiating user
- Invitation Flow: Target user's protection level determines approval requirements
- Message Approval: Depends on both sender's and recipient's protection levels
- Guardian Coordination: System automatically handles cross-guardian approvals
- Contacts Independence: Address book functionality doesn't gate communication
Walkthrough 1: Multi-Protection Level Cross-Guardian Communication
This walkthrough demonstrates how different protection levels create different approval requirements in cross-guardian scenarios.
📋 Scenario Setup
- Emma (GuardianFullyManaged, Guardian: Sarah)
- Jake (GuardianFullyModerated, Guardian: David)
- Lily (Trusted, Guardian: Sarah)
Part A: GuardianFullyManaged ↔ GuardianFullyModerated (Cross-Guardian)
Communication Flow:
- Channel Creation: Guardian Sarah creates delegated channel for Emma targeting Jake
- Guardian Approval: Guardian David approves channel invitation for Jake (GuardianFullyModerated)
- User Approval: Jake accepts the channel invitation (GuardianFullyModerated collaborative feature)
- Message Sending: Emma sends message to Jake (returns pendingMessageId)
- Outgoing Approval: Sarah approves Emma's outgoing message
- Incoming Approval: David approves Jake's incoming message
- Message Delivered: Message is now visible to Jake
Implementation
// Step 1: Guardian Sarah creates direct channel for Emma
const channelResponse = await fetch('/api/guardian/channels/create-direct', {
method: 'POST',
headers: {
'Authorization': `Bearer ${sarahToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
fromUserId: 'emma-user-id',
targetUserId: 'jake-user-id'
})
});
const { channelId } = await channelResponse.json();
// Step 2: Guardian David approves channel invitation for Jake
const davidPendingResponse = await fetch('/api/guardian/channels/pending', {
headers: { 'Authorization': `Bearer ${davidToken}` }
});
const davidPending = await davidPendingResponse.json();
const channelInvite = davidPending.channelInvites.find(invite =>
invite.channelId === channelId
);
await fetch(`/api/guardian/channels/invite/${channelInvite.id}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${davidToken}` }
});
// Step 3: Jake accepts channel invitation
const jakeInvitesResponse = await fetch('/api/channels/invites/pending', {
headers: { 'Authorization': `Bearer ${jakeToken}` }
});
const jakeInvites = await jakeInvitesResponse.json();
const jakeInvite = jakeInvites.find(invite => invite.channelId === channelId);
await fetch(`/api/channels/invites/${jakeInvite.id}/accept`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${jakeToken}` }
});
// Steps 4-7: Message approval workflow
const messageResponse = await fetch(`/api/messages/channel/${channelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${emmaToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ content: 'Hi Jake!', messageType: 'text' })
});
const { pendingMessageId } = await messageResponse.json();
// Step 5: Sarah approves Emma's outgoing message
await fetch(`/api/guardian/pending-messages/${pendingMessageId}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${sarahToken}` }
});
// Step 6: David approves Jake's incoming message (system automatically handles this step)
// Step 7: Message is now delivered to Jake
Part B: GuardianFullyManaged ↔ Trusted (Same Guardian)
Communication Flow:
- Channel Creation: Guardian Sarah creates delegated channel for Emma targeting Lily
- Immediate Access: Lily (Trusted) has immediate channel access (same guardian optimization)
- Emma → Lily Message: Emma sends message → Sarah approval required for outbound message → Delivered
- Lily → Emma Message: Lily sends message → Returns pendingMessageId (Needs approval from Sarah - Emma's Guardian) → Sarah approval → Delivered
Implementation
// Step 1 & 2: Channel creation with same guardian optimization
const sameGuardianChannelResponse = await fetch('/api/guardian/channels/create-direct', {
method: 'POST',
headers: {
'Authorization': `Bearer ${sarahToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
fromUserId: 'emma-user-id',
targetUserId: 'lily-user-id'
})
});
const { channelId: sameGuardianChannelId } = await sameGuardianChannelResponse.json();
// Both users have immediate access (no invitation workflow needed)
// Lily automatically gets channel access since Sarah manages both users
// Step 3: Emma → Lily Message
const emmaMessage = {
content: 'Hi Lily! How are you doing?',
messageType: 'text'
};
const emmaResponse = await fetch(`/api/messages/channel/${sameGuardianChannelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${emmaToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(emmaMessage)
});
const { pendingMessageId: emmaPending } = await emmaResponse.json();
// Sarah approves Emma's message
await fetch(`/api/guardian/pending-messages/${emmaPending}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${sarahToken}` }
});
// Step 4: Lily → Emma Message
const lilyMessage = {
content: 'Hi Emma! Great to hear from you!',
messageType: 'text'
};
const lilyResponse = await fetch(`/api/messages/channel/${sameGuardianChannelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${lilyToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(lilyMessage)
});
// Even though Lily is Trusted (outgoing approval automatic), the message still shows as pending
// because Emma (GuardianFullyManaged) needs approval to receive it
const { pendingMessageId: lilyPending } = await lilyResponse.json();
// Sarah must approve Lily's message for Emma to receive it
await fetch(`/api/guardian/pending-messages/${lilyPending}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${sarahToken}` }
});
// Verify both messages are delivered
const channelMessages = await fetch(`/api/messages/channel/${sameGuardianChannelId}`, {
headers: { 'Authorization': `Bearer ${lilyToken}` }
});
const messages = await channelMessages.json();
console.log(`Messages delivered: ${messages.length}`); // Should be 2
Part C: GuardianFullyModerated ↔ Trusted (Cross-Guardian)
Communication Flow:
- Channel Creation: Jake (GuardianFullyModerated) initiates direct channel creation to chat with Lily
- Creation Approval: David approves Jake's channel creation request
- Invitation Delivery: System creates invitation for Lily (no guardian approval needed - she's Trusted)
- User Acceptance: Lily accepts channel invitation
- Jake → Lily Message: Jake sends message → David approves outbound message → Delivered
- Lily → Jake Message: Lily sends → Returns pendingMessageId (Jake needs approval) → David approves inbound message → Delivered
Implementation
// Step 1: Jake creates direct channel (GuardianFullyModerated can initiate)
const jakeChannelResponse = await fetch(`/api/channels/direct/lily-user-id`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${jakeToken}`,
'Content-Type': 'application/json'
}
});
const { channelId: jakeChannelId } = await jakeChannelResponse.json();
// Step 2: David (Jake's guardian) must approve the channel creation first
const davidPendingResponse = await fetch('/api/guardian/channels/pending', {
headers: { 'Authorization': `Bearer ${davidToken}` }
});
const davidPending = await davidPendingResponse.json();
// David approves Jake's channel creation request
const channelCreationRequest = davidPending.addMemberToChannelRequests
.find(req => req.channelId === jakeChannelId);
await fetch(`/api/guardian/channels/add-member/${channelCreationRequest.id}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${davidToken}` }
});
// Step 3: Lily (Trusted) gets the invite directly and can accept it
const lilyInvitesResponse = await fetch('/api/channels/invites/pending', {
headers: { 'Authorization': `Bearer ${lilyToken}` }
});
const lilyInvites = await lilyInvitesResponse.json();
const lilyInvite = lilyInvites.find(invite => invite.channelId === jakeChannelId);
await fetch(`/api/channels/invites/${lilyInvite.id}/accept`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${lilyToken}` }
});
// Steps 5-6: Message approvals - both directions need David's approval for Jake
const jakeMessage = {
content: 'Hey Lily, want to study together?',
messageType: 'text'
};
const jakeMessageResponse = await fetch(`/api/messages/channel/${jakeChannelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${jakeToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(jakeMessage)
});
// Jake's outgoing message needs approval
const { pendingMessageId: jakePending } = await jakeMessageResponse.json();
await fetch(`/api/guardian/pending-messages/${jakePending}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${davidToken}` }
});
// Lily responds - her message also needs approval for Jake to receive
const lilyReplyResponse = await fetch(`/api/messages/channel/${jakeChannelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${lilyToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: "Absolutely! Let's meet at the library.",
messageType: 'text'
})
});
// Even Lily's message (from Trusted user) needs approval for Jake to receive
const { pendingMessageId: lilyReplyPending } = await lilyReplyResponse.json();
await fetch(`/api/guardian/pending-messages/${lilyReplyPending}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${davidToken}` }
});
🔄 Same Guardian Optimization
When the same guardian manages both users, the system provides optimizations to streamline workflows while maintaining protection level requirements.
Benefits:
- Immediate Channel Access: No complex invitation workflows
- Single Approval Point: One guardian handles all approvals
- Simplified Coordination: No cross-guardian communication needed
Example: Emma (GuardianFullyManaged) ↔ Lily (Trusted), both managed by Sarah
// Channel creation automatically grants access to both users
const optimizedChannel = await fetch('/api/guardian/channels/create-direct', {
method: 'POST',
headers: {
'Authorization': `Bearer ${sarahToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
fromUserId: 'emma-user-id',
targetUserId: 'lily-user-id'
})
});
// Both users immediately see channel in their channel list
// Messages still require approval based on Emma's GuardianFullyManaged status
// But only Sarah needs to approve (no cross-guardian coordination)
Walkthrough 2: Protection Level Conversion and Workflow Evolution
This walkthrough demonstrates how users can be converted to protected status and how protection level changes affect existing communication workflows.
📋 Scenario
- Alex (Regular User) → becomes protected user under Guardian Maria
- Sophie (GuardianFullyModerated) → protection level changed to Trusted
- Existing communication channels adapt to new protection requirements
User Conversion Process
Step 1: Guardian Initiates Conversion
const conversionResponse = await fetch('/api/user-conversion/request', {
method: 'POST',
headers: {
'Authorization': `Bearer ${mariaToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
targetUserId: 'alex-user-id',
protectionLevel: 'GuardianFullyModerated'
})
});
const { conversionCode, expiresAt } = await conversionResponse.json();
console.log(`Conversion code: ${conversionCode}`);
console.log(`Expires: ${expiresAt}`);
Step 2: User Accepts Conversion
const acceptResponse = await fetch('/api/user-conversion/accept', {
method: 'POST',
headers: {
'Authorization': `Bearer ${alexToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
guardianUserId: 'maria-user-id',
conversionCode: conversionCode,
confirmConversion: true
})
});
const { success, message } = await acceptResponse.json();
// Alex is now protected under Maria's guardianship
Protection Level Updates
Upgrading Protection Level (More Restrictive)
// Maria changes Sophie from GuardianFullyModerated to GuardianFullyManaged
await fetch('/api/protected-user/sophie-user-id', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${mariaToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: "Sophie Johnson",
protectionLevel: 'GuardianFullyManaged'
})
});
Downgrading Protection Level (Less Restrictive)
// Maria changes Sophie from GuardianFullyModerated to Trusted
await fetch('/api/protected-user/sophie-user-id', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${mariaToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: "Sophie Johnson",
protectionLevel: 'Trusted'
})
});
Dynamic Workflow Adaptation
Impact of Protection Level Changes:
| Change | Channel Behavior | Message Behavior | User Experience |
|---|---|---|---|
| Regular → GuardianFullyModerated | User now needs guardian approval for channel creation | All messages require guardian approval | Messages show "Pending approval" status |
| GuardianFullyManaged → Trusted | User gains immediate channel creation/acceptance | Messages send/receive immediately | No approval delays |
| GuardianFullyModerated → GuardianFullyManaged | Guardian must create all channels | Same approval workflow | User loses channel creation ability |
Existing Channel Adaptation:
// Existing channels continue working with updated approval workflows
// No re-invitation required - protection level changes apply immediately
// Example: After Sophie becomes Trusted, her messages in existing channels
// no longer require approval for outgoing messages
const immediateMessage = await fetch(`/api/messages/channel/${existingChannelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sophieToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'This message sends immediately now!',
messageType: 'text'
})
});
// No pendingMessageId returned - message delivers immediately
const { message } = await immediateMessage.json();
Walkthrough 3: Guardian Authority and Cross-Guardian Scenarios
This walkthrough explores guardian authority boundaries and complex cross-guardian communication patterns.
📋 Scenario
- Guardian Michael manages Sophie and Alex
- Guardian Sarah manages Emma and Jake
- Guardian David manages Lily
- Multiple cross-guardian communications with authority validation
Cross-Guardian Channel Creation
// Step 1: Michael creates channel for Sophie targeting Emma (Sarah's user)
const crossChannelResponse = await fetch('/api/guardian/channels/create-direct', {
method: 'POST',
headers: {
'Authorization': `Bearer ${michaelToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
fromUserId: 'sophie-user-id', // Michael's user
targetUserId: 'emma-user-id' // Sarah's user
})
});
const { channelId: crossChannelId } = await crossChannelResponse.json();
// Step 2: Sarah approves channel invitation for Emma
const sarahPendingResponse = await fetch('/api/guardian/channels/pending', {
headers: { 'Authorization': `Bearer ${sarahToken}` }
});
const sarahPending = await sarahPendingResponse.json();
const emmaInvite = sarahPending.channelInvites
.find(invite => invite.channelId === crossChannelId);
await fetch(`/api/guardian/channels/invite/${emmaInvite.id}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${sarahToken}` }
});
// Step 3: Emma accepts invitation (if GuardianFullyModerated)
// Step 4: Channel established between Sophie and Emma
Cross-Guardian Message Approval Sequence
// Sophie sends message to Emma - requires both guardians' approval
const sophieMessageResponse = await fetch(`/api/messages/channel/${crossChannelId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sophieToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Hi Emma! Nice to meet you!',
messageType: 'text'
})
});
const { pendingMessageId } = await sophieMessageResponse.json();
// Sequential approvals required:
// 1. Michael approves Sophie's outgoing message
await fetch(`/api/guardian/pending-messages/${pendingMessageId}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${michaelToken}` }
});
// 2. System automatically coordinates with Sarah for Emma's incoming approval
// 3. Sarah approves Emma's incoming message
// 4. Message delivered to Emma
Authority Boundary Enforcement
Guardian Scope Validation:
// ❌ This will fail - Michael cannot approve messages for Sarah's users
try {
await fetch(`/api/guardian/pending-messages/${emmaIncomingMessageId}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${michaelToken}` }
});
} catch (error) {
// Returns 403 Forbidden
console.error('Authority violation:', error.message);
// "UNAUTHORIZED_GUARDIAN_ACTION: Cannot approve messages for users not under your guardianship"
}
// ❌ This will fail - Sarah cannot approve messages for Michael's users
try {
await fetch(`/api/guardian/pending-messages/${sophieOutgoingMessageId}/approve`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${sarahToken}` }
});
} catch (error) {
// Returns 403 Forbidden
console.error('Authority violation:', error.message);
}
Key Authority Principles
- Guardian Scope: Each guardian can only manage their own protected users
- Sequential Approvals: Cross-guardian messages require both guardians' approval in sequence
- Authority Validation: System enforces strict guardian boundaries with 403 Forbidden responses
- Protection Level Enforcement: Each user's protection level applies regardless of who initiates communication
- Error Handling: Clear error codes for unauthorized operations
💡 Best Practices
Error Handling
For comprehensive guardian authority error handling, see the Error Handling Guide. Common scenarios include:
UNAUTHORIZED_GUARDIAN_ACTION: Guardian lacks authority for the userMESSAGE_ALREADY_PROCESSED: Message was already approved/rejected
Workflow Coordination
- Poll for Updates: Regularly check pending approvals
- Handle State Changes: Listen for SignalR events for real-time updates
- Graceful Degradation: Handle network failures and retry logic
- User Feedback: Provide clear status updates during approval workflows
Performance Considerations
- Batch Operations: Group multiple approvals when possible
- Caching: Cache protection level information to reduce API calls
- Optimistic UI: Show provisional states while approvals are pending
🔗 Related Documentation
- Core Concepts: Understanding protection levels and user types
- Protected User Management: Guardian relationships and supervision workflows
- Authentication Guide: Setting up secure API access
- Real-time Features: SignalR events for approval notifications
- Architecture Guide: Understanding the guardian authority system