Files
homelab-agents/n8n-skool/ai-termimal-skool-config-reference.md
Pete Marfleet d14832cfa6 Add Skool + n8n integration documentation and workflow template
Created comprehensive setup guide for syncing Skool community members to email lists via n8n. Includes browser token extraction guide, n8n workflow template, and configuration examples for multiple email platforms (ConvertKit, Mailchimp, ActiveCampaign, SendGrid).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 23:00:07 +00:00

497 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Skool + n8n Configuration Reference
**VPS**: ai-termimal
**Created**: 2025-11-29
## Quick Configuration Checklist
Use this as a quick reference when setting up or updating your workflow.
### Required Values to Extract from Browser
| Value | Where to Find | Used In | Notes |
|-------|--------------|---------|-------|
| Cookie | Network > request-bulk-action > Headers > Request Headers | Nodes 2, 3, 4 | Very long string, expires periodically |
| User-Agent | Network > request-bulk-action > Headers > Request Headers | Nodes 2, 3, 4 | Your browser identifier |
| X-AWS-WAF-Token | Network > request-bulk-action > Headers > Request Headers | Nodes 2, 3 | Security token, expires |
| Request Payload | Network > request-bulk-action > Payload | Node 2 | JSON body for export request |
| Expires | Network > Doc > Downloaded file URL | Node 4 | Query parameter for file download |
| Signature | Network > Doc > Downloaded file URL | Node 4 | Query parameter for file download |
| Key-Pair-Id | Network > Doc > Downloaded file URL | Node 4 | Query parameter for file download |
---
## Customization Points
### 1. Schedule Frequency
**Location**: Schedule Trigger Node
**Default**: Every 2 hours
**Customization**:
```
Hours: 1, 2, 4, 6, 12, 24
Minutes: 15, 30, 45, 60
```
**Decision Guide**:
- **Active community** (>10 joins/day): Every 1-2 hours
- **Medium activity** (3-10 joins/day): Every 4-6 hours
- **Low activity** (<3 joins/day): Every 12-24 hours
### 2. Member Limit
**Location**: Limit Node
**Default**: 15 members
**Formula**: `(Expected joins/hour × Hours between triggers) × 1.5`
**Examples**:
- If you get 5 members/day and run every 2 hours: `(5/12 × 2) × 1.5 ≈ 2` → Set to 5 (minimum safe)
- If you get 50 members/day and run every 2 hours: `(50/12 × 2) × 1.5 ≈ 12` → Set to 15
- If you get 200 members/day and run every 2 hours: `(200/12 × 2) × 1.5 ≈ 50` → Set to 50
**Why 1.5x multiplier?**: Safety buffer for spikes in signups
### 3. Email Platform Integration
This is the most variable part of the workflow. Here are examples for popular platforms:
---
## Email Platform Examples
### ConvertKit
#### Search for Subscriber
```json
Node Type: HTTP Request
Method: GET
URL: https://api.convertkit.com/v3/subscribers
Query Parameters:
- api_secret: YOUR_API_SECRET
- email_address: {{ $json.email }}
Response: Returns subscriber if exists, empty if not
```
#### Create Subscriber
```json
Node Type: HTTP Request
Method: POST
URL: https://api.convertkit.com/v3/forms/YOUR_FORM_ID/subscribe
Headers:
- Content-Type: application/json
Body:
{
"api_key": "YOUR_API_KEY",
"email": "{{ $json.email }}",
"first_name": "{{ $json.first_name }}",
"tags": ["Skool Member"]
}
```
#### Tag Subscriber
```json
Node Type: HTTP Request
Method: POST
URL: https://api.convertkit.com/v3/tags/YOUR_TAG_ID/subscribe
Body:
{
"api_secret": "YOUR_API_SECRET",
"email": "{{ $json.email }}"
}
```
---
### Mailchimp
#### Search for Subscriber
```json
Node Type: Mailchimp
Operation: Get Member
Audience ID: YOUR_AUDIENCE_ID
Email: {{ $json.email }}
If exists: Returns member info
If not exists: Returns error (handle with error workflow)
```
#### Create Subscriber
```json
Node Type: Mailchimp
Operation: Create or Update Member
Audience ID: YOUR_AUDIENCE_ID
Email: {{ $json.email }}
Status: subscribed
Merge Fields:
FNAME: {{ $json.first_name }}
LNAME: {{ $json.last_name }}
Tags: Skool Member, New Member
```
---
### ActiveCampaign
#### Search for Contact
```json
Node Type: ActiveCampaign
Operation: Get Contact
Email: {{ $json.email }}
```
#### Create Contact
```json
Node Type: ActiveCampaign
Operation: Create or Update Contact
Email: {{ $json.email }}
First Name: {{ $json.first_name }}
Last Name: {{ $json.last_name }}
Tags: Skool Member
Lists: YOUR_LIST_ID
```
---
### SendGrid
#### Search for Contact
```json
Node Type: HTTP Request
Method: POST
URL: https://api.sendgrid.com/v3/marketing/contacts/search
Headers:
- Authorization: Bearer YOUR_API_KEY
- Content-Type: application/json
Body:
{
"query": "email = '{{ $json.email }}'"
}
```
#### Create Contact
```json
Node Type: HTTP Request
Method: PUT
URL: https://api.sendgrid.com/v3/marketing/contacts
Headers:
- Authorization: Bearer YOUR_API_KEY
- Content-Type: application/json
Body:
{
"contacts": [
{
"email": "{{ $json.email }}",
"first_name": "{{ $json.first_name }}",
"last_name": "{{ $json.last_name }}"
}
]
}
```
---
### Generic HTTP Request Template
For any email platform with API access:
#### Search Pattern
```json
Method: GET or POST
URL: [PLATFORM_API_URL]/search or /subscribers or /contacts
Authentication: API Key / Bearer Token (check platform docs)
Query/Body: { "email": "{{ $json.email }}" }
```
#### Create Pattern
```json
Method: POST or PUT
URL: [PLATFORM_API_URL]/create or /subscribe or /add
Authentication: API Key / Bearer Token
Body:
{
"email": "{{ $json.email }}",
"first_name": "{{ $json.first_name }}",
"last_name": "{{ $json.last_name }}",
"tags": ["Skool Member"],
"custom_fields": { ... }
}
```
---
## CSV Field Mapping
The CSV export from Skool typically contains these fields (may vary):
| Skool Field | Description | Use In Email Platform |
|-------------|-------------|---------------------|
| email | Member's email | Primary identifier |
| first_name | First name | Personalization |
| last_name | Last name | Personalization |
| joined_at | Join timestamp | Segmentation |
| status | active/inactive | Filter |
| profile_url | Skool profile link | Custom field |
| member_id | Unique ID | Reference |
**Access in n8n**: `{{ $json.field_name }}`
**Example**:
- Email: `{{ $json.email }}`
- Name: `{{ $json.first_name }} {{ $json.last_name }}`
- Join Date: `{{ $json.joined_at }}`
---
## Advanced Customizations
### 1. Filter by Join Date
Add a filter node after CSV extraction to only process members who joined in the last X hours:
```javascript
// In a Function node
const hoursAgo = 2; // Match your schedule frequency
const cutoffTime = new Date(Date.now() - (hoursAgo * 60 * 60 * 1000));
const joinedAt = new Date($json.joined_at);
return joinedAt > cutoffTime;
```
### 2. Add Custom Fields
Map Skool data to custom fields in your email platform:
```json
{
"email": "{{ $json.email }}",
"custom_fields": {
"skool_join_date": "{{ $json.joined_at }}",
"skool_profile": "{{ $json.profile_url }}",
"skool_member_id": "{{ $json.member_id }}",
"source": "Skool Community"
}
}
```
### 3. Conditional Tagging
Tag members based on Skool data:
```javascript
// In a Function node before adding to email platform
const tags = ['Skool Member'];
// Check if joined recently (within 24 hours)
const joinedRecently = (Date.now() - new Date($json.joined_at)) < 86400000;
if (joinedRecently) {
tags.push('New Member');
}
// Add to return object
return {
...$json,
tags: tags
};
```
### 4. Segment by Activity Level
If your Skool export includes activity data:
```javascript
// Assuming there's an activity_score field
if ($json.activity_score > 50) {
tags.push('Highly Engaged');
} else if ($json.activity_score > 20) {
tags.push('Moderately Engaged');
} else {
tags.push('Low Engagement');
}
```
### 5. Welcome Email Sequences
After adding to email platform, trigger different sequences:
```javascript
// Based on member type or interests
if ($json.interests && $json.interests.includes('AI')) {
sequenceId = 'AI_WELCOME_SEQUENCE';
} else if ($json.interests && $json.interests.includes('Marketing')) {
sequenceId = 'MARKETING_WELCOME_SEQUENCE';
} else {
sequenceId = 'GENERAL_WELCOME_SEQUENCE';
}
```
---
## Error Handling
### Recommended Error Workflow
Add an "Error Trigger" node to handle failures:
1. **Error Trigger Node**: Catches any workflow errors
2. **IF Node**: Check error type
3. **Notification Node**: Send alert (email, Slack, Discord)
**Common Errors**:
- `401 Unauthorized`: Tokens expired → Re-extract from browser
- `404 Not Found`: URL changed → Check Skool API structure
- `429 Too Many Requests`: Rate limited → Reduce frequency
- `CSV Parse Error`: Format changed → Check CSV structure
### Error Notification Template
```json
{
"subject": "Skool Sync Error",
"message": "Workflow failed at {{ $json.node }}\nError: {{ $json.error.message }}\nTime: {{ $now }}"
}
```
---
## Performance Optimization
### Reduce n8n Executions
**Current Cost** (example):
- Runs every 2 hours = 12 executions/day
- Checks 15 members each = 180 checks/day
- With native integrations ≈ 200 operations/day
**Optimization**:
- Use native nodes when available (count as 1 operation vs HTTP = multiple)
- Increase schedule interval if acceptable latency
- Use conditional logic to skip unnecessary API calls
### Batch Processing
For high-volume communities, consider batching:
```javascript
// Instead of loop, batch add members
const newMembers = $input.all().map(item => ({
email: item.json.email,
first_name: item.json.first_name,
last_name: item.json.last_name
}));
// Send as array to email platform (if supported)
return [{ json: { contacts: newMembers } }];
```
---
## Security Best Practices
### 1. Use n8n Credentials
Don't hardcode tokens in nodes:
1. Go to **Credentials** in n8n
2. Create new credential for each service
3. Select credential in nodes instead of pasting values
### 2. Environment Variables
For self-hosted n8n, use environment variables:
```bash
export SKOOL_COOKIE="your_cookie"
export CONVERTKIT_API_KEY="your_key"
```
Access in workflow: `{{ $env.SKOOL_COOKIE }}`
### 3. Rotate Tokens Regularly
Set a reminder to refresh Skool tokens:
- Weekly for high-security requirements
- Bi-weekly for normal use
- When workflow fails (reactive)
### 4. Monitor for Unauthorized Access
Check your email platform for:
- Unusual subscription patterns
- Failed authentication attempts
- Unexpected data changes
---
## Testing Checklist
Before activating your workflow:
- [ ] Test Schedule Trigger manually
- [ ] Verify HTTP Request 1 returns `fileId`
- [ ] Verify HTTP Request 2 returns download URL
- [ ] Verify HTTP Request 3 downloads CSV
- [ ] Check CSV extraction produces member objects
- [ ] Confirm Limit node returns expected count
- [ ] Test email platform search (existing member)
- [ ] Test email platform create (new member)
- [ ] Verify tags are applied correctly
- [ ] Check welcome sequence triggers
- [ ] Test complete workflow end-to-end
- [ ] Monitor first scheduled execution
- [ ] Verify no duplicate additions after 24 hours
---
## Maintenance Schedule
### Weekly
- [ ] Check workflow execution history
- [ ] Verify no failed executions
- [ ] Spot-check email platform for new members
### Monthly
- [ ] Review member limit vs actual joins
- [ ] Optimize schedule frequency if needed
- [ ] Audit email platform for data accuracy
- [ ] Update tokens if experiencing auth issues
### Quarterly
- [ ] Review automation ROI (time saved)
- [ ] Consider additional automations
- [ ] Update documentation with learnings
---
## Troubleshooting Decision Tree
```
Workflow Failed?
├─ Authentication Error (401)
│ └─ Re-extract cookies and tokens
├─ No fileId returned
│ └─ Check request payload structure
├─ CSV download failed
│ └─ Re-extract download URL parameters
├─ No new members found
│ └─ Normal if no one joined (check Skool)
├─ Duplicates in email list
│ └─ Check IF node logic (should check existence)
└─ Email platform error
└─ Check API credentials and rate limits
```
---
## Support Resources
- **n8n Community**: https://community.n8n.io
- **n8n Documentation**: https://docs.n8n.io
- **Your Email Platform API Docs**: [Insert link]
- **Original Tutorial**: https://www.youtube.com/watch?v=vZaYYzb4y5Y
---
## Version History
- **v1.0** (2025-11-29): Initial configuration based on Sant Kala's tutorial
- Document updated for VPS: ai-termimal