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

12 KiB
Raw Blame History

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

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

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

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

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

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

Node Type: ActiveCampaign
Operation: Get Contact
Email: {{ $json.email }}

Create Contact

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

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

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

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

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:

// 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:

{
  "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:

// 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:

// 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:

// 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

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

{
  "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:

// 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:

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


Version History

  • v1.0 (2025-11-29): Initial configuration based on Sant Kala's tutorial
  • Document updated for VPS: ai-termimal