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:

  1. Channel Creation: Guardian Sarah creates delegated channel for Emma targeting Jake
  2. Guardian Approval: Guardian David approves channel invitation for Jake (GuardianFullyModerated)
  3. User Approval: Jake accepts the channel invitation (GuardianFullyModerated collaborative feature)
  4. Message Sending: Emma sends message to Jake (returns pendingMessageId)
  5. Outgoing Approval: Sarah approves Emma's outgoing message
  6. Incoming Approval: David approves Jake's incoming message
  7. 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:

  1. Channel Creation: Guardian Sarah creates delegated channel for Emma targeting Lily
  2. Immediate Access: Lily (Trusted) has immediate channel access (same guardian optimization)
  3. Emma → Lily Message: Emma sends message → Sarah approval required for outbound message → Delivered
  4. 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:

  1. Channel Creation: Jake (GuardianFullyModerated) initiates direct channel creation to chat with Lily
  2. Creation Approval: David approves Jake's channel creation request
  3. Invitation Delivery: System creates invitation for Lily (no guardian approval needed - she's Trusted)
  4. User Acceptance: Lily accepts channel invitation
  5. Jake → Lily Message: Jake sends message → David approves outbound message → Delivered
  6. 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 user
  • MESSAGE_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