CLAUDE: Add productivity tools with n8n workflow automation

- Add CONTEXT.md with ADHD-optimized task management patterns
- Add troubleshooting guide for productivity tools
- Add n8n workflow documentation including Ko-fi integration
- Document n8n at LXC 210 (10.10.0.210)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-12-07 00:48:28 -06:00
parent 8e74633ab3
commit c8dcf2b5ee
14 changed files with 5300 additions and 0 deletions

317
productivity/CONTEXT.md Normal file
View File

@ -0,0 +1,317 @@
# Productivity Tools - Technology Context
## Overview
ADHD-optimized task management system designed to break the context-switching spiral. The system uses a persistent terminal dashboard with zero-friction brain dump capture to help maintain focus while capturing interrupting thoughts.
## Architecture Patterns
### Single-Focus with Brain Dump Pattern
**Pattern**: One visible task at a time with instant thought capture
- **Current Focus**: Single task display with time tracking
- **Brain Dump**: Zero-friction capture without context switching
- **Auto-Progression**: System-managed task transitions
- **Priority Queue**: Automatic high→medium→low ordering
**When to Use**:
- ADHD workflow challenges
- Frequent context switching problems
- "Meant to do" task accumulation
- Need for always-visible task tracking
- Terminal-based work environments
### Design Principles
1. **Minimal Friction**: Two-second task capture without switching
2. **Always Visible**: Persistent terminal dashboard prevents forgetting
3. **Single Focus**: One task display reduces overwhelm
4. **Auto-Progression**: System picks next task, reducing decision fatigue
5. **Time Awareness**: Duration tracking without judgment
## Core Components
### Task Manager Core
**Purpose**: Central task management with priority queuing and state management
**Location**: `~/.claude/tools/task-manager/task_manager.py`
**Key Features**:
- Priority-based task ordering (High/Medium/Low)
- Task states: brain_dump, current, completed, snoozed
- Auto-progression on task completion
- Snooze functionality with time-based wake-up
- JSON-based persistence
**Data Model**:
```python
class Task:
id: str # Timestamp-based unique ID
description: str # Task description
priority: str # high, medium, low
status: str # brain_dump, current, completed, snoozed
created_at: str # ISO timestamp
started_at: Optional[str] # When task became current
completed_at: Optional[str] # Completion timestamp
snoozed_until: Optional[str] # Wake-up time for snoozed tasks
```
### Terminal Dashboard
**Purpose**: Persistent visual display of current focus and brain dump queue
**Location**: `~/.claude/tools/task-manager/dashboard.py`
**Display Components**:
- Current focus with elapsed time
- Brain dump queue (up to 8 tasks visible)
- Completion statistics
- Command reference
**Update Behavior**:
- Auto-refreshes every 1 second
- Reads from JSON file (detects CLI changes)
- Uses rich library for clean TUI rendering
### CLI Interface
**Purpose**: Zero-friction command-line task operations
**Location**: `~/.claude/tools/task-manager/cli.py`
**Core Commands**:
- `task dump "description"` - Brain dump capture (most important!)
- `task done` - Complete current, auto-load next
- `task next` - Skip to next task
- `task snooze [hours]` - Snooze current task
- `task add "description" --now` - Add and make current
- `task` - Show current status
**Priority Flags**:
- `--high` - High priority (🔴)
- `--medium` - Medium priority (🟡) [default]
- `--low` - Low priority (🟢)
## Implementation Patterns
### The Context-Switch Spiral Solution
**Problem Pattern**:
```
Working on Task A → Remember Task B → Switch to Task B →
Remember Task C → Task A forgotten → Task B becomes "meant to do" → Loop
```
**Solution Pattern**:
```
Working on Task A → Remember Task B → `task dump "Task B"`
Continue Task A → Complete with `task done` → System auto-loads Task B
```
### Brain Dump Workflow
1. **Capture**: `task dump "thought"` (2 seconds, no context switch)
2. **Visible**: Task appears in brain dump queue on dashboard
3. **Continue**: Keep working on current focus
4. **Auto-Load**: Task surfaces when current task completes
### Priority Management
**Automatic Ordering**:
1. High priority tasks (🔴) surface first
2. Medium priority tasks (🟡) second
3. Low priority tasks (🟢) last
4. Within priority level, oldest tasks first
**Use Cases**:
- `--high`: Urgent, time-sensitive, or blocking tasks
- `--medium`: Normal daily work (default)
- `--low`: "Nice to have", research, exploration
### Time Tracking Pattern
**Purpose**: Awareness without judgment
- Shows elapsed time on current task
- No alerts or pressure
- Helps identify time sinks naturally
- User decides when to move on
## Common Workflows
### Morning Startup
```bash
# Brain dump everything you need to do
task dump "Review PRs" --high
task dump "Update documentation"
task dump "Research new library" --low
# Load first task (highest priority)
task next
# Start dashboard in persistent pane
task-dashboard
```
### During Work Day
```bash
# Working on current task... suddenly remember something
task dump "email Staci about dinner"
# Thought is captured, visible in brain dump, keep working
# Finish current task
task done
# → Auto-loads next task from queue
```
### Handling Interruptions
```bash
# Urgent task comes up
task add "Fix production bug" --now --high
# → Immediately becomes current focus
# → Previous task moved back to brain dump
# Can't work on current task right now
task snooze 2
# → Snoozed for 2 hours
# → Next task auto-loaded
```
### End of Day
```bash
# Check what's left
task
# Everything in brain dump waits for tomorrow
# Completed tasks tracked in stats
```
## Best Practices
### Dashboard Setup
**Persistent Pane Strategy**:
- Use tmux/screen split pane for dashboard
- Keep visible on secondary monitor
- Always-visible = can't forget to check
- Alternative: Dedicated terminal window
**Terminal Multiplexer Example** (tmux):
```bash
# Split terminal horizontally
tmux split-window -h
# Run dashboard in right pane
task-dashboard
# Work in left pane
```
### Brain Dump Discipline
**Key Principles**:
- Capture thoughts immediately when they appear
- Don't evaluate or organize - just dump
- Trust the system to surface them later
- Priority can be added later if wrong
**Anti-Patterns to Avoid**:
- Stopping work to add detailed task information
- Trying to organize tasks while capturing
- Skipping capture because task seems "small"
- Context switching to "quickly do" the task
### Priority Usage
**High Priority** - Use sparingly:
- Urgent deadlines (today/this week)
- Blocking other people's work
- Production issues
- Time-sensitive opportunities
**Medium Priority** - Default for most work:
- Regular daily tasks
- Normal project work
- Scheduled activities
**Low Priority** - Background/optional:
- Research and exploration
- "Someday/maybe" items
- Nice-to-have improvements
### System Maintenance
**Daily**:
- Complete tasks as you work
- Brain dump new thoughts immediately
- Trust auto-progression
**Weekly**:
- Review low-priority items
- Clean up stale tasks
- Adjust priorities if needed
## Integration Patterns
### PATH Integration
**Location**: `~/.bashrc`
```bash
export PATH="$HOME/.claude/tools/task-manager:$PATH"
```
**Benefits**:
- `task` and `task-dashboard` available globally
- No full paths needed
- Muscle memory development
### Data Storage
**Location**: `~/.claude/tools/task-manager/tasks.json`
**Format**: JSON with task array and metadata
**Backup Strategy**:
- File-based storage enables easy backup
- Can sync via Dropbox/Google Drive if desired
- Simple JSON enables scripting/reporting
### Future Integration Points
**Planned Enhancements**:
- Notion MCP sync (Cal has integration available)
- Discord notifications on task completion
- Scheduled Jarvis check-ins (periodic reminders)
- Voice capture integration
- Pomodoro timer integration
- Analytics and reporting
## Technical Details
### File Structure
```
~/.claude/tools/task-manager/
├── task_manager.py # Core task management logic
├── dashboard.py # TUI dashboard (persistent display)
├── cli.py # Command-line interface
├── task # Shell wrapper for CLI
├── task-dashboard # Shell wrapper for dashboard
├── tasks.json # Data storage (auto-created)
└── README.md # User documentation
```
### Dependencies
- Python 3.7+
- `rich` library for TUI rendering
- No other external dependencies
### Installation
**Already Installed** - Cal's system:
- Scripts: `~/.claude/tools/task-manager/`
- PATH: Added to `~/.bashrc`
- Data: `~/.claude/tools/task-manager/tasks.json`
## ADHD-Specific Optimizations
### Why Traditional Systems Fail
1. **Hidden in apps** - Out of sight, out of mind
2. **Too much organization** - Analysis paralysis
3. **Too many features** - Overwhelm and abandonment
4. **No immediate feedback** - Delayed dopamine
5. **Context switching required** - Defeats purpose
### How This System Addresses ADHD
1. **Always visible** - Persistent terminal display
2. **Minimal organization** - Just dump and go
3. **Minimal features** - Only what's needed
4. **Immediate feedback** - Instant completion confirmation
5. **Zero context switch** - Capture without abandoning work
### The Psychology
**Reduced Anxiety**: Thoughts are captured and visible
**Maintained Focus**: No need to switch away from current work
**Reduced Overwhelm**: Single focus display, not full list
**Dopamine Feedback**: Immediate confirmation on completion
**Decision Reduction**: System picks next task automatically
This productivity system is optimized specifically for ADHD workflows and terminal-based work environments, providing the minimal viable structure needed to maintain focus while capturing interrupting thoughts.

408
productivity/n8n/CONTEXT.md Normal file
View File

@ -0,0 +1,408 @@
# n8n Workflow Automation Platform
Self-hosted workflow automation platform for building integrations and automating tasks across your homelab infrastructure.
## Overview
n8n is a fair-code licensed workflow automation tool that allows you to connect various services and automate complex processes through a visual workflow editor.
**Deployment Date:** 2025-11-13
**Version:** Latest (auto-updating via Docker)
**License:** Fair-code (free for self-hosted)
## Architecture
### Infrastructure
**LXC Container:**
- **VMID:** 210
- **Hostname:** docker-n8n-lxc
- **IP Address:** 10.10.0.210
- **CPU:** 4 cores
- **RAM:** 8GB
- **Disk:** 128GB
- **OS:** Ubuntu 20.04 LTS
**Services:**
```
┌─────────────────────────────────────┐
│ https://n8n.manticorum.com │
│ (Nginx Proxy Manager) │
└──────────────┬──────────────────────┘
│ SSL Termination
┌─────────────────────────────────────┐
│ LXC 210 (10.10.0.210) │
│ │
│ ┌──────────────┐ ┌─────────────┐ │
│ │ n8n │ │ PostgreSQL │ │
│ │ :5678 │→ │ :5432 │ │
│ │ │ │ │ │
│ │ - Workflows │ │ - Database │ │
│ │ - Webhooks │ │ - Creds │ │
│ │ - Executions │ │ - History │ │
│ └──────────────┘ └─────────────┘ │
└─────────────────────────────────────┘
```
### Data Persistence
**Docker Volumes:**
- `n8n_data` - Workflow files, credentials, settings
- `n8n_postgres_data` - PostgreSQL database
**Configuration:**
- `/opt/n8n/docker-compose.yml` - Container orchestration
- `/opt/n8n/.env` - Environment variables and secrets
## Access & Authentication
### External Access (Production)
```
URL: https://n8n.manticorum.com/
Username: admin
Password: [stored in .env file]
Protocol: HTTPS (via NPM with Let's Encrypt)
```
### Internal Access (Development/Testing)
```
URL: http://10.10.0.210:5678
Username: admin
Password: [same as above]
Protocol: HTTP (direct to container)
```
### SSH Access
```bash
# Using homelab SSH key
ssh -i ~/.ssh/homelab_rsa root@10.10.0.210
# Or with SSH config alias
ssh n8n
```
## Configuration
### Environment Variables
Key configuration settings in `/opt/n8n/.env`:
**Database:**
```bash
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=[secure-random-password]
```
**n8n Settings:**
```bash
N8N_HOST=n8n.manticorum.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.manticorum.com/
GENERIC_TIMEZONE=America/Los_Angeles
```
**Security:**
```bash
N8N_ENCRYPTION_KEY=[64-char-hex-key] # NEVER CHANGE AFTER FIRST RUN
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=[secure-random-password]
```
**Performance:**
```bash
NODE_ENV=production
EXECUTIONS_PROCESS=main
EXECUTIONS_MODE=regular
```
### Nginx Proxy Manager Configuration
**Proxy Host Settings:**
- **Domain:** n8n.manticorum.com
- **Scheme:** http
- **Forward Host:** 10.10.0.210
- **Forward Port:** 5678
- **Websockets:** ✅ Enabled (REQUIRED)
- **Block Common Exploits:** ✅ Enabled
- **SSL:** Let's Encrypt with HTTP/2 and HSTS
**Custom Nginx Config:**
```nginx
# Increase timeout for long-running workflows
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
# WebSocket support for real-time workflow updates
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Headers for n8n
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
```
## Operations
### Starting/Stopping Services
**Start n8n:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose up -d"
```
**Stop n8n:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down"
```
**Restart n8n (apply configuration changes):**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose restart n8n"
```
**View logs:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f"
# Just n8n logs
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"
# Just PostgreSQL logs
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f postgres"
```
**Check status:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose ps"
```
### Updates
**Update to latest n8n version:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose pull && docker compose up -d"
```
**Note:** n8n handles database migrations automatically on startup.
### Backups
**Database Backup:**
```bash
# Create timestamped backup
ssh root@10.10.0.210 "cd /opt/n8n && docker compose exec -T postgres pg_dump -U n8n n8n > /root/n8n-backup-\$(date +%Y%m%d-%H%M%S).sql"
# Download backup locally
scp -i ~/.ssh/homelab_rsa root@10.10.0.210:/root/n8n-backup-*.sql ~/backups/n8n/
```
**Full Backup (including volumes):**
```bash
ssh root@10.10.0.210 "
cd /opt/n8n
docker compose down
tar -czf /root/n8n-full-backup-\$(date +%Y%m%d-%H%M%S).tar.gz \
/opt/n8n \
/var/lib/docker/volumes/n8n_data \
/var/lib/docker/volumes/n8n_postgres_data
docker compose up -d
"
```
**Restore from Backup:**
```bash
# Restore database only
cat n8n-backup-20251113.sql | ssh root@10.10.0.210 "docker compose exec -T postgres psql -U n8n n8n"
# Full restore (requires downtime)
ssh root@10.10.0.210 "
cd /opt/n8n
docker compose down
tar -xzf /root/n8n-full-backup-20251113.tar.gz -C /
docker compose up -d
"
```
### Monitoring
**Health Check:**
```bash
# Check if n8n is responding
curl -s -o /dev/null -w "%{http_code}" http://10.10.0.210:5678
# Expected: 200
# Check container health
ssh root@10.10.0.210 "docker ps --filter name=n8n --format '{{.Status}}'"
```
**Resource Usage:**
```bash
ssh root@10.10.0.210 "docker stats --no-stream n8n n8n-postgres"
```
## Security Considerations
### Encryption Key
- **Critical:** The `N8N_ENCRYPTION_KEY` encrypts all stored credentials
- **Never change** this key after initial setup - it will make existing credentials unreadable
- **Backup securely** - store this key in a password manager or secrets vault
### Credentials Storage
- All workflow credentials are encrypted in the PostgreSQL database
- Credentials are never exposed in workflow definitions
- Use environment variables for sensitive data when possible
### Network Security
- Port 5678 exposed on container for internal access
- External access only via NPM (HTTPS with SSL)
- Consider using NPM's built-in access lists for IP whitelisting
### Authentication
- Basic auth enabled by default
- Consider integrating with SSO for multi-user scenarios
- Regularly rotate admin password
## Active Workflows
### Ko-fi → Paper Dynasty Integration
**Purpose:** Automated pack distribution when users purchase on Ko-fi
**Documentation:** `workflows/kofi-paper-dynasty.md`
**Workflow Features:**
- Receives Ko-fi shop order webhooks
- Multi-method user identification (discord_userid or team abbrev)
- Product mapping via custom variables
- Paper Dynasty API integration for pack grants
- Discord notifications for success/errors/manual review
- Comprehensive error handling and retry logic
**Webhook URLs:**
- Test: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
- Production: `https://n8n.manticorum.com/webhook/kofi-paperdy`
**Related Files:**
- `workflows/kofi-paper-dynasty.md` - Complete setup guide
- `workflows/kofi-product-mapping-template.json` - Product configuration template
- `workflows/kofi-testing-guide.md` - Testing procedures and sample payloads
**Credentials Required:**
- Ko-fi Verification Token
- Paper Dynasty API Key
- Discord Webhook URL
**Custom Variables:**
- `KOFI_PRODUCT_MAP` - JSON mapping of Ko-fi products to PD packs
---
## Integration Patterns
### Common Use Cases
**Homelab Automation:**
- Monitor system health and send alerts
- Backup automation workflows
- Service restart orchestration
- Log aggregation and analysis
**Infrastructure Integration:**
- Proxmox API automation
- Docker container management
- Network device configuration
- DNS record management
**Notification Workflows:**
- Discord/Slack alerts
- Email notifications
- SMS via Twilio
- Push notifications
**Data Processing:**
- API data collection and transformation
- Scheduled report generation
- Database sync operations
- File processing and organization
**E-commerce Integration:**
- Ko-fi payment processing (Paper Dynasty packs)
- Automated product fulfillment
- Customer notification workflows
- Transaction logging and analytics
### Webhook Configuration
**Webhook URLs follow this pattern:**
```
https://n8n.manticorum.com/webhook/{webhook-path}
https://n8n.manticorum.com/webhook-test/{webhook-path}
```
**Test webhooks before production:**
- Use `/webhook-test/` for development
- Switch to `/webhook/` for production
- Configure URL in n8n workflow webhook node
## Performance Tuning
### Resource Allocation
Current allocation is suitable for:
- Up to 50 concurrent workflow executions
- 100+ saved workflows
- Moderate webhook traffic (<100 req/min)
**If experiencing performance issues:**
- Increase LXC RAM allocation
- Add more CPU cores
- Monitor PostgreSQL query performance
- Consider workflow optimization
### Execution Modes
**Current:** `EXECUTIONS_MODE=regular` (main process)
**Alternative:** `EXECUTIONS_MODE=queue` (separate worker)
- Better for high-volume workflows
- Requires Redis
- More complex setup
## Troubleshooting
See [troubleshooting.md](troubleshooting.md) for common issues and solutions.
## Related Documentation
- [Troubleshooting Guide](troubleshooting.md) - Common issues and fixes
- [Examples](examples/) - Configuration examples and templates
- [Docker Context](/docker/CONTEXT.md) - Container infrastructure patterns
- [Networking Context](/networking/CONTEXT.md) - NPM and reverse proxy setup
## References
- **Official Documentation:** https://docs.n8n.io/
- **Community Forum:** https://community.n8n.io/
- **GitHub Repository:** https://github.com/n8n-io/n8n
- **Docker Hub:** https://hub.docker.com/r/n8nio/n8n
## Change Log
### 2025-11-13 - Initial Deployment
- Created LXC 210 (docker-n8n-lxc) with 4 cores, 8GB RAM, 128GB disk
- Deployed n8n with PostgreSQL 15 backend
- Configured domain: n8n.manticorum.com
- Integrated with Nginx Proxy Manager for HTTPS access
- Enabled basic authentication
- Set timezone to America/Los_Angeles

196
productivity/n8n/README.md Normal file
View File

@ -0,0 +1,196 @@
# n8n Workflow Automation - Quick Reference
Self-hosted n8n instance at **https://n8n.manticorum.com/**
## Quick Links
- **Web UI:** https://n8n.manticorum.com/
- **Documentation:** [CONTEXT.md](CONTEXT.md) - Complete technical documentation
- **Troubleshooting:** [troubleshooting.md](troubleshooting.md) - Common issues and solutions
- **Examples:** [examples/](examples/) - Configuration templates
## At a Glance
| Property | Value |
|----------|-------|
| **URL** | https://n8n.manticorum.com/ |
| **Internal IP** | 10.10.0.210 |
| **Container** | LXC 210 (docker-n8n-lxc) |
| **Resources** | 4 CPU, 8GB RAM, 128GB disk |
| **Database** | PostgreSQL 15 |
| **Deployment** | 2025-11-13 |
## Quick Commands
```bash
# SSH into n8n host
ssh -i ~/.ssh/homelab_rsa root@10.10.0.210
# View logs
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"
# Restart n8n
ssh root@10.10.0.210 "cd /opt/n8n && docker compose restart n8n"
# Backup database
ssh root@10.10.0.210 "cd /opt/n8n && docker compose exec -T postgres pg_dump -U n8n n8n > /root/n8n-backup-\$(date +%Y%m%d).sql"
# Update to latest version
ssh root@10.10.0.210 "cd /opt/n8n && docker compose pull && docker compose up -d"
# Check status
ssh root@10.10.0.210 "cd /opt/n8n && docker compose ps"
```
## File Locations
| Path | Description |
|------|-------------|
| `/opt/n8n/docker-compose.yml` | Container configuration |
| `/opt/n8n/.env` | Environment variables and secrets |
| `n8n_data` volume | Workflows, credentials, settings |
| `n8n_postgres_data` volume | PostgreSQL database |
## Access Credentials
**Location:** `/opt/n8n/.env` on LXC 210
View current credentials:
```bash
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep BASIC_AUTH"
```
## Common Tasks
### Create a New Workflow
1. Login to https://n8n.manticorum.com/
2. Click "Add workflow"
3. Add nodes by clicking "+"
4. Connect nodes by dragging
5. Test with "Execute Workflow"
6. Activate with toggle switch
### Setup a Webhook
1. Add "Webhook" node to workflow
2. Choose HTTP Method (GET, POST, etc.)
3. Set Path (e.g., `my-webhook`)
4. Activate workflow
5. Use URL: `https://n8n.manticorum.com/webhook/my-webhook`
### Backup Workflows
```bash
# Backup database (includes all workflows and credentials)
ssh root@10.10.0.210 "cd /opt/n8n && docker compose exec -T postgres pg_dump -U n8n n8n" > n8n-backup.sql
# Download locally
scp -i ~/.ssh/homelab_rsa root@10.10.0.210:/root/n8n-backup-*.sql ~/backups/n8n/
```
### View Execution History
1. Login to n8n UI
2. Click "Executions" in top nav
3. Filter by workflow, status, or date
4. Click execution to view details
## Architecture
```
Internet
Nginx Proxy Manager (NPM)
↓ HTTPS/SSL
https://n8n.manticorum.com
↓ HTTP (internal)
LXC 210 (10.10.0.210:5678)
├─ n8n Container
│ └─ Workflow Engine
└─ PostgreSQL Container
└─ Database (workflows, credentials, history)
```
## Integration Examples
### Discord Notifications
1. Webhook node → Discord Webhook URL
2. Message node → Format notification
3. Trigger: Manual, Schedule, or Webhook
### Homelab Monitoring
1. HTTP Request → Check service health
2. IF node → Check response code
3. Discord/Email → Send alert if down
4. Schedule Trigger → Run every 5 minutes
### Automated Backups
1. Execute Command → Run backup script
2. HTTP Request → Upload to cloud storage
3. Discord → Send completion notification
4. Schedule Trigger → Daily at 2 AM
### API Data Collection
1. HTTP Request → Fetch data from API
2. Set node → Transform data
3. PostgreSQL → Store in database
4. Schedule Trigger → Hourly
## Security Best Practices
- ✅ Basic authentication enabled
- ✅ HTTPS via NPM with Let's Encrypt
- ✅ Internal network only (no direct internet exposure)
- ✅ Credentials encrypted with N8N_ENCRYPTION_KEY
- ⚠️ Never share encryption key
- ⚠️ Regularly rotate admin password
- ⚠️ Review workflow permissions
## Troubleshooting Quick Checks
| Issue | Quick Check |
|-------|-------------|
| Can't access n8n.manticorum.com | `curl http://10.10.0.210:5678` |
| Login fails | Check `.env` file for correct credentials |
| Webhook not working | Verify workflow is **Active** (green toggle) |
| Slow performance | `ssh root@10.10.0.210 "docker stats n8n"` |
| Database errors | `ssh root@10.10.0.210 "docker compose logs postgres"` |
See [troubleshooting.md](troubleshooting.md) for detailed solutions.
## Resources
- **Official Docs:** https://docs.n8n.io/
- **Community Forum:** https://community.n8n.io/
- **Workflow Templates:** https://n8n.io/workflows/
- **Discord:** https://discord.gg/n8n
## Maintenance Schedule
**Weekly:**
- Review failed executions
- Check disk space
- Monitor container health
**Monthly:**
- Backup database
- Update n8n version
- Clean up old execution data
- Review and archive unused workflows
**Quarterly:**
- Test disaster recovery
- Audit credentials
- Review webhook security
- Performance optimization
## Support
For issues or questions:
1. Check [troubleshooting.md](troubleshooting.md)
2. Review [CONTEXT.md](CONTEXT.md) for configuration details
3. Search n8n community forum
4. Check n8n documentation
---
**Last Updated:** 2025-11-13
**Deployed By:** Claude Code automation
**Maintained By:** Cal Corum

View File

@ -0,0 +1,90 @@
version: '3.8'
# n8n Production Deployment with PostgreSQL
# Location: /opt/n8n/docker-compose.yml on LXC 210 (10.10.0.210)
#
# This is the complete docker-compose configuration for n8n.manticorum.com
# Updated: 2025-11-13
services:
postgres:
image: postgres:15-alpine
container_name: n8n-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
networks:
- n8n-network
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "5678:5678"
environment:
# Database Configuration
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
# n8n Configuration
- N8N_HOST=${N8N_HOST}
- N8N_PORT=5678
- N8N_PROTOCOL=${N8N_PROTOCOL}
- WEBHOOK_URL=${WEBHOOK_URL}
- GENERIC_TIMEZONE=${TIMEZONE}
# Security
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
# Performance
- NODE_ENV=production
- EXECUTIONS_PROCESS=main
- EXECUTIONS_MODE=regular
# Logging
- N8N_LOG_LEVEL=info
- N8N_LOG_OUTPUT=console
# Execution Data Management (Optional)
# Uncomment to enable automatic cleanup:
# - EXECUTIONS_DATA_PRUNE=true
# - EXECUTIONS_DATA_MAX_AGE=168 # Delete after 7 days
# - EXECUTIONS_DATA_SAVE_ON_ERROR=all
# - EXECUTIONS_DATA_SAVE_ON_SUCCESS=all
# - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true
volumes:
- n8n_data:/home/node/.n8n
networks:
- n8n-network
volumes:
postgres_data:
name: n8n_postgres_data
n8n_data:
name: n8n_data
networks:
n8n-network:
name: n8n-network
driver: bridge

View File

@ -0,0 +1,102 @@
# n8n Environment Configuration Template
# Copy this file to /opt/n8n/.env and customize values
#
# IMPORTANT: Generate secure random values for passwords and keys!
# Never commit .env files to version control!
# ============================================================================
# PostgreSQL Configuration
# ============================================================================
POSTGRES_USER=n8n
POSTGRES_PASSWORD=CHANGE_ME_GENERATE_RANDOM_PASSWORD
POSTGRES_DB=n8n
# Generate random password:
# openssl rand -base64 32 | tr -d '/+='
# ============================================================================
# n8n Configuration
# ============================================================================
# Your domain name (used for webhooks and external access)
N8N_HOST=n8n.manticorum.com
# Protocol (should be https if using NPM with SSL)
N8N_PROTOCOL=https
# Webhook URL (MUST match your public URL exactly)
# This is used for webhook callbacks from external services
WEBHOOK_URL=https://n8n.manticorum.com/
# Timezone for executions and cron jobs
# Find yours at: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TIMEZONE=America/Los_Angeles
# ============================================================================
# Security Configuration
# ============================================================================
# Encryption Key - CRITICAL!
# This encrypts all credentials stored in the database
# ⚠️ NEVER CHANGE THIS AFTER FIRST RUN - you'll lose access to encrypted credentials!
# ⚠️ BACKUP THIS KEY SECURELY - store in password manager
#
# Generate with: openssl rand -hex 32
N8N_ENCRYPTION_KEY=CHANGE_ME_GENERATE_64_CHAR_HEX
# Basic Authentication
# Protects your n8n instance from unauthorized access
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=CHANGE_ME_GENERATE_RANDOM_PASSWORD
# Generate random password:
# openssl rand -base64 16 | tr -d '/+='
# ============================================================================
# Optional: Execution Data Management
# ============================================================================
# Automatically delete old execution data to save disk space
# Uncomment and customize as needed:
# EXECUTIONS_DATA_PRUNE=true
# EXECUTIONS_DATA_MAX_AGE=168 # Delete executions older than 7 days (in hours)
# EXECUTIONS_DATA_SAVE_ON_ERROR=all # Save error data (all, none)
# EXECUTIONS_DATA_SAVE_ON_SUCCESS=all # Save success data (all, none)
# EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true # Save manually triggered executions
# ============================================================================
# Optional: Advanced Configuration
# ============================================================================
# Uncomment and customize as needed:
# Email Configuration (for alerts, sharing workflows, etc.)
# N8N_EMAIL_MODE=smtp
# N8N_SMTP_HOST=smtp.gmail.com
# N8N_SMTP_PORT=465
# N8N_SMTP_USER=your-email@gmail.com
# N8N_SMTP_PASS=your-app-password
# N8N_SMTP_SENDER=your-email@gmail.com
# N8N_SMTP_SSL=true
# External Hooks (for custom event handling)
# EXTERNAL_HOOK_FILES=/data/hooks/hooks.js
# Workflow Default Settings
# N8N_DEFAULT_BINARY_DATA_MODE=filesystem
# N8N_BINARY_DATA_TTL=60 # Keep binary data for 60 minutes
# Metrics (for monitoring)
# N8N_METRICS=true
# N8N_METRICS_PREFIX=n8n_
# ============================================================================
# Deployment Information
# ============================================================================
# Deployment Date: 2025-11-13
# LXC Container: 210 (docker-n8n-lxc)
# IP Address: 10.10.0.210
# Location: /opt/n8n/.env

View File

@ -0,0 +1,577 @@
# n8n Troubleshooting Guide
Common issues and solutions for the n8n deployment at n8n.manticorum.com.
## Quick Diagnostics
**First steps when n8n isn't working:**
```bash
# Check container status
ssh root@10.10.0.210 "docker ps --filter name=n8n"
# Check logs for errors
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs --tail=50 n8n"
# Check if service is responding
curl -I http://10.10.0.210:5678
# Check NPM proxy status (if external access fails)
# Access NPM UI and check proxy host status
```
---
## Container Issues
### n8n Container Won't Start
**Symptoms:**
- Container exits immediately after starting
- `docker ps` shows no n8n container
- Error in logs: "database connection failed"
**Diagnosis:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs n8n | tail -30"
```
**Solutions:**
1. **Check PostgreSQL is healthy:**
```bash
ssh root@10.10.0.210 "docker compose ps postgres"
# Status should show "healthy"
```
2. **Verify database credentials in .env:**
```bash
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep POSTGRES"
```
3. **Restart both services:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down && docker compose up -d"
```
4. **Check database connectivity:**
```bash
ssh root@10.10.0.210 "docker compose exec postgres psql -U n8n -d n8n -c 'SELECT 1;'"
```
### PostgreSQL Container Issues
**Symptoms:**
- n8n fails to connect to database
- PostgreSQL container shows "unhealthy" status
**Diagnosis:**
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs postgres | tail -50"
```
**Common Causes:**
1. **Corrupted database:**
```bash
# Check database integrity
ssh root@10.10.0.210 "docker compose exec postgres pg_isready -U n8n"
```
2. **Disk space full:**
```bash
ssh root@10.10.0.210 "df -h /"
# Should have >10GB free
```
3. **Permission issues:**
```bash
ssh root@10.10.0.210 "docker volume inspect n8n_postgres_data"
```
**Recovery:**
```bash
# Restore from backup
ssh root@10.10.0.210 "
cd /opt/n8n
docker compose down
docker volume rm n8n_postgres_data
docker compose up -d postgres
# Wait for healthy status
cat /root/n8n-backup-YYYYMMDD.sql | docker compose exec -T postgres psql -U n8n n8n
docker compose up -d n8n
"
```
---
## Access Issues
### Can't Access n8n.manticorum.com
**Symptoms:**
- Browser shows "Connection timed out" or "Can't reach this page"
- Works on http://10.10.0.210:5678 but not via domain
**Diagnosis Steps:**
1. **Check DNS resolution:**
```bash
nslookup n8n.manticorum.com
# Should return your public IP
```
2. **Test internal access:**
```bash
curl -I http://10.10.0.210:5678
# Should return HTTP 200
```
3. **Check NPM proxy host:**
- Login to NPM UI
- Verify proxy host for n8n.manticorum.com exists
- Check if status shows "online"
4. **Test NPM connectivity:**
```bash
# From NPM host
curl -I http://10.10.0.210:5678
```
**Solutions:**
1. **DNS not configured:**
- Add A record: `n8n.manticorum.com``[your-public-IP]`
- Wait for DNS propagation (up to 48 hours)
2. **NPM proxy host misconfigured:**
- Domain: `n8n.manticorum.com`
- Scheme: `http` (not https)
- Forward Host: `10.10.0.210`
- Forward Port: `5678`
- ✅ Enable WebSockets Support
3. **Firewall blocking:**
- Ensure ports 80 and 443 open on firewall
- Check Proxmox firewall rules
- Check LXC firewall if enabled
### SSL Certificate Issues
**Symptoms:**
- Browser shows "Your connection is not private"
- Certificate error in browser
- NPM shows "Certificate request failed"
**Diagnosis:**
```bash
# Test SSL
openssl s_client -connect n8n.manticorum.com:443 -servername n8n.manticorum.com
# Check certificate expiry
echo | openssl s_client -connect n8n.manticorum.com:443 2>/dev/null | openssl x509 -noout -dates
```
**Solutions:**
1. **Request new certificate in NPM:**
- Edit proxy host
- SSL tab → Request new SSL Certificate
- Ensure email is correct
- Check Let's Encrypt rate limits (5 per week)
2. **DNS validation failing:**
- Verify domain points to correct IP
- Ensure port 80 is accessible (Let's Encrypt uses HTTP validation)
3. **Use DNS challenge instead:**
- If port 80 is blocked, use DNS challenge method in NPM
- Requires API credentials for your DNS provider
### Login/Authentication Issues
**Symptoms:**
- Can access n8n but login fails
- "Invalid credentials" error
- Basic auth popup keeps appearing
**Diagnosis:**
```bash
# Check current credentials
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep BASIC_AUTH"
```
**Solutions:**
1. **Reset admin password:**
```bash
ssh root@10.10.0.210 "
cd /opt/n8n
# Generate new password
NEW_PASS=\$(openssl rand -base64 16 | tr -d '/+=')
echo \"New password: \$NEW_PASS\"
# Update .env
sed -i \"s/N8N_BASIC_AUTH_PASSWORD=.*/N8N_BASIC_AUTH_PASSWORD=\$NEW_PASS/\" .env
# Restart
docker compose restart n8n
"
```
2. **Clear browser cache:**
- Browser may cache old credentials
- Try incognito/private window
- Clear site data for n8n.manticorum.com
3. **Disable basic auth temporarily:**
```bash
ssh root@10.10.0.210 "
cd /opt/n8n
sed -i 's/N8N_BASIC_AUTH_ACTIVE=true/N8N_BASIC_AUTH_ACTIVE=false/' .env
docker compose restart n8n
"
```
**Warning:** Only do this for troubleshooting, re-enable immediately!
---
## Workflow Issues
### Webhooks Not Working
**Symptoms:**
- External services can't trigger workflows
- Webhook URL returns 404 or timeout
- Test webhooks work but production ones don't
**Diagnosis:**
```bash
# Test webhook URL
curl -X POST https://n8n.manticorum.com/webhook/test
# Check n8n logs for incoming requests
ssh root@10.10.0.210 "docker compose logs -f n8n | grep webhook"
```
**Common Causes:**
1. **Incorrect WEBHOOK_URL in configuration:**
```bash
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep WEBHOOK_URL"
# Should be: https://n8n.manticorum.com/
```
2. **Workflow not activated:**
- Check workflow is toggled "Active" in n8n UI
- Look for green indicator on workflow
3. **NPM WebSocket support not enabled:**
- Edit proxy host in NPM
- Details tab → ✅ WebSockets Support
4. **Firewall blocking webhooks:**
- Ensure external services can reach your public IP on port 443
**Solutions:**
```bash
# Update webhook URL
ssh root@10.10.0.210 "
cd /opt/n8n
sed -i 's|WEBHOOK_URL=.*|WEBHOOK_URL=https://n8n.manticorum.com/|' .env
docker compose restart n8n
"
# Test after restart
curl -X POST https://n8n.manticorum.com/webhook/test
```
### Executions Failing or Timing Out
**Symptoms:**
- Workflows start but never complete
- Timeout errors in execution logs
- Memory errors
**Diagnosis:**
```bash
# Check resource usage
ssh root@10.10.0.210 "docker stats --no-stream n8n"
# Check execution logs
# Access n8n UI → Executions → View failed execution
```
**Solutions:**
1. **Increase timeout in NPM:**
- NPM proxy host → Advanced tab
- Add: `proxy_read_timeout 300;`
2. **Increase LXC resources:**
```bash
# On Proxmox host
ssh root@10.10.0.11 "
pct set 210 --memory 16384 # Increase to 16GB
pct set 210 --cores 8 # Increase to 8 cores
pct reboot 210
"
```
3. **Optimize workflow:**
- Break large workflows into smaller ones
- Use pagination for API calls
- Add delay nodes between heavy operations
4. **Check external service timeouts:**
- API you're calling may be slow
- Increase timeout in HTTP Request nodes
### Database/Credential Issues
**Symptoms:**
- "Error loading credentials" in workflow
- Saved credentials not appearing
- "Credentials could not be decrypted"
**Critical Error - Encryption Key Changed:**
If you see "could not be decrypted," the encryption key was changed or is incorrect.
**This is UNRECOVERABLE without the original key!**
```bash
# Check current encryption key
ssh root@10.10.0.210 "cat /opt/n8n/.env | grep N8N_ENCRYPTION_KEY"
# If you have the old key, restore it:
ssh root@10.10.0.210 "
cd /opt/n8n
sed -i 's/N8N_ENCRYPTION_KEY=.*/N8N_ENCRYPTION_KEY=YOUR_OLD_KEY/' .env
docker compose restart n8n
"
```
**Prevention:**
- Backup `.env` file regularly
- Store encryption key in password manager
- Never regenerate encryption key after initial setup
---
## Performance Issues
### n8n Running Slow
**Symptoms:**
- UI sluggish or unresponsive
- Workflows take longer than expected
- High CPU/memory usage
**Diagnosis:**
```bash
# Check resource usage
ssh root@10.10.0.210 "
docker stats n8n n8n-postgres
df -h /
free -h
"
# Check PostgreSQL performance
ssh root@10.10.0.210 "
docker compose exec postgres psql -U n8n -d n8n -c '
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;'
"
```
**Solutions:**
1. **Clean up old executions:**
```bash
# In n8n UI: Settings → Executions
# Set: "Delete executions older than X days"
```
2. **Optimize database:**
```bash
ssh root@10.10.0.210 "
docker compose exec postgres psql -U n8n -d n8n -c 'VACUUM ANALYZE;'
"
```
3. **Increase LXC resources** (see above)
4. **Check disk I/O:**
```bash
ssh root@10.10.0.210 "iostat -x 1 5"
# If %util is consistently >80%, consider faster storage
```
### Database Growing Too Large
**Symptoms:**
- Disk space warning
- n8n slowing down over time
- Backup files becoming huge
**Diagnosis:**
```bash
# Check database size
ssh root@10.10.0.210 "
docker compose exec postgres psql -U n8n -d n8n -c '
SELECT pg_size_pretty(pg_database_size(current_database()));'
"
# Check table sizes
ssh root@10.10.0.210 "
docker compose exec postgres psql -U n8n -d n8n -c '
SELECT tablename, pg_size_pretty(pg_total_relation_size(tablename::text))
FROM pg_tables
WHERE schemaname = '\''public'\''
ORDER BY pg_total_relation_size(tablename::text) DESC;'
"
```
**Solutions:**
1. **Configure execution pruning:**
- Settings → Executions
- Enable: "Delete executions older than 7 days"
- Set: "Max execution data to save"
2. **Manual cleanup:**
```bash
ssh root@10.10.0.210 "
docker compose exec postgres psql -U n8n -d n8n -c '
DELETE FROM execution_entity
WHERE \"startedAt\" < NOW() - INTERVAL '\''30 days'\'';
VACUUM FULL;'
"
```
---
## Emergency Procedures
### Complete Service Restart
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down && docker compose up -d"
```
### Emergency Backup Before Changes
```bash
ssh root@10.10.0.210 "
cd /opt/n8n
# Create emergency backup
docker compose exec -T postgres pg_dump -U n8n n8n > /root/n8n-emergency-$(date +%Y%m%d-%H%M%S).sql
# Copy .env
cp .env /root/n8n-env-emergency-$(date +%Y%m%d-%H%M%S).env
"
```
### Complete Reset (DESTRUCTIVE)
**Only if all else fails and you're okay losing workflows:**
```bash
ssh root@10.10.0.210 "
cd /opt/n8n
docker compose down
docker volume rm n8n_data n8n_postgres_data
docker compose up -d
"
```
**Note:** This deletes everything. Restore from backup immediately after!
---
## Prevention & Monitoring
### Regular Maintenance
**Weekly:**
- Check disk space: `df -h /`
- Review failed executions in n8n UI
- Check log for errors: `docker compose logs --since 7d`
**Monthly:**
- Backup database and .env file
- Update n8n: `docker compose pull && docker compose up -d`
- Vacuum database: `VACUUM ANALYZE;`
- Review execution data retention settings
**Quarterly:**
- Test disaster recovery procedure
- Review and archive old workflows
- Audit credentials and remove unused ones
- Check for security updates
### Monitoring Setup
**Basic health check script:**
```bash
#!/bin/bash
# /opt/monitoring/check-n8n.sh
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://10.10.0.210:5678)
if [ "$STATUS" != "200" ]; then
echo "❌ n8n is down! Status: $STATUS"
# Send alert (Discord, email, etc.)
else
echo "✅ n8n is healthy"
fi
```
**Add to cron:**
```bash
*/5 * * * * /opt/monitoring/check-n8n.sh >> /var/log/n8n-health.log 2>&1
```
---
## Getting Help
### Log Collection for Support
```bash
# Collect all relevant logs
ssh root@10.10.0.210 "
cd /opt/n8n
mkdir -p /tmp/n8n-debug
docker compose logs --tail=200 > /tmp/n8n-debug/docker-logs.txt
docker compose ps > /tmp/n8n-debug/container-status.txt
cat .env | sed 's/PASSWORD=.*/PASSWORD=***/' > /tmp/n8n-debug/env-redacted.txt
df -h > /tmp/n8n-debug/disk-space.txt
free -h > /tmp/n8n-debug/memory.txt
docker stats --no-stream > /tmp/n8n-debug/container-stats.txt
tar -czf /root/n8n-debug-$(date +%Y%m%d-%H%M%S).tar.gz /tmp/n8n-debug/
"
```
### Resources
- **n8n Community Forum:** https://community.n8n.io/
- **Official Docs:** https://docs.n8n.io/
- **GitHub Issues:** https://github.com/n8n-io/n8n/issues
- **Discord:** https://discord.gg/n8n
### When to Escalate
Escalate to n8n community/support if:
- Database corruption suspected
- Consistent crashes with no clear cause
- Performance issues persist after optimization
- Security concerns
- Bug suspected in n8n itself
Always provide:
- n8n version: `docker inspect n8n | grep Image`
- Error messages from logs
- Steps to reproduce
- What you've already tried

View File

@ -0,0 +1,322 @@
# Ko-fi → Paper Dynasty Quick Start Guide
Get the Ko-fi integration running in 30 minutes.
## Prerequisites Checklist
Before you start, gather these items:
- [ ] Ko-fi verification token (from Ko-fi dashboard)
- [ ] Paper Dynasty API key (production)
- [ ] Discord webhook URL for notifications
- [ ] List of Ko-fi product codes you'll sell
- [ ] Access to n8n at https://n8n.manticorum.com
## Setup Steps
### Step 1: Get Ko-fi Verification Token (2 minutes)
1. Login to Ko-fi: https://ko-fi.com/
2. Navigate to: Settings → API → Webhooks → Advanced (dropdown)
3. Copy the **Verification Token** (looks like: `8c2a3835-4544-4f27-a53a-adfd1fcc5664`)
4. Save it securely - you'll add it to n8n next
### Step 2: Create Discord Webhook (2 minutes)
1. Open Discord channel where you want notifications
2. Channel Settings → Integrations → Webhooks → New Webhook
3. Name it: "Ko-fi Paper Dynasty"
4. Copy webhook URL (looks like: `https://discord.com/api/webhooks/...`)
5. Save URL - you'll add it to n8n next
### Step 3: Configure n8n Credentials (5 minutes)
Login to n8n and create three credentials:
**Credential 1: Ko-fi Verification Token**
1. n8n → Settings → Credentials → Add Credential
2. Search for "Generic Credential"
3. Name: `Ko-fi Verification Token`
4. Add field: `verification_token`
5. Value: `[paste your Ko-fi token here]`
6. Save
**Credential 2: Paper Dynasty API**
1. Add Credential → Search for "Header Auth"
2. Name: `Paper Dynasty API`
3. Header Name: `Authorization`
4. Value: `Bearer [your-pd-api-key]`
5. Save
**Credential 3: Discord Webhook**
1. Add Credential → Search for "Discord Webhook"
2. Name: `Discord Ko-fi Notifications`
3. Webhook URL: `[paste Discord webhook URL]`
4. Save
### Step 4: Create Product Mapping (5 minutes)
1. n8n → Settings → Variables → Add Variable
2. Variable Name: `KOFI_PRODUCT_MAP`
3. Value: Copy and customize this JSON:
```json
{
"pack-standard-5": {
"name": "5-Pack Standard",
"pack_type_id": 1,
"quantity": 5,
"pack_cardset_id": null
},
"pack-premium-10": {
"name": "10-Pack Premium",
"pack_type_id": 3,
"quantity": 10,
"pack_cardset_id": null
}
}
```
**Important:** Replace `pack-standard-5` and `pack-premium-10` with your actual Ko-fi product codes.
4. Save variable
### Step 5: Import Workflow (10 minutes)
**Option A: Manual Creation**
Follow the detailed node-by-node instructions in `kofi-paper-dynasty.md`
**Option B: JSON Import (if workflow JSON provided)**
1. n8n → Workflows → Add Workflow → Import from File
2. Select workflow JSON file
3. Review imported nodes
4. Update credential references if needed
5. Activate workflow
**Key Nodes to Verify:**
- Webhook path: `/kofi-paperdy` (use `/webhook-test/kofi-paperdy` initially)
- Token validation references correct credential
- Product mapping loads from variable
- API endpoints correct (`https://pd.manticorum.com/api/v2/`)
- Discord notifications reference correct credential
### Step 6: Test the Workflow (5 minutes)
**Quick Test with cURL:**
```bash
# Replace YOUR_KOFI_TOKEN_HERE with your actual token
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "quick-test-001",
"timestamp": "2025-11-13T12:00:00Z",
"type": "Shop Order",
"from_name": "Test User",
"message": "Team: SKB",
"amount": "5.00",
"email": "[email protected]",
"currency": "USD",
"kofi_transaction_id": "quick-test-001",
"shop_items": [
{
"direct_link_code": "pack-standard-5",
"variation_name": "5-Pack Standard",
"quantity": 1
}
],
"discord_userid": null
}'
```
**Expected Results:**
- ✅ n8n shows successful execution
- ✅ Discord notification received
- ✅ If using real API, 5 packs granted to team SKB
### Step 7: Configure Ko-fi Webhook (3 minutes)
1. Ko-fi → Settings → API → Webhooks
2. **Webhook URL:** `https://n8n.manticorum.com/webhook-test/kofi-paperdy` (test first!)
3. Save
4. Click "Test Webhook" button
5. Verify n8n receives webhook and processes successfully
6. Check Discord for test notification
### Step 8: Go Live (2 minutes)
Once testing is successful:
1. **Update workflow webhook path:**
- n8n → Edit Workflow → Webhook node
- Change path from `webhook-test/kofi-paperdy` to `webhook/kofi-paperdy`
- Save and activate workflow
2. **Update Ko-fi webhook URL:**
- Ko-fi → Settings → API → Webhooks
- Change URL to: `https://n8n.manticorum.com/webhook/kofi-paperdy`
- Save
3. **Monitor first transactions:**
- Watch Discord channel closely
- Review n8n execution logs
- Verify packs granted correctly in Paper Dynasty
Done! Your Ko-fi integration is now live.
---
## Post-Setup Tasks
### Create Ko-fi Products
For each pack type you want to sell:
1. Ko-fi → Shop → Add Product
2. **Product Name:** "5-Pack Standard Bundle"
3. **Price:** $5.00
4. **Description:** "Five standard Paper Dynasty packs"
5. **Advanced Settings → Product Code:** `pack-standard-5`
- ⚠️ This MUST match your product mapping!
6. Add product image
7. Publish product
**Repeat for each pack type** (premium, team choice, etc.)
### Document Your Product Codes
Keep a master list of all Ko-fi product codes and their mappings:
| Ko-fi Code | Pack Type | Quantity | Price | PD pack_type_id |
|------------|-----------|----------|-------|-----------------|
| pack-standard-5 | Standard | 5 | $5.00 | 1 |
| pack-standard-10 | Standard | 10 | $9.00 | 1 |
| pack-premium-5 | Premium | 5 | $10.00 | 3 |
| pack-premium-10 | Premium | 10 | $18.00 | 3 |
| pack-team-choice | Team Choice | 1 | $3.00 | 8 |
Update this when adding new products!
### Set Up Monitoring
**Create a monitoring checklist:**
- [ ] Check Discord notifications daily
- [ ] Review n8n execution logs weekly
- [ ] Verify pack distribution accuracy weekly
- [ ] Update product mapping when adding Ko-fi products
- [ ] Test disaster recovery monthly
### Backup n8n
**Create weekly backup:**
```bash
ssh root@10.10.0.210 "
cd /opt/n8n && \
docker compose exec -T postgres pg_dump -U n8n n8n > \
/root/n8n-backup-\$(date +%Y%m%d-%H%M%S).sql
"
# Download backup
scp root@10.10.0.210:/root/n8n-backup-*.sql ~/backups/n8n/
```
---
## Troubleshooting
### Webhook Not Receiving Data
**Check:**
```bash
# Test webhook endpoint
curl -I https://n8n.manticorum.com/webhook-test/kofi-paperdy
# Should return: 200 OK or 400 Bad Request (not 404)
```
**Fix:**
- Ensure workflow is Active (toggle in n8n UI)
- Verify webhook path matches Ko-fi configuration
- Check n8n logs: `ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"`
### Token Validation Failing
**Check:**
- Token in n8n credential matches Ko-fi exactly (no spaces!)
- Credential name in IF node: `{{ $credentials.kofiVerificationToken.verification_token }}`
**Fix:**
- Re-copy token from Ko-fi
- Update n8n credential
- Test again
### Team Not Found
**Check:**
- User has discord_userid linked in Paper Dynasty
- OR user included team abbrev in message (e.g., "Team: SKB")
- Team abbreviation exists in PD database
**Fix:**
- Manual review Discord notification will be sent
- Admin grants packs manually using transaction ID
### Packs Not Granted
**Check:**
- n8n execution log shows 200 OK from PD API
- Product code exists in `KOFI_PRODUCT_MAP` variable
- pack_type_id is valid
**Verify in Paper Dynasty:**
```python
from api_client import PaperDynastyAPI
api = PaperDynastyAPI(environment='prod')
api.list_packs(team_id=69, opened=False, limit=20)
```
---
## Next Steps
Now that your integration is running:
1. **Read full documentation:** `kofi-paper-dynasty.md`
2. **Review test scenarios:** `kofi-testing-guide.md`
3. **Customize product mapping:** Add more pack types
4. **Set up monitoring alerts:** Create health check workflow
5. **Document your process:** Add notes for future reference
---
## Support Resources
**Documentation:**
- Full Setup: `kofi-paper-dynasty.md`
- Testing Guide: `kofi-testing-guide.md`
- Product Mapping: `kofi-product-mapping-template.json`
- n8n Context: `/productivity/n8n/CONTEXT.md`
**External Resources:**
- Ko-fi Webhook Docs: https://help.ko-fi.com/hc/en-us/articles/360004162298
- n8n Documentation: https://docs.n8n.io/
- Paper Dynasty API: See `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
**Need Help?**
- Review n8n execution logs for detailed error messages
- Check Discord notifications for manual review requests
- Test with cURL to isolate issues
- Consult troubleshooting guide in main documentation
---
## Change Log
### 2025-11-13 - Initial Version
- Created quick start guide
- Defined 30-minute setup process
- Added troubleshooting quick reference
- Included post-setup tasks and monitoring

View File

@ -0,0 +1,333 @@
# n8n Workflows Documentation
Collection of production n8n workflows and integration guides.
## Available Workflows
### Ko-fi → Paper Dynasty Integration
**Status:** Production Ready
**Purpose:** Automated pack distribution for Ko-fi shop purchases
**Quick Links:**
- 🚀 **[QUICK-START.md](QUICK-START.md)** - Get running in 30 minutes
- 📖 **[kofi-paper-dynasty.md](kofi-paper-dynasty.md)** - Complete setup guide and node configuration
- 🧪 **[kofi-testing-guide.md](kofi-testing-guide.md)** - Testing procedures and sample payloads
- 🗺️ **[kofi-product-mapping-template.json](kofi-product-mapping-template.json)** - Product configuration template
- 📝 **[kofi-implementation-notes.md](kofi-implementation-notes.md)** - Real implementation, gotchas, and lessons learned
**Features:**
- Receives Ko-fi webhooks securely
- Multi-method user identification (discord_userid or team abbrev)
- Flexible product mapping system
- Paper Dynasty API integration
- Discord notifications for all outcomes
- Comprehensive error handling
- Manual review workflow for edge cases
**Webhook URLs:**
- Test: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
- Production: `https://n8n.manticorum.com/webhook/kofi-paperdy`
---
## Documentation Structure
### Quick Start Guide
**File:** `QUICK-START.md`
**Audience:** First-time setup
**Time:** 30 minutes
**Contents:**
- Prerequisites checklist
- Step-by-step setup (8 steps)
- Post-setup tasks
- Quick troubleshooting
### Complete Setup Guide
**File:** `kofi-paper-dynasty.md`
**Audience:** Detailed implementation
**Contents:**
- Architecture overview
- Prerequisites and configuration
- Node-by-node workflow setup
- Discord notification templates
- Troubleshooting guide
- Monitoring and maintenance
### Testing Guide
**File:** `kofi-testing-guide.md`
**Audience:** QA and validation
**Contents:**
- 5-phase testing strategy
- 8 test scenarios with cURL commands
- Validation scripts
- Testing checklist
- Production monitoring
### Product Mapping Template
**File:** `kofi-product-mapping-template.json`
**Audience:** Configuration reference
**Contents:**
- JSON structure for n8n custom variable
- Example product configurations
- Pack type reference
- Naming conventions
- Usage examples
---
## Getting Started
### New to Ko-fi Integration?
1. **Start here:** [QUICK-START.md](QUICK-START.md)
2. **Need details?** [kofi-paper-dynasty.md](kofi-paper-dynasty.md)
3. **Ready to test?** [kofi-testing-guide.md](kofi-testing-guide.md)
### Adding New Products?
1. Open [kofi-product-mapping-template.json](kofi-product-mapping-template.json)
2. Copy an existing product configuration
3. Update Ko-fi product code and pack details
4. Add to n8n custom variable `KOFI_PRODUCT_MAP`
5. Create matching product in Ko-fi shop
6. Test with cURL before going live
### Troubleshooting?
**Quick Fixes:**
- Webhook 404: Check workflow is Active in n8n
- Token error: Verify Ko-fi token in n8n credential
- Team not found: Check discord_userid or message field
- Packs not granted: Review API response in execution log
**Detailed Help:**
- Full troubleshooting: `kofi-paper-dynasty.md` → Troubleshooting section
- Test scenarios: `kofi-testing-guide.md` → Common Issues
- Quick reference: `QUICK-START.md` → Troubleshooting
---
## Workflow Architecture
```
┌─────────────────────────────────────────────────────┐
│ Ko-fi Shop Order │
└──────────────────┬──────────────────────────────────┘
│ POST webhook
┌─────────────────────────────────────────────────────┐
│ n8n Webhook: /webhook/kofi-paperdy │
│ - Parse form data │
│ - Validate Ko-fi token │
│ - Filter shop orders only │
└──────────────────┬──────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Identify Paper Dynasty Team │
│ • discord_userid → gmid lookup │
│ • OR message field → team abbrev │
└──────────────────┬──────────────────────────────────┘
┌─────────┴─────────┐
│ Team Found? │
└─────────┬─────────┘
Yes ↓ ↓ No
│ └──→ Manual Review Discord Notification
┌─────────────────────────────────────────────────────┐
│ Map Ko-fi Products → Paper Dynasty Packs │
│ • Load KOFI_PRODUCT_MAP variable │
│ • Map shop_items to pack requests │
└──────────────────┬──────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Call Paper Dynasty API: POST /packs │
│ • Grant packs to team │
│ • Auto-retry on failure (3x) │
└──────────────────┬──────────────────────────────────┘
┌─────────┴─────────┐
│ API Success? │
└─────────┬─────────┘
Yes ↓ ↓ No
│ └──→ Error Discord Notification
┌─────────────────────────────────────────────────────┐
│ Success Discord Notification │
│ • Customer name and team │
│ • Packs granted │
│ • Transaction ID │
└──────────────────┬──────────────────────────────────┘
Return 200 OK to Ko-fi
```
---
## Prerequisites
### System Requirements
- n8n instance running at `https://n8n.manticorum.com`
- PostgreSQL database for execution history
- HTTPS access via Nginx Proxy Manager
- WebSocket support enabled
### Required Credentials
1. **Ko-fi Verification Token**
- Source: Ko-fi dashboard → Settings → API → Webhooks
- Format: UUID (e.g., `8c2a3835-4544-4f27-a53a-adfd1fcc5664`)
- Security: Validates webhook authenticity
2. **Paper Dynasty API Key**
- Source: Paper Dynasty admin
- Format: Bearer token
- Permissions: Read teams, write packs
3. **Discord Webhook URL**
- Source: Discord channel → Integrations → Webhooks
- Format: `https://discord.com/api/webhooks/...`
- Purpose: Success/error/manual review notifications
### Required Custom Variables
1. **KOFI_PRODUCT_MAP**
- Type: JSON object
- Location: n8n → Settings → Variables
- Purpose: Map Ko-fi product codes to PD packs
- Template: `kofi-product-mapping-template.json`
---
## Monitoring & Maintenance
### Daily Monitoring
- ✅ Check Discord for notifications
- ✅ Review failed executions in n8n
- ✅ Verify packs granted correctly
### Weekly Tasks
- 📊 Review n8n execution statistics
- 🔍 Check for unknown product codes
- 📝 Update product mapping if needed
- 🧪 Test new products before launch
### Monthly Tasks
- 💾 Backup n8n database
- 🔐 Rotate Ko-fi verification token
- 🔑 Rotate Paper Dynasty API key
- 📖 Update documentation with lessons learned
### Quarterly Tasks
- 🔧 Optimize workflow performance
- 📈 Review transaction analytics
- 🛡️ Security audit (credentials, access logs)
- 🧪 Test disaster recovery procedure
---
## Support & Resources
### Internal Documentation
- **n8n Infrastructure:** `/productivity/n8n/CONTEXT.md`
- **n8n Troubleshooting:** `/productivity/n8n/troubleshooting.md`
- **Paper Dynasty API:** `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
### External Resources
- **n8n Documentation:** https://docs.n8n.io/
- **Ko-fi Webhook API:** https://help.ko-fi.com/hc/en-us/articles/360004162298
- **n8n Community:** https://community.n8n.io/
- **n8n Webhook Templates:** https://n8n.io/workflows/?categories=Webhooks
### Getting Help
1. **Check execution logs:** n8n → Executions → Filter by workflow
2. **Review Discord notifications:** Look for error details and transaction IDs
3. **Test with cURL:** Isolate webhook vs API vs n8n issues
4. **Consult troubleshooting guides:** Each doc has troubleshooting section
5. **Manual intervention:** Use Discord notifications to process failed orders
---
## Security Considerations
### Webhook Security
**Token Validation:** All webhooks validated against Ko-fi verification token
**HTTPS Only:** All communication encrypted via NPM SSL
**Rate Limiting:** Configure in NPM for webhook endpoints
**Path Obfuscation:** Use non-obvious webhook paths
### Credential Management
**Encrypted Storage:** n8n encrypts credentials with N8N_ENCRYPTION_KEY
**No Hardcoding:** Never hardcode secrets in workflows
**Regular Rotation:** Quarterly rotation schedule for tokens
**Backup Security:** Store encryption key in password manager
### Data Privacy
**PII Handling:** Minimize customer data in logs and notifications
**Retention Policy:** Auto-delete old executions (30 days)
**Sanitized Logging:** Remove sensitive fields from error logs
**GDPR Compliance:** Data deletion mechanism available
---
## Contributing
### Adding New Workflows
1. Create workflow documentation (follow `kofi-paper-dynasty.md` structure)
2. Add testing guide with sample payloads
3. Update this README with workflow description
4. Create quick-start guide if complex
5. Update `/productivity/n8n/CONTEXT.md` with workflow details
### Documentation Standards
- **Markdown format** for all docs
- **Code blocks** with language syntax highlighting
- **cURL examples** for all webhook tests
- **Screenshots** where helpful (workflow diagrams)
- **Version history** in Change Log section
### Testing Requirements
- ✅ Test with simulated webhooks (cURL)
- ✅ Test with real data in `/webhook-test/` path
- ✅ Validate all error paths
- ✅ Verify Discord notifications
- ✅ Test edge cases and invalid data
---
## Change Log
### 2025-11-13 - Ko-fi Integration
- Added Ko-fi → Paper Dynasty workflow
- Created comprehensive documentation suite
- Implemented multi-method user identification
- Added Discord notification system
- Created testing guide with 8 scenarios
- Documented product mapping system
---
## License & Attribution
**n8n:** Fair-code licensed (n8n GmbH)
**Workflows:** Created by Cal Corum for Paper Dynasty
**Documentation:** MIT License
---
**Questions?** Consult the [QUICK-START.md](QUICK-START.md) guide or review workflow-specific documentation.

View File

@ -0,0 +1,462 @@
# Ko-fi → Paper Dynasty Implementation Notes
**Date:** 2025-11-13
**Status:** Working in pddev, ready for production
**n8n Version:** Community (self-hosted)
## What We Actually Built
This document captures the real implementation vs. the original plan, including all the gotchas we encountered.
---
## Key Differences from Original Plan
### 1. No Custom Variables (Enterprise Feature)
**Original Plan:** Use n8n custom variable `KOFI_PRODUCT_MAP` for product mapping
**Actual Implementation:** Hardcoded product mapping in "Map Products" Code node
**Why:** Custom variables are enterprise-only in n8n
**Product Mapping (Hardcoded in Code Node):**
```javascript
const PRODUCT_MAP = {
'61de350207': { // Ko-fi product ID from URL
name: 'Premium Pack',
pack_type_id: 3,
packs_per_quantity: 1
},
'2bdb7a4916': {
name: 'Standard Pack',
pack_type_id: 1,
packs_per_quantity: 1
},
'3d7a4935aa': {
name: 'In-Game Currency',
type: 'currency',
needs_manual_review: true // Not implemented yet
}
};
```
### 2. Team Lookup Split into 3 Nodes
**Original Plan:** Single "Lookup PD Team" Code node with `$http.request()`
**Actual Implementation:** 3 separate nodes
**Why:** `fetch()` and `$http` are not available in n8n Code nodes
**The 3 Nodes:**
1. **Extract Team Abbrev** (Code) - Regex extraction only
2. **Lookup Team API** (HTTP Request) - API call to PD
3. **Process Team Result** (Code) - Parse API response
### 3. Ko-fi Product Codes
**Original Plan:** Use Ko-fi's "direct_link_code" custom field
**Actual Reality:** Ko-fi doesn't have custom product codes
**Solution:** Using Ko-fi's internal product IDs from URLs:
- `https://ko-fi.com/s/61de350207` → ID is `61de350207`
- These IDs appear in webhook's `shop_items[].direct_link_code`
### 4. Webhook Domain Configuration
**Issue:** n8n showed `https://n8n.yourdomain.com` by default
**Solution:** Environment variables were set correctly, just needed n8n restart:
```bash
ssh root@10.10.0.210 "cd /opt/n8n && docker compose down && docker compose up -d"
```
### 5. API Request Body Formatting
**Issue:** Paper Dynasty API rejected stringified JSON
**Solution:** Use `{{ $json.pack_requests }}` without `JSON.stringify()` or `=` prefix
**Wrong:** `={{ JSON.stringify({"packs": $json.pack_requests}) }}`
**Right:** `{{ $json.pack_requests }}`
### 6. Discord Notifications
**Issue:** Empty body caused "Cannot send an empty message"
**Solution:** Always include `content` field even when using embeds
**Minimum working Discord message:**
```json
{
"content": "Message text here",
"embeds": [...]
}
```
### 7. Added Donation Support
**Extension:** Added Switch node to route by transaction type
**Routes:**
- Output 0: Shop Order → Grant Packs flow
- Output 1: Donation → Thank you notification
- Output 2: Subscription → (Future implementation)
- Fallback: Unknown types → Return 200 OK
---
## Final Working Architecture
```
Ko-fi Webhook POST
[Webhook: Ko-fi Webhook]
Path: /kofi-pd-purchase
[Code: Parse & Validate]
- Parse form data JSON
- Validate Ko-fi token (hardcoded)
[Switch: Route by Type]
Value: {{ $json.type }}
├─ Output 0: "Shop Order"
│ ↓
│ [Code: Extract Team Abbrev]
│ - Regex: /\b([A-Z]{2,4})\b/
│ ↓
│ [HTTP: Lookup Team API]
│ - URL: pddev.manticorum.com/api/v2/teams?abbrev=...
│ - Auth: Paper Dynasty API credential
│ ↓
│ [Code: Process Team Result]
│ - Extract team ID and abbrev
│ ↓
│ [IF: Team Found?]
│ ├─ True → [Code: Map Products]
│ │ - Hardcoded product map
│ │ - Create pack_requests array
│ │ ↓
│ │ [HTTP: Grant Packs]
│ │ - POST /packs with array
│ │ ↓
│ │ [IF: API Success?]
│ │ ├─ True → [HTTP: Discord Success]
│ │ └─ False → [HTTP: Discord Error]
│ │
│ └─ False → [HTTP: Discord Manual Review]
├─ Output 1: "Donation"
│ ↓
│ [HTTP: Discord Thank You]
│ - Notification for manual thanks
└─ Fallback: Other types
[Return 200 OK]
```
---
## Current Configuration
### Environment
- **Testing:** pddev.manticorum.com (Paper Dynasty Dev)
- **Production:** pd.manticorum.com (not yet deployed)
### Credentials Created
1. **Paper Dynasty API** (Header Auth)
- Header: `Authorization`
- Value: `Bearer Tp3aO3jhYve5NJF1IqOmJTmk`
2. **Discord Webhook** (not used as credential)
- URL hardcoded in HTTP Request nodes
- Webhook: `1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej`
### Ko-fi Settings
- **Verification Token:** `44d1f957-ac15-497e-8306-4dc667de55d1`
- **Webhook URL (test):** `https://n8n.manticorum.com/webhook-test/kofi-pd-purchase`
- **Webhook URL (prod):** `https://n8n.manticorum.com/webhook/kofi-pd-purchase`
### Products Configured
| Ko-fi ID | Name | PD Pack Type | Quantity |
|----------|------|--------------|----------|
| 61de350207 | Premium Pack | 3 | 1 per qty |
| 2bdb7a4916 | Standard Pack | 1 | 1 per qty |
| 3d7a4935aa | In-Game Currency | N/A | Manual review |
---
## Testing Results
### Successful Test (2025-11-13)
- ✅ Webhook received and parsed
- ✅ Token validated
- ✅ Team "SKB" found via message extraction
- ✅ Product mapped (2bdb7a4916 → 5 standard packs)
- ✅ API call succeeded (201 Created)
- ✅ 5 packs granted to team SKB in pddev
- ✅ Discord success notification sent
### Test Command Used
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-pd-purchase' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "44d1f957-ac15-497e-8306-4dc667de55d1",
"message_id": "test-001",
"timestamp": "2025-11-13T12:00:00Z",
"type": "Shop Order",
"from_name": "Test Customer",
"message": "SKB",
"amount": "5.00",
"email": "test@example.com",
"currency": "USD",
"kofi_transaction_id": "test-001",
"shop_items": [
{
"direct_link_code": "2bdb7a4916",
"variation_name": "Standard Pack",
"quantity": 5
}
],
"discord_userid": null
}'
```
---
## Known Limitations
### 1. No Discord User ID Support Yet
**Status:** Code is in place but untested
**Why:** Test payload used `discord_userid: null`
**Todo:** Test with real Discord-linked Ko-fi account
### 2. Currency Product Not Implemented
**Status:** Routed to manual review
**Reason:** Paper Dynasty doesn't have currency system yet
**Todo:** Implement currency granting when PD adds support
### 3. Subscription Type Not Handled
**Status:** Returns 200 OK but does nothing
**Reason:** Decided to implement shop orders and donations first
**Todo:** Add subscription tier handling
### 4. Single Discord Channel
**Status:** All notifications go to "cals-hidey-hole"
**Todo:** Consider separate channels for success/errors/reviews
### 5. Hardcoded Configuration
**Status:** All settings hardcoded in Code nodes
**Why:** Community edition limitations
**Impact:** Must edit code nodes to change products, tokens, or URLs
**Mitigation:** Well-documented code with clear comments
---
## Production Deployment Checklist
Before going live with production:
- [ ] **Update all API URLs from pddev to prod:**
- [ ] "Lookup Team API" node URL
- [ ] "Grant Packs" node URL
- [ ] **Update Ko-fi webhook URL:**
- [ ] Change from `/webhook-test/` to `/webhook/`
- [ ] Configure in Ko-fi dashboard
- [ ] **Activate workflow:**
- [ ] Toggle "Active" switch in n8n
- [ ] **Test with real Ko-fi purchase:**
- [ ] Create $0.01 test product
- [ ] Make test purchase
- [ ] Verify packs granted in production
- [ ] Verify Discord notification
- [ ] **Monitor first 10 transactions:**
- [ ] Watch Discord for notifications
- [ ] Check n8n execution logs
- [ ] Verify pack distribution accuracy
- [ ] **Document any issues encountered**
---
## Troubleshooting Guide
### Webhook Not Triggering
**Symptoms:** n8n shows "Waiting for trigger event" but nothing happens
**Solutions:**
1. Verify workflow is Active (green toggle)
2. Check webhook path matches Ko-fi configuration
3. Test with cURL to isolate issue
4. Check n8n logs: `ssh root@10.10.0.210 "cd /opt/n8n && docker compose logs -f n8n"`
### Token Validation Failing
**Symptoms:** Workflow stops at "Parse & Validate" with error
**Solutions:**
1. Verify token in Code node matches Ko-fi dashboard exactly
2. Check for extra spaces or quotes
3. Re-copy token from Ko-fi
### Team Not Found
**Symptoms:** Workflow takes "Team Found? False" path
**Solutions:**
1. Check message field has valid team abbrev (2-4 uppercase letters)
2. Test API directly: `curl -H "Authorization: Bearer TOKEN" "URL/teams?abbrev=SKB"`
3. Verify team exists in target environment (pddev vs prod)
### API Call Failing
**Symptoms:** "API Success?" takes False path
**Solutions:**
1. Check HTTP Request body format (no JSON.stringify, no = prefix)
2. Verify `pack_requests` is an array in "Map Products" output
3. Check API credentials are correct
4. Review full error in node execution data
### Discord Notification Not Sending
**Symptoms:** No Discord message received
**Solutions:**
1. Verify webhook URL is correct
2. Test webhook directly with cURL
3. Check body has `content` field (required even with embeds)
4. Verify Discord channel hasn't deleted the webhook
### Unknown Product Code
**Symptoms:** `unknown_products` array has items, takes manual review path
**Solutions:**
1. Check Ko-fi product ID matches `PRODUCT_MAP` keys
2. Get product ID from Ko-fi product URL: `/s/{ID}`
3. Add new product to PRODUCT_MAP in "Map Products" code
---
## Maintenance Tasks
### Adding New Products
1. Get Ko-fi product ID from product URL
2. Edit "Map Products" Code node
3. Add entry to `PRODUCT_MAP`:
```javascript
'new-product-id': {
name: 'Product Name',
pack_type_id: 1, // 1=Standard, 3=Premium, 8=Team Choice
packs_per_quantity: 1
}
```
4. Save and test
### Changing API Endpoints
1. Edit "Lookup Team API" HTTP Request URL
2. Edit "Grant Packs" HTTP Request URL
3. Update "Parse & Validate" API_TOKEN if needed
4. Test thoroughly before activating
### Updating Discord Webhooks
1. Create new webhook in Discord
2. Update all Discord HTTP Request node URLs:
- Discord Success
- Discord Error
- Discord Manual Review
- Discord Thank You
3. Test each notification type
### Rotating Credentials
**Ko-fi Token:**
1. Get new token from Ko-fi dashboard
2. Update in "Parse & Validate" Code node
3. Test with cURL
**Paper Dynasty API Key:**
1. Generate new key in PD admin
2. Update "Paper Dynasty API" credential in n8n
3. Test API calls
---
## Future Enhancements
### Priority 1 (Next Session)
- [ ] Test Discord user ID → gmid lookup
- [ ] Add Subscription handling
- [ ] Implement currency product logic
- [ ] Test with real Ko-fi purchases
### Priority 2 (Nice to Have)
- [ ] Separate Discord channels for different notification types
- [ ] Email notifications for manual reviews
- [ ] Transaction history logging (database or file)
- [ ] Analytics dashboard for pack distributions
### Priority 3 (Optimization)
- [ ] Retry logic for failed transactions
- [ ] Batch processing for multiple purchases
- [ ] Scheduled reconciliation job
- [ ] Admin UI for product mapping
---
## Lessons Learned
### 1. n8n Community vs Enterprise
Community edition is powerful but limited:
- No custom variables (use hardcoded values in Code nodes)
- Code nodes can't make HTTP requests (use HTTP Request nodes)
- Works great for our use case with workarounds
### 2. Ko-fi Webhook Structure
- Form-urlencoded with JSON in `data` field
- Product codes are internal Ko-fi IDs, not custom
- `shop_items` can have multiple items in single order
- Always validate `verification_token`
### 3. Paper Dynasty API Integration
- Straightforward REST API
- Expects JSON arrays, not stringified
- Good error messages
- Dev environment perfect for testing
### 4. Discord Webhook Best Practices
- Always include `content` field
- Embeds are great for rich notifications
- @here mentions work for urgent alerts
- Test webhooks fail silently if deleted
### 5. Workflow Development Process
- Start simple, add complexity gradually
- Test each node individually
- Use debug fields (like `debug_info`) during development
- Document as you go
---
## Resources
**Documentation Created:**
- `/productivity/n8n/workflows/kofi-paper-dynasty.md` - Original detailed guide
- `/productivity/n8n/workflows/QUICK-START.md` - 30-minute setup guide
- `/productivity/n8n/workflows/kofi-testing-guide.md` - Testing procedures
- `/productivity/n8n/workflows/kofi-product-mapping-template.json` - Product config
- `/productivity/n8n/workflows/kofi-implementation-notes.md` - This file
**External References:**
- Ko-fi Webhook Docs: https://help.ko-fi.com/hc/en-us/articles/360004162298
- n8n Documentation: https://docs.n8n.io/
- Paper Dynasty API: See `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
**Test Files:**
- Ko-fi payload structure: `/home/cal/Desktop/kofi-integration.md`
---
## Change Log
### 2025-11-13 - Initial Implementation
- Created complete Ko-fi → Paper Dynasty workflow
- Working in pddev environment
- Shop orders fully functional
- Donation notifications implemented
- Team identification via message extraction tested
- Discord notifications for all paths
- Ready for production deployment
---
**Next Session Goals:**
1. Test Discord user ID lookup
2. Deploy to production
3. Make first real Ko-fi purchase
4. Monitor and iterate
**Status:** ✅ Ready for production deployment

View File

@ -0,0 +1,350 @@
{
"name": "Ko-fi to Paper Dynasty",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "kofi-paperdy",
"responseMode": "lastNode",
"options": {}
},
"id": "webhook-trigger",
"name": "Ko-fi Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [250, 300],
"webhookId": "kofi-paperdy"
},
{
"parameters": {
"jsCode": "// Parse Ko-fi form data\nconst formData = $input.item.json.body;\nconst kofiData = JSON.parse(formData.data);\n\n// Ko-fi verification token\nconst KOFI_TOKEN = '44d1f957-ac15-497e-8306-4dc667de55d1';\n\n// Validate token\nif (kofiData.verification_token !== KOFI_TOKEN) {\n throw new Error('Invalid Ko-fi verification token');\n}\n\n// Add metadata\nreturn [{\n json: {\n ...kofiData,\n _parsed_at: new Date().toISOString(),\n _execution_id: $execution.id\n }\n}];"
},
"id": "parse-and-validate",
"name": "Parse & Validate",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [450, 300]
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.type }}",
"operation": "equals",
"value2": "Shop Order"
}
]
}
},
"id": "filter-shop-orders",
"name": "Shop Order?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [650, 300]
},
{
"parameters": {
"jsCode": "// Lookup Paper Dynasty team\nconst PD_API_BASE = 'https://pd.manticorum.com/api/v2';\nconst API_TOKEN = 'Bearer Tp3aO3jhYve5NJF1IqOmJTmk';\n\nlet teamId = null;\nlet teamAbbrev = null;\nlet identificationMethod = null;\n\n// Method 1: Discord User ID -> gmid lookup\nif ($json.discord_userid) {\n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?gmid=${$json.discord_userid}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'discord_userid';\n }\n } catch (error) {\n console.log('Discord lookup failed:', error.message);\n }\n}\n\n// Method 2: Extract team abbrev from message\nif (!teamId && $json.message) {\n const abbrevMatch = $json.message.match(/\\b([A-Z]{2,4})\\b/);\n \n if (abbrevMatch) {\n const extractedAbbrev = abbrevMatch[1];\n \n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?abbrev=${extractedAbbrev}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'message_abbrev';\n }\n } catch (error) {\n console.log('Team abbrev lookup failed:', error.message);\n }\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pd_team_id: teamId,\n pd_team_abbrev: teamAbbrev,\n identification_method: identificationMethod,\n needs_manual_review: !teamId\n }\n}];"
},
"id": "lookup-team",
"name": "Lookup PD Team",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [850, 200]
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.needs_manual_review }}",
"value2": false
}
]
}
},
"id": "check-team-found",
"name": "Team Found?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [1050, 200]
},
{
"parameters": {
"jsCode": "// Product mapping\nconst PRODUCT_MAP = {\n '61de350207': {\n name: 'Premium Pack',\n pack_type_id: 3,\n packs_per_quantity: 1\n },\n '2bdb7a4916': {\n name: 'Standard Pack',\n pack_type_id: 1,\n packs_per_quantity: 1\n },\n '3d7a4935aa': {\n name: 'In-Game Currency',\n type: 'currency',\n needs_manual_review: true\n }\n};\n\nconst shopItems = $json.shop_items;\nconst packRequests = [];\nlet unknownProducts = [];\nlet needsReview = [];\n\nfor (const item of shopItems) {\n const productConfig = PRODUCT_MAP[item.direct_link_code];\n \n if (!productConfig) {\n unknownProducts.push({\n code: item.direct_link_code,\n variation: item.variation_name,\n quantity: item.quantity\n });\n continue;\n }\n \n // Handle currency products\n if (productConfig.type === 'currency') {\n needsReview.push({\n product: productConfig.name,\n quantity: item.quantity,\n reason: 'Currency system not implemented yet'\n });\n continue;\n }\n \n // Calculate total packs\n const totalPacks = item.quantity * productConfig.packs_per_quantity;\n \n // Create pack requests\n for (let i = 0; i < totalPacks; i++) {\n packRequests.push({\n team_id: $json.pd_team_id,\n pack_type_id: productConfig.pack_type_id,\n pack_cardset_id: null\n });\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pack_requests: packRequests,\n unknown_products: unknownProducts,\n needs_review_items: needsReview,\n has_unknown_products: unknownProducts.length > 0,\n has_review_items: needsReview.length > 0,\n total_packs: packRequests.length\n }\n}];"
},
"id": "map-products",
"name": "Map Products",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1250, 100]
},
{
"parameters": {
"method": "POST",
"url": "https://pd.manticorum.com/api/v2/packs",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "packs",
"value": "={{ JSON.stringify($json.pack_requests) }}"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": true
}
},
"retry": {
"retry": {
"maxRetries": 3,
"retryInterval": 2000
}
}
}
},
"id": "grant-packs",
"name": "Grant Packs",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1450, 100],
"credentials": {
"httpHeaderAuth": {
"id": "1",
"name": "Paper Dynasty API"
}
}
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.statusCode }}",
"operation": "between",
"value2": 200,
"value3": 299
}
]
}
},
"id": "check-api-success",
"name": "API Success?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [1650, 100]
},
{
"parameters": {
"method": "POST",
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
"sendBody": true,
"contentType": "json",
"body": "={{ JSON.stringify({\n content: '✅ Ko-fi Order Processed',\n embeds: [{\n title: 'Paper Dynasty Packs Granted',\n color: 3066993,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name,\n inline: true\n },\n {\n name: 'Team',\n value: $('Lookup PD Team').item.json.pd_team_abbrev,\n inline: true\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount + ' ' + $('Parse & Validate').item.json.currency,\n inline: true\n },\n {\n name: 'Packs Granted',\n value: $('Map Products').item.json.total_packs + ' packs',\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
},
"id": "discord-success",
"name": "Discord Success",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1850, 0]
},
{
"parameters": {
"method": "POST",
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
"sendBody": true,
"contentType": "json",
"body": "={{ JSON.stringify({\n content: '@here ⚠️ Ko-fi Order Processing Error',\n embeds: [{\n title: 'Paper Dynasty API Error',\n color: 15158332,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name + ' (' + $('Parse & Validate').item.json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount,\n inline: true\n },\n {\n name: 'Team ID',\n value: String($('Lookup PD Team').item.json.pd_team_id || 'Unknown'),\n inline: true\n },\n {\n name: 'API Status',\n value: String($json.statusCode || 'Request Failed'),\n inline: true\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: 'Manual pack distribution needed. Use transaction ID to process manually.',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
},
"id": "discord-error",
"name": "Discord Error",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1850, 200]
},
{
"parameters": {
"method": "POST",
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
"sendBody": true,
"contentType": "json",
"body": "={{ JSON.stringify({\n content: '@here 🔍 Ko-fi Order Needs Manual Review',\n embeds: [{\n title: 'Unable to Identify Paper Dynasty Team',\n color: 16776960,\n fields: [\n {\n name: 'Customer',\n value: $json.from_name + ' (' + $json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $json.amount + ' ' + $json.currency,\n inline: true\n },\n {\n name: 'Discord User ID',\n value: $json.discord_userid || 'Not provided',\n inline: true\n },\n {\n name: 'Message',\n value: $json.message || 'No message',\n inline: false\n },\n {\n name: 'Products',\n value: $json.shop_items.map(i => i.quantity + 'x ' + i.variation_name + ' (' + i.direct_link_code + ')').join('\\n'),\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: '1. Identify user\\'s Paper Dynasty team\\n2. Manually grant packs using PD API\\n3. Reply when resolved',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
},
"id": "discord-manual-review",
"name": "Discord Manual Review",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [1250, 300]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({\n status: 'success',\n message: 'Order received and processed',\n execution_id: $execution.id\n}) }}"
},
"id": "respond-webhook",
"name": "Return 200 OK",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [2050, 150]
}
],
"connections": {
"Ko-fi Webhook": {
"main": [
[
{
"node": "Parse & Validate",
"type": "main",
"index": 0
}
]
]
},
"Parse & Validate": {
"main": [
[
{
"node": "Shop Order?",
"type": "main",
"index": 0
}
]
]
},
"Shop Order?": {
"main": [
[
{
"node": "Lookup PD Team",
"type": "main",
"index": 0
}
],
[
{
"node": "Return 200 OK",
"type": "main",
"index": 0
}
]
]
},
"Lookup PD Team": {
"main": [
[
{
"node": "Team Found?",
"type": "main",
"index": 0
}
]
]
},
"Team Found?": {
"main": [
[
{
"node": "Map Products",
"type": "main",
"index": 0
}
],
[
{
"node": "Discord Manual Review",
"type": "main",
"index": 0
}
]
]
},
"Map Products": {
"main": [
[
{
"node": "Grant Packs",
"type": "main",
"index": 0
}
]
]
},
"Grant Packs": {
"main": [
[
{
"node": "API Success?",
"type": "main",
"index": 0
}
]
]
},
"API Success?": {
"main": [
[
{
"node": "Discord Success",
"type": "main",
"index": 0
}
],
[
{
"node": "Discord Error",
"type": "main",
"index": 0
}
]
]
},
"Discord Success": {
"main": [
[
{
"node": "Return 200 OK",
"type": "main",
"index": 0
}
]
]
},
"Discord Error": {
"main": [
[
{
"node": "Return 200 OK",
"type": "main",
"index": 0
}
]
]
},
"Discord Manual Review": {
"main": [
[
{
"node": "Return 200 OK",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"updatedAt": "2025-11-13T00:00:00.000Z",
"versionId": "1"
}

View File

@ -0,0 +1,860 @@
# Ko-fi → Paper Dynasty Integration
Automated workflow that processes Ko-fi shop orders and grants Paper Dynasty packs to users.
## Overview
When a user purchases packs on Ko-fi, this workflow:
1. Receives the webhook from Ko-fi
2. Identifies the Paper Dynasty team (via discord_userid or team abbrev)
3. Maps Ko-fi products to Paper Dynasty pack types
4. Grants packs via Paper Dynasty API
5. Sends Discord notifications on success/failure
## Architecture
```
┌──────────────────────────────────────────────────────────────┐
│ Ko-fi Shop Order Flow │
└──────────────────────────────────────────────────────────────┘
Ko-fi Shop Order
Ko-fi Webhook POST
https://n8n.manticorum.com/webhook/kofi-paperdy
┌─────────────────────────────────────────────────────────────┐
│ n8n Workflow: Ko-fi to Paper Dynasty │
│ │
│ 1. Parse Ko-fi payload │
│ 2. Validate verification token │
│ 3. Identify Paper Dynasty team: │
│ • If discord_userid → Lookup by gmid │
│ • Else extract team abbrev from message │
│ 4. Map Ko-fi products → PD packs │
│ 5. Grant packs via PD API │
│ 6. Send Discord notification │
└─────────────────────────────────────────────────────────────┘
Paper Dynasty Database Updated
+
Discord Notification Sent
```
## Prerequisites
### 1. Ko-fi Configuration
**Get Verification Token:**
1. Login to Ko-fi: https://ko-fi.com/
2. Navigate to: Settings → API → Webhooks → Advanced
3. Copy your **Verification Token** (UUID format)
4. Save securely for n8n credentials setup
**Configure Webhook URL:**
1. Ko-fi → Settings → API → Webhooks
2. Set Webhook URL: `https://n8n.manticorum.com/webhook/kofi-paperdy`
3. (Initially use `/webhook-test/` for testing)
4. Enable webhook
5. Test using Ko-fi's webhook tester
### 2. Paper Dynasty API
**API Key:**
- Production API: `https://pd.manticorum.com/api/v2/`
- Required header: `Authorization: Bearer YOUR_API_KEY`
- Store API key securely for n8n credentials
**Required Endpoints:**
- `GET /teams?gmid={discord_userid}` - Lookup team by Discord user ID
- `GET /teams?abbrev={team_abbrev}` - Lookup team by abbreviation
- `POST /packs` - Grant packs to team
### 3. Discord Notifications
**Webhook URL:**
1. Discord channel → Settings → Integrations → Webhooks
2. Create webhook for Ko-fi notifications
3. Copy webhook URL
4. Save for n8n credentials setup
## n8n Credentials Setup
Navigate to n8n → Settings → Credentials and create:
### 1. Ko-fi Verification Token
- **Type:** Generic Credential
- **Name:** `Ko-fi Verification Token`
- **Field:** `verification_token`
- **Value:** `[your-kofi-verification-token-uuid]`
### 2. Paper Dynasty API Key
- **Type:** Header Auth
- **Name:** `Paper Dynasty API`
- **Header Name:** `Authorization`
- **Value:** `Bearer [your-pd-api-key]`
### 3. Discord Webhook
- **Type:** Discord Webhook (or HTTP Header Auth)
- **Name:** `Discord Ko-fi Notifications`
- **Webhook URL:** `https://discord.com/api/webhooks/...`
## Product Mapping Configuration
Create n8n custom variable for product mapping.
**Navigate to:** n8n → Settings → Variables
**Variable Name:** `KOFI_PRODUCT_MAP`
**Value (JSON):**
```json
{
"pack-standard-5": {
"name": "5-Pack Standard",
"pack_type_id": 1,
"quantity": 5,
"pack_cardset_id": null
},
"pack-premium-10": {
"name": "10-Pack Premium",
"pack_type_id": 3,
"quantity": 10,
"pack_cardset_id": null
},
"pack-team-choice": {
"name": "Team Choice Pack",
"pack_type_id": 8,
"quantity": 1,
"pack_cardset_id": 27
}
}
```
**Product Code Convention:**
- Use Ko-fi's `direct_link_code` field as key
- Set this in Ko-fi product settings
- Format: `pack-{tier}-{quantity}` or `pack-{special-name}`
- Examples: `pack-standard-5`, `pack-premium-10`, `pack-legendary-single`
## Workflow Node Configuration
### Node 1: Webhook Trigger
**Node Type:** Webhook
**Name:** Ko-fi Webhook Trigger
**Settings:**
- **HTTP Method:** POST
- **Path:** `kofi-paperdy`
- **Authentication:** None (we validate token manually)
- **Response Mode:** When Last Node Finishes
- **Response Code:** 200
**Creates URL:** `https://n8n.manticorum.com/webhook/kofi-paperdy`
---
### Node 2: Parse Ko-fi Data
**Node Type:** Code
**Name:** Parse Ko-fi Form Data
**Mode:** Run Once for All Items
**Code:**
```javascript
// Ko-fi sends application/x-www-form-urlencoded
// The 'data' field contains JSON string
const formData = $input.item.json.body;
// Parse JSON from data field
const kofiData = JSON.parse(formData.data);
// Add metadata
return [{
json: {
...kofiData,
_parsed_at: new Date().toISOString(),
_execution_id: $execution.id
}
}];
```
---
### Node 3: Validate Token
**Node Type:** IF
**Name:** Validate Ko-fi Token
**Condition:**
- **Type:** String
- **Value 1:** `{{ $json.verification_token }}`
- **Operation:** Equal
- **Value 2:** `{{ $credentials.kofiVerificationToken.verification_token }}`
**Routing:**
- **True:** Continue to Node 4
- **False:** Connect to "Stop and Error" node
**False Path - Stop and Error Node:**
- **Error Message:** `Invalid Ko-fi verification token`
---
### Node 4: Check Transaction Type
**Node Type:** IF
**Name:** Filter Shop Orders
**Condition:**
- **Type:** String
- **Value 1:** `{{ $json.type }}`
- **Operation:** Equal
- **Value 2:** `Shop Order`
**Routing:**
- **True:** Continue to Node 5
- **False:** Connect to "Respond to Webhook" with message "Not a shop order"
---
### Node 5: Identify Paper Dynasty Team
**Node Type:** Code
**Name:** Lookup Paper Dynasty Team
**Mode:** Run Once for All Items
**Code:**
```javascript
// Multi-method team identification:
// 1. discord_userid → lookup by gmid
// 2. message field → extract team abbrev
const PD_API_BASE = 'https://pd.manticorum.com/api/v2';
const API_TOKEN = '{{ $credentials.paperDynastyApi.headerAuth.value }}';
let teamId = null;
let teamAbbrev = null;
let identificationMethod = null;
// Method 1: Discord User ID → gmid lookup
if ($json.discord_userid) {
try {
const response = await $http.request({
method: 'GET',
url: `${PD_API_BASE}/teams?gmid=${$json.discord_userid}`,
headers: {
'Authorization': API_TOKEN,
'Content-Type': 'application/json'
}
});
if (response.body && response.body.length > 0) {
teamId = response.body[0].id;
teamAbbrev = response.body[0].abbrev;
identificationMethod = 'discord_userid';
}
} catch (error) {
console.log('Discord user ID lookup failed:', error.message);
}
}
// Method 2: Extract team abbrev from message field
if (!teamId && $json.message) {
// Look for team abbreviations (typically 3 uppercase letters)
// Match patterns like: "CAR", "SKB", "PPD", etc.
const abbrevMatch = $json.message.match(/\b([A-Z]{2,4})\b/);
if (abbrevMatch) {
const extractedAbbrev = abbrevMatch[1];
try {
const response = await $http.request({
method: 'GET',
url: `${PD_API_BASE}/teams?abbrev=${extractedAbbrev}`,
headers: {
'Authorization': API_TOKEN,
'Content-Type': 'application/json'
}
});
if (response.body && response.body.length > 0) {
teamId = response.body[0].id;
teamAbbrev = response.body[0].abbrev;
identificationMethod = 'message_abbrev';
}
} catch (error) {
console.log('Team abbrev lookup failed:', error.message);
}
}
}
// Return result
return [{
json: {
...$json,
pd_team_id: teamId,
pd_team_abbrev: teamAbbrev,
identification_method: identificationMethod,
needs_manual_review: !teamId
}
}];
```
---
### Node 6: Route by Identification Status
**Node Type:** IF
**Name:** Check Team Identified
**Condition:**
- **Type:** Boolean
- **Value 1:** `{{ $json.needs_manual_review }}`
- **Operation:** Equal
- **Value 2:** `false`
**Routing:**
- **True:** Continue to Node 7 (process order)
- **False:** Connect to "Manual Review" Discord notification
**False Path - Discord Notification:**
- **Message:** Manual review needed (see Discord node config below)
---
### Node 7: Map Ko-fi Products
**Node Type:** Code
**Name:** Map Products to PD Packs
**Mode:** Run Once for All Items
**Code:**
```javascript
// Load product mapping
const productMap = JSON.parse($vars.KOFI_PRODUCT_MAP);
const shopItems = $json.shop_items;
// Process each shop item
const packRequests = [];
let unknownProducts = [];
for (const item of shopItems) {
const productConfig = productMap[item.direct_link_code];
if (!productConfig) {
// Unknown product code
unknownProducts.push({
code: item.direct_link_code,
variation: item.variation_name,
quantity: item.quantity
});
continue;
}
// Create pack request for each quantity
for (let i = 0; i < item.quantity; i++) {
packRequests.push({
team_id: $json.pd_team_id,
pack_type_id: productConfig.pack_type_id,
pack_cardset_id: productConfig.pack_cardset_id
});
}
}
return [{
json: {
...$json,
pack_requests: packRequests,
unknown_products: unknownProducts,
has_unknown_products: unknownProducts.length > 0,
total_packs: packRequests.length
}
}];
```
---
### Node 8: Grant Packs via API
**Node Type:** HTTP Request
**Name:** Paper Dynasty Grant Packs
**Settings:**
- **Method:** POST
- **URL:** `https://pd.manticorum.com/api/v2/packs`
- **Authentication:** Use Credential → Select "Paper Dynasty API"
- **Body Content Type:** JSON
- **Continue on Fail:** ✅ Enabled
- **Retry on Fail:** ✅ Enabled
- **Max Tries:** 3
- **Wait Between Tries:** 2000ms
- **Timeout:** 10000ms
**Body (JSON):**
```json
{
"packs": {{ JSON.stringify($json.pack_requests) }}
}
```
**Example:**
```json
{
"packs": [
{"team_id": 69, "pack_type_id": 1, "pack_cardset_id": null},
{"team_id": 69, "pack_type_id": 1, "pack_cardset_id": null},
{"team_id": 69, "pack_type_id": 3, "pack_cardset_id": null}
]
}
```
---
### Node 9: Check API Response
**Node Type:** IF
**Name:** API Success Check
**Condition:**
- **Type:** Number
- **Value 1:** `{{ $json.statusCode }}`
- **Operation:** Between
- **Value 2:** `200`
- **Value 3:** `299`
**Routing:**
- **True:** Success Discord notification
- **False:** Error Discord notification
---
### Node 10a: Success Notification
**Node Type:** Discord
**Name:** Success Notification
**Credential:** Discord Ko-fi Notifications
**Message (JSON):**
```json
{
"content": "✅ Ko-fi Order Processed",
"embeds": [{
"title": "Paper Dynasty Packs Granted",
"color": 3066993,
"fields": [
{
"name": "Customer",
"value": "{{ $('Parse Ko-fi Form Data').item.json.from_name }}",
"inline": true
},
{
"name": "Team",
"value": "{{ $('Lookup Paper Dynasty Team').item.json.pd_team_abbrev }}",
"inline": true
},
{
"name": "Amount",
"value": "${{ $('Parse Ko-fi Form Data').item.json.amount }} {{ $('Parse Ko-fi Form Data').item.json.currency }}",
"inline": true
},
{
"name": "Packs Granted",
"value": "{{ $('Map Products to PD Packs').item.json.total_packs }} packs",
"inline": false
},
{
"name": "Products",
"value": "{{ $('Parse Ko-fi Form Data').item.json.shop_items.map(i => `${i.quantity}x ${i.variation_name}`).join('\\n') }}",
"inline": false
},
{
"name": "Transaction ID",
"value": "`{{ $('Parse Ko-fi Form Data').item.json.kofi_transaction_id }}`",
"inline": false
}
],
"timestamp": "{{ new Date().toISOString() }}"
}]
}
```
---
### Node 10b: Error Notification
**Node Type:** Discord
**Name:** Error Notification
**Credential:** Discord Ko-fi Notifications
**Message (JSON):**
```json
{
"content": "@here ⚠️ Ko-fi Order Processing Error",
"embeds": [{
"title": "Paper Dynasty API Error",
"color": 15158332,
"fields": [
{
"name": "Customer",
"value": "{{ $('Parse Ko-fi Form Data').item.json.from_name }} ({{ $('Parse Ko-fi Form Data').item.json.email }})",
"inline": false
},
{
"name": "Amount",
"value": "${{ $('Parse Ko-fi Form Data').item.json.amount }} {{ $('Parse Ko-fi Form Data').item.json.currency }}",
"inline": true
},
{
"name": "Team ID",
"value": "{{ $('Lookup Paper Dynasty Team').item.json.pd_team_id || 'Unknown' }}",
"inline": true
},
{
"name": "Products",
"value": "{{ $('Parse Ko-fi Form Data').item.json.shop_items.map(i => `${i.quantity}x ${i.variation_name}`).join('\\n') }}",
"inline": false
},
{
"name": "API Status",
"value": "{{ $('Paper Dynasty Grant Packs').item.json.statusCode || 'Request Failed' }}",
"inline": true
},
{
"name": "Error Message",
"value": "```{{ $('Paper Dynasty Grant Packs').item.json.body?.error || 'Unknown error' }}```",
"inline": false
},
{
"name": "Transaction ID",
"value": "`{{ $('Parse Ko-fi Form Data').item.json.kofi_transaction_id }}`",
"inline": false
},
{
"name": "Action Required",
"value": "Manual pack distribution needed. Use transaction ID to process manually.",
"inline": false
}
],
"timestamp": "{{ new Date().toISOString() }}"
}]
}
```
---
### Node 10c: Manual Review Notification
**Node Type:** Discord
**Name:** Manual Review Needed
**Credential:** Discord Ko-fi Notifications
**Message (JSON):**
```json
{
"content": "@here 🔍 Ko-fi Order Needs Manual Review",
"embeds": [{
"title": "Unable to Identify Paper Dynasty Team",
"color": 16776960,
"fields": [
{
"name": "Customer",
"value": "{{ $json.from_name }} ({{ $json.email }})",
"inline": false
},
{
"name": "Amount",
"value": "${{ $json.amount }} {{ $json.currency }}",
"inline": true
},
{
"name": "Discord User ID",
"value": "{{ $json.discord_userid || 'Not provided' }}",
"inline": true
},
{
"name": "Message",
"value": "{{ $json.message || 'No message' }}",
"inline": false
},
{
"name": "Products",
"value": "{{ $json.shop_items.map(i => `${i.quantity}x ${i.variation_name} (${i.direct_link_code})`).join('\\n') }}",
"inline": false
},
{
"name": "Transaction ID",
"value": "`{{ $json.kofi_transaction_id }}`",
"inline": false
},
{
"name": "Action Required",
"value": "1. Identify user's Paper Dynasty team\n2. Manually grant packs using Paper Dynasty API\n3. Reply to this message when resolved",
"inline": false
}
],
"timestamp": "{{ new Date().toISOString() }}"
}]
}
```
---
### Node 11: Respond to Webhook
**Node Type:** Respond to Webhook
**Name:** Return 200 OK
**Settings:**
- **Response Code:** 200
- **Response Body:** JSON
**Body:**
```json
{
"status": "success",
"message": "Order received and processed",
"execution_id": "{{ $execution.id }}"
}
```
**Note:** This node must execute after all processing to ensure Ko-fi receives the 200 status code.
## Workflow Visual Structure
```
[Webhook Trigger]
[Parse Ko-fi Data]
[Validate Token] → (false) → [Stop and Error]
↓ (true)
[Filter Shop Orders] → (false) → [Respond: Not shop order]
↓ (true)
[Lookup PD Team]
[Check Team Identified]
├─ (false) → [Discord: Manual Review] → [Respond to Webhook]
↓ (true)
[Map Products to PD Packs]
[PD API: Grant Packs]
[API Success Check]
├─ (true) → [Discord: Success]
└─ (false) → [Discord: Error]
[Respond to Webhook: 200 OK]
```
## Testing
### Phase 1: Development Testing
**Use Test Webhook Path:**
- Test URL: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
- This won't trigger production workflows
- View test executions in n8n UI
**Test with cURL:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_TOKEN_HERE",
"message_id": "test-001",
"timestamp": "2025-11-13T12:00:00Z",
"type": "Shop Order",
"is_public": true,
"from_name": "Test Customer",
"message": "Team: SKB",
"amount": "10.00",
"url": "https://ko-fi.com/test",
"email": "[email protected]",
"currency": "USD",
"kofi_transaction_id": "test-txn-001",
"shop_items": [
{
"direct_link_code": "pack-standard-5",
"variation_name": "5-Pack Standard",
"quantity": 1
}
],
"discord_userid": "012345678901234567"
}'
```
### Phase 2: Ko-fi Test Webhook
**Configure Ko-fi:**
1. Ko-fi → API → Webhooks
2. Set URL: `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
3. Use Ko-fi's "Test Webhook" button
4. Verify n8n receives and processes correctly
### Phase 3: Test Purchases
**Create Test Products:**
1. Ko-fi → Shop → Add Product
2. Set product code (e.g., `pack-test-1`)
3. Price: $0.01 (minimum)
4. Make test purchase
5. Verify workflow processes correctly
6. Check Discord for notification
7. Verify packs granted in Paper Dynasty
### Phase 4: Production Deployment
**Switch to Production:**
1. Update webhook path in workflow from `/webhook-test/` to `/webhook/`
2. Update Ko-fi webhook URL to production path
3. Activate workflow in n8n
4. Monitor first 10 real transactions closely
5. Verify all Discord notifications working
6. Check Paper Dynasty pack grants are correct
## Troubleshooting
### Webhook Not Triggering
**Check:**
- Workflow is Active (toggle in n8n)
- Webhook path matches Ko-fi configuration
- n8n is accessible from internet
- NPM proxy host is running
**Test:**
```bash
curl -I https://n8n.manticorum.com/webhook/kofi-paperdy
# Should return: HTTP/1.1 200 OK or 404 (workflow not active)
```
### Invalid Token Error
**Check:**
- Token in n8n credentials matches Ko-fi dashboard
- No extra whitespace in token
- Credential name referenced correctly in IF node
**Fix:**
- Re-copy token from Ko-fi
- Update n8n credential
- Test workflow again
### Team Not Found
**Causes:**
- Discord user ID not linked to Paper Dynasty account
- Team abbrev not in message field
- Typo in team abbreviation
**Resolution:**
- Manual review Discord notification sent
- Admin manually grants packs
- Update user's PD profile with correct gmid
### API Call Failing
**Check:**
- API key is valid
- Paper Dynasty API is online
- Request payload format is correct
- Team ID exists in database
**Debug:**
- View full API response in n8n execution log
- Test API call manually with curl
- Check Paper Dynasty API logs
### Packs Not Granted
**Verify:**
- API returned 200 status
- pack_type_id is valid
- team_id exists
- Correct number of packs in request
**Check in Paper Dynasty:**
```bash
# Using Paper Dynasty API
curl -H "Authorization: Bearer YOUR_KEY" \
"https://pd.manticorum.com/api/v2/packs?team_id=69&opened=false&limit=20"
```
## Monitoring
### n8n Execution Logs
**View Executions:**
1. n8n → Executions tab
2. Filter by workflow: "Ko-fi to Paper Dynasty"
3. Review successful and failed executions
4. Click execution to see node-by-node data flow
### Discord Notifications
**Monitor Channel:**
- All successful orders → success notification
- All errors → error notification with @here mention
- Manual reviews → notification with customer details
### Paper Dynasty Verification
**Verify Packs Granted:**
```python
from api_client import PaperDynastyAPI
api = PaperDynastyAPI(environment='prod')
# Check recent packs
recent_packs = api.list_packs(opened=False, new_to_old=True, limit=50)
# Filter for Ko-fi source (if transaction_id stored)
kofi_packs = [p for p in recent_packs if 'kofi' in str(p.get('metadata', {}))]
```
## Maintenance
### Weekly Tasks
- [ ] Review failed executions in n8n
- [ ] Check for unknown product codes
- [ ] Verify all manual review items resolved
- [ ] Monitor API response times
### Monthly Tasks
- [ ] Update product mapping for new Ko-fi products
- [ ] Review and optimize workflow performance
- [ ] Backup n8n database
- [ ] Test disaster recovery procedure
### Quarterly Tasks
- [ ] Rotate Ko-fi verification token
- [ ] Rotate Paper Dynasty API key
- [ ] Review workflow for optimization opportunities
- [ ] Update documentation with lessons learned
## References
- **Ko-fi Webhook Docs:** https://help.ko-fi.com/hc/en-us/articles/360004162298
- **Paper Dynasty API:** `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
- **n8n Documentation:** https://docs.n8n.io/
- **n8n Webhook Node:** https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/
## Change Log
### 2025-11-13 - Initial Design
- Created workflow specification
- Defined multi-method team identification
- Configured product mapping structure
- Designed error handling and manual review process

View File

@ -0,0 +1,178 @@
{
"_comment": "Ko-fi Product Mapping Template for Paper Dynasty",
"_instructions": "1. Copy this template to n8n → Settings → Variables → Create Variable 'KOFI_PRODUCT_MAP'",
"_instructions_2": "2. Update product codes to match your Ko-fi shop 'direct_link_code' values",
"_instructions_3": "3. Set pack_type_id according to Paper Dynasty pack types (see pack_types reference below)",
"_instructions_4": "4. Use pack_cardset_id for cardset-specific packs (or null for current/default cardset)",
"_pack_types_reference": {
"1": "Standard",
"2": "Starter",
"3": "Premium",
"4": "Check-In Player",
"5": "MVP",
"6": "All Star",
"7": "Mario",
"8": "Team Choice (1 card, choice of 4 from selected MLB club)",
"9": "Promo Choice"
},
"_naming_conventions": {
"format": "pack-{tier}-{quantity}",
"examples": [
"pack-standard-5 (5 standard packs)",
"pack-premium-10 (10 premium packs)",
"pack-team-choice (1 team choice pack)",
"pack-mvp-bundle (special bundle)",
"sub-monthly-5packs (monthly subscription with 5 packs)"
],
"best_practices": [
"Use lowercase with hyphens",
"Be descriptive but concise",
"Include quantity in name if variable",
"Use consistent prefixes (pack-, sub-, card-)"
]
},
"pack-standard-5": {
"name": "5-Pack Standard Bundle",
"description": "Five standard packs",
"pack_type_id": 1,
"quantity": 5,
"pack_cardset_id": null,
"price_usd": 5.00
},
"pack-standard-10": {
"name": "10-Pack Standard Bundle",
"description": "Ten standard packs - Best Value!",
"pack_type_id": 1,
"quantity": 10,
"pack_cardset_id": null,
"price_usd": 9.00
},
"pack-premium-5": {
"name": "5-Pack Premium Bundle",
"description": "Five premium packs with better odds",
"pack_type_id": 3,
"quantity": 5,
"pack_cardset_id": null,
"price_usd": 10.00
},
"pack-premium-10": {
"name": "10-Pack Premium Bundle",
"description": "Ten premium packs - Maximum Value!",
"pack_type_id": 3,
"quantity": 10,
"pack_cardset_id": null,
"price_usd": 18.00
},
"pack-team-choice": {
"name": "Team Choice Pack",
"description": "One card, choice of 4 from your favorite MLB team",
"pack_type_id": 8,
"quantity": 1,
"pack_cardset_id": 27,
"price_usd": 3.00
},
"pack-mvp-special": {
"name": "MVP Special Pack",
"description": "Special MVP rarity pack",
"pack_type_id": 5,
"quantity": 1,
"pack_cardset_id": null,
"price_usd": 15.00
},
"pack-allstar-bundle": {
"name": "All-Star 3-Pack Bundle",
"description": "Three All-Star rarity packs",
"pack_type_id": 6,
"quantity": 3,
"pack_cardset_id": null,
"price_usd": 12.00
},
"sub-monthly-standard": {
"name": "Monthly Subscription - Standard",
"description": "Monthly subscription with 5 standard packs",
"pack_type_id": 1,
"quantity": 5,
"pack_cardset_id": null,
"price_usd": 5.00,
"subscription": true,
"subscription_period": "monthly"
},
"sub-monthly-premium": {
"name": "Monthly Subscription - Premium",
"description": "Monthly subscription with 5 premium packs",
"pack_type_id": 3,
"quantity": 5,
"pack_cardset_id": null,
"price_usd": 10.00,
"subscription": true,
"subscription_period": "monthly"
},
"_example_seasonal_products": {
"pack-2005-throwback": {
"name": "2005 Throwback Pack",
"description": "Limited edition 2005 season packs",
"pack_type_id": 1,
"quantity": 3,
"pack_cardset_id": 27,
"price_usd": 7.00,
"limited_edition": true
},
"pack-holiday-2025": {
"name": "Holiday Special 2025",
"description": "Holiday bundle with 10 mixed packs",
"pack_type_id": 3,
"quantity": 10,
"pack_cardset_id": null,
"price_usd": 20.00,
"limited_edition": true,
"available_until": "2025-12-31"
}
},
"_usage_examples": {
"simple_product": {
"code": "pack-simple",
"config": {
"pack_type_id": 1,
"quantity": 5,
"pack_cardset_id": null
}
},
"cardset_specific": {
"code": "pack-2005-special",
"config": {
"pack_type_id": 1,
"quantity": 3,
"pack_cardset_id": 27
}
},
"with_metadata": {
"code": "pack-promo-code",
"config": {
"pack_type_id": 1,
"quantity": 5,
"pack_cardset_id": null,
"promo_code": "WELCOME2025",
"tracking": {
"source": "email_campaign",
"campaign_id": "welcome-2025-q1"
}
}
}
}
}

View File

@ -0,0 +1,667 @@
# Ko-fi → Paper Dynasty Testing Guide
Comprehensive testing guide for the Ko-fi integration workflow.
## Testing Phases
### Phase 1: Local Development Testing
Test n8n workflow logic without involving Ko-fi.
**Objective:** Validate workflow nodes process data correctly
**Method:** Manual execution in n8n UI
**Steps:**
1. Open workflow in n8n
2. Click "Execute Workflow" button
3. Workflow will wait at Webhook node
4. Use cURL or Postman to send test payload
5. Review execution results node-by-node
---
### Phase 2: Webhook Testing with cURL
Test webhook endpoint with simulated Ko-fi payloads.
**Objective:** Verify webhook receives and parses Ko-fi data
**Test URL:** `https://n8n.manticorum.com/webhook-test/kofi-paperdy`
---
### Phase 3: Ko-fi Test Webhook
Use Ko-fi's built-in webhook tester.
**Objective:** Validate integration with Ko-fi's actual webhook system
**Steps:**
1. Ko-fi → Settings → API → Webhooks
2. Set webhook URL to test path
3. Click "Test Webhook" button
4. Review n8n execution logs
---
### Phase 4: Test Purchases
Make real purchases with test products.
**Objective:** End-to-end validation with real Ko-fi data
**Steps:**
1. Create test products in Ko-fi shop ($0.01 minimum)
2. Make test purchases
3. Verify packs granted in Paper Dynasty
4. Check Discord notifications
---
### Phase 5: Production Deployment
Switch to production webhook path and monitor.
**Objective:** Deploy to production with confidence
**Steps:**
1. Switch webhook path from `/webhook-test/` to `/webhook/`
2. Update Ko-fi webhook URL
3. Monitor first 10 transactions manually
4. Verify all flows working correctly
---
## Test Payloads
### Test 1: Basic Order with Discord User ID
**Scenario:** User with linked Discord account purchases 5 standard packs
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "test-001-discord",
"timestamp": "2025-11-13T12:00:00Z",
"type": "Shop Order",
"is_public": true,
"from_name": "Test Customer Discord",
"message": "Thanks for the packs!",
"amount": "5.00",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-001",
"email": "[email protected]",
"currency": "USD",
"is_subscription_payment": false,
"is_first_subscription_payment": false,
"kofi_transaction_id": "test-001-discord-userid",
"shop_items": [
{
"direct_link_code": "pack-standard-5",
"variation_name": "5-Pack Standard",
"quantity": 1
}
],
"tier_name": null,
"shipping": null,
"discord_username": "TestUser#1234",
"discord_userid": "012345678901234567"
}'
```
**Expected Result:**
- ✅ Token validated
- ✅ Team found via discord_userid → gmid lookup
- ✅ 5 standard packs granted to team
- ✅ Success Discord notification sent
---
### Test 2: Order with Team Abbrev in Message
**Scenario:** User without Discord link includes team abbreviation in message
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "test-002-abbrev",
"timestamp": "2025-11-13T12:05:00Z",
"type": "Shop Order",
"is_public": true,
"from_name": "Test Customer Abbrev",
"message": "Team: SKB - Thanks!",
"amount": "10.00",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-002",
"email": "[email protected]",
"currency": "USD",
"is_subscription_payment": false,
"is_first_subscription_payment": false,
"kofi_transaction_id": "test-002-team-abbrev",
"shop_items": [
{
"direct_link_code": "pack-premium-10",
"variation_name": "10-Pack Premium",
"quantity": 1
}
],
"tier_name": null,
"shipping": null,
"discord_username": null,
"discord_userid": null
}'
```
**Expected Result:**
- ✅ Token validated
- ✅ Team "SKB" extracted from message
- ✅ Team found via abbrev lookup
- ✅ 10 premium packs granted to team
- ✅ Success Discord notification sent
---
### Test 3: Multiple Items in Single Order
**Scenario:** User purchases multiple product types in one transaction
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "test-003-multi",
"timestamp": "2025-11-13T12:10:00Z",
"type": "Shop Order",
"is_public": true,
"from_name": "Test Customer Multi",
"message": "CAR",
"amount": "25.00",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-003",
"email": "[email protected]",
"currency": "USD",
"is_subscription_payment": false,
"is_first_subscription_payment": false,
"kofi_transaction_id": "test-003-multiple-items",
"shop_items": [
{
"direct_link_code": "pack-standard-5",
"variation_name": "5-Pack Standard",
"quantity": 2
},
{
"direct_link_code": "pack-premium-5",
"variation_name": "5-Pack Premium",
"quantity": 1
},
{
"direct_link_code": "pack-team-choice",
"variation_name": "Team Choice Pack",
"quantity": 1
}
],
"tier_name": null,
"shipping": null,
"discord_username": null,
"discord_userid": null
}'
```
**Expected Result:**
- ✅ Token validated
- ✅ Team "CAR" found via abbrev
- ✅ 15 total packs granted (10 standard + 5 premium + 1 team choice)
- ✅ Success Discord notification with itemized list
---
### Test 4: Team Not Found (Manual Review)
**Scenario:** User provides neither Discord ID nor valid team abbrev
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "test-004-unknown",
"timestamp": "2025-11-13T12:15:00Z",
"type": "Shop Order",
"is_public": true,
"from_name": "Unknown User",
"message": "I love Paper Dynasty!",
"amount": "5.00",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-004",
"email": "[email protected]",
"currency": "USD",
"is_subscription_payment": false,
"is_first_subscription_payment": false,
"kofi_transaction_id": "test-004-manual-review",
"shop_items": [
{
"direct_link_code": "pack-standard-5",
"variation_name": "5-Pack Standard",
"quantity": 1
}
],
"tier_name": null,
"shipping": null,
"discord_username": null,
"discord_userid": null
}'
```
**Expected Result:**
- ✅ Token validated
- ❌ No team identified
- ⚠️ Manual review Discord notification sent with @here mention
- ⚠️ Admin grants packs manually after identifying user
---
### Test 5: Unknown Product Code
**Scenario:** Ko-fi product code not in mapping
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "test-005-unknown-product",
"timestamp": "2025-11-13T12:20:00Z",
"type": "Shop Order",
"is_public": true,
"from_name": "Test Unknown Product",
"message": "SKB",
"amount": "99.99",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-005",
"email": "[email protected]",
"currency": "USD",
"is_subscription_payment": false,
"is_first_subscription_payment": false,
"kofi_transaction_id": "test-005-unknown-product",
"shop_items": [
{
"direct_link_code": "pack-does-not-exist",
"variation_name": "Mystery Pack",
"quantity": 1
}
],
"tier_name": null,
"shipping": null,
"discord_username": null,
"discord_userid": null
}'
```
**Expected Result:**
- ✅ Token validated
- ✅ Team "SKB" found
- ❌ Product code "pack-does-not-exist" not in mapping
- ⚠️ Manual review Discord notification
- ⚠️ Admin adds product to mapping or processes manually
---
### Test 6: Invalid Verification Token
**Scenario:** Test security - invalid Ko-fi token
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "INVALID-TOKEN-12345",
"message_id": "test-006-invalid",
"timestamp": "2025-11-13T12:25:00Z",
"type": "Shop Order",
"is_public": true,
"from_name": "Hacker Attempt",
"message": "SKB",
"amount": "1000.00",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-006",
"email": "[email protected]",
"currency": "USD",
"kofi_transaction_id": "test-006-security",
"shop_items": [
{
"direct_link_code": "pack-standard-5",
"variation_name": "5-Pack Standard",
"quantity": 100
}
],
"discord_userid": null
}'
```
**Expected Result:**
- ❌ Token validation fails
- ⛔ Workflow stops at validation node
- ⛔ NO packs granted
- ⛔ NO Discord notification (security - don't reveal valid tokens)
---
### Test 7: Non-Shop Order Type
**Scenario:** Ko-fi sends donation (not shop order)
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "test-007-donation",
"timestamp": "2025-11-13T12:30:00Z",
"type": "Donation",
"is_public": true,
"from_name": "Generous Donor",
"message": "Keep up the great work!",
"amount": "10.00",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-007",
"email": "[email protected]",
"currency": "USD",
"is_subscription_payment": false,
"kofi_transaction_id": "test-007-donation",
"shop_items": null,
"discord_userid": null
}'
```
**Expected Result:**
- ✅ Token validated
- Type is "Donation" not "Shop Order"
- ⏭️ Workflow skips processing (no packs to grant)
- ✅ Returns 200 OK to Ko-fi
---
### Test 8: Monthly Subscription Payment
**Scenario:** Recurring subscription payment
**cURL Command:**
```bash
curl -X POST 'https://n8n.manticorum.com/webhook-test/kofi-paperdy' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'data={
"verification_token": "YOUR_KOFI_TOKEN_HERE",
"message_id": "test-008-subscription",
"timestamp": "2025-11-13T12:35:00Z",
"type": "Subscription",
"is_public": true,
"from_name": "Monthly Subscriber",
"message": "PPD",
"amount": "5.00",
"url": "https://ko-fi.com/Home/CoffeeShop?txid=test-008",
"email": "[email protected]",
"currency": "USD",
"is_subscription_payment": true,
"is_first_subscription_payment": false,
"kofi_transaction_id": "test-008-subscription",
"shop_items": [
{
"direct_link_code": "sub-monthly-standard",
"variation_name": "Monthly Standard Subscription",
"quantity": 1
}
],
"tier_name": "Standard Member",
"discord_username": "Subscriber#5678",
"discord_userid": "987654321098765432"
}'
```
**Expected Result:**
- ✅ Token validated
- ✅ Subscription payment processed as shop order
- ✅ 5 standard packs granted
- ✅ Success notification includes subscription tier
---
## Testing Checklist
### Pre-Deployment Testing
- [ ] **Test 1:** Basic order with Discord user ID ✅
- [ ] **Test 2:** Order with team abbrev in message ✅
- [ ] **Test 3:** Multiple items in single order ✅
- [ ] **Test 4:** Team not found (manual review) ⚠️
- [ ] **Test 5:** Unknown product code ⚠️
- [ ] **Test 6:** Invalid verification token ❌
- [ ] **Test 7:** Non-shop order type (donation) ⏭️
- [ ] **Test 8:** Monthly subscription payment ✅
### Integration Testing
- [ ] Ko-fi webhook test button works
- [ ] Discord notifications received in correct channel
- [ ] Discord @here mentions work for errors
- [ ] Paper Dynasty API grants packs correctly
- [ ] API retry logic works (simulate timeout)
- [ ] Multiple webhooks handled concurrently
### Security Testing
- [ ] Invalid tokens rejected
- [ ] No sensitive data in Discord notifications
- [ ] Webhook accessible only via HTTPS
- [ ] n8n basic auth required for UI access
- [ ] Credentials encrypted in n8n database
### Performance Testing
- [ ] Webhook responds within 5 seconds
- [ ] Paper Dynasty API calls timeout correctly
- [ ] Large orders (20+ items) process successfully
- [ ] Concurrent webhooks don't cause issues
### Edge Cases
- [ ] Empty shop_items array
- [ ] Null discord_userid and message fields
- [ ] Special characters in team abbrev
- [ ] Very large quantity (100+ packs)
- [ ] Product with quantity: 0
- [ ] Duplicate message_id (Ko-fi retry)
### Monitoring & Alerts
- [ ] Success notifications readable
- [ ] Error notifications actionable
- [ ] Manual review requests clear
- [ ] n8n execution logs captured
- [ ] Failed executions visible in n8n UI
---
## Validation Scripts
### Verify Ko-fi Webhook Format
**Script:** `validate-kofi-payload.js`
```javascript
// Run in n8n Code node or standalone Node.js
function validateKofiPayload(payload) {
const required = [
'verification_token',
'message_id',
'timestamp',
'type',
'from_name',
'amount',
'email',
'currency',
'kofi_transaction_id'
];
const missing = required.filter(field => !payload[field]);
if (missing.length > 0) {
throw new Error(`Missing required fields: ${missing.join(', ')}`);
}
if (payload.type === 'Shop Order' && !payload.shop_items) {
throw new Error('Shop Order missing shop_items array');
}
console.log('✅ Payload valid');
return true;
}
// Test
const testPayload = JSON.parse($json.body.data);
validateKofiPayload(testPayload);
```
### Verify Paper Dynasty Pack Grant
**Script:** `verify-pack-grant.py`
```python
#!/usr/bin/env python3
"""Verify packs were granted in Paper Dynasty"""
from api_client import PaperDynastyAPI
from datetime import datetime, timezone
api = PaperDynastyAPI(environment='prod', verbose=True)
# Get packs created in last hour
recent_packs = api.list_packs(opened=False, new_to_old=True, limit=100)
# Filter by timestamp
one_hour_ago = datetime.now(timezone.utc).timestamp() - 3600
for pack in recent_packs:
pack_time = pack.get('created_at', 0) / 1000
if pack_time > one_hour_ago:
team = api.get_team(team_id=pack['team'])
print(f"✅ Pack {pack['id']} granted to {team['abbrev']} at {datetime.fromtimestamp(pack_time)}")
```
---
## Common Issues During Testing
### Issue: Webhook 404 Not Found
**Cause:** Workflow not active or wrong webhook path
**Fix:**
```bash
# Check workflow is active in n8n UI
# Verify webhook path: n8n → Workflow → Webhook node → Path
# Test webhook accessibility
curl -I https://n8n.manticorum.com/webhook-test/kofi-paperdy
# Should return: 200 OK or 400 Bad Request (not 404)
```
### Issue: Token Validation Failing
**Cause:** Token mismatch or credential not configured
**Fix:**
1. Copy token exactly from Ko-fi dashboard
2. Check n8n credential name matches IF node reference
3. No extra spaces or quotes in token value
4. Re-create credential if needed
### Issue: Team Not Found
**Cause:** Discord user ID or team abbrev doesn't exist in PD
**Fix:**
```python
# Check if discord_userid exists in PD
api.get('teams', params={'gmid': '012345678901234567'})
# Check if team abbrev exists
api.get('teams', params={'abbrev': 'SKB'})
# If not found, update user's PD profile or use manual review
```
### Issue: Packs Not Granted
**Cause:** API request succeeded but packs not visible
**Fix:**
1. Check API response body in n8n execution log
2. Verify team_id is correct
3. Check pack_type_id is valid
4. Query PD database directly:
```python
api.list_packs(team_id=69, opened=False, limit=20)
```
### Issue: Discord Notification Not Sent
**Cause:** Discord webhook URL invalid or permissions issue
**Fix:**
1. Test Discord webhook directly:
```bash
curl -X POST 'DISCORD_WEBHOOK_URL' \
-H 'Content-Type: application/json' \
-d '{"content": "Test notification"}'
```
2. Regenerate webhook in Discord if needed
3. Update n8n credential with new URL
---
## Production Monitoring
### Daily Checks
- [ ] Review n8n execution logs (filter by workflow)
- [ ] Check for failed executions
- [ ] Review manual review Discord notifications
- [ ] Verify all orders processed successfully
### Weekly Checks
- [ ] Review unknown product codes
- [ ] Update product mapping for new Ko-fi products
- [ ] Check Paper Dynasty pack distribution accuracy
- [ ] Review API response times
### Monthly Checks
- [ ] Test disaster recovery procedure
- [ ] Backup n8n database
- [ ] Rotate Ko-fi verification token
- [ ] Update documentation with lessons learned
---
## References
- **Main Workflow Documentation:** `kofi-paper-dynasty.md`
- **Product Mapping Template:** `kofi-product-mapping-template.json`
- **Ko-fi Webhook Docs:** https://help.ko-fi.com/hc/en-us/articles/360004162298
- **Paper Dynasty API:** `/home/cal/.claude/skills/paper-dynasty/SKILL.md`
## Change Log
### 2025-11-13 - Initial Version
- Created comprehensive testing guide
- Defined 8 test scenarios with cURL commands
- Added validation scripts and troubleshooting
- Documented testing checklist and monitoring procedures

View File

@ -0,0 +1,438 @@
# Productivity Tools Troubleshooting Guide
## Task Manager Issues
### Problem: Commands not found
**Symptoms**: `task: command not found` or `task-dashboard: command not found`
**Root Cause**: PATH not configured or shell not reloaded
### Solution: PATH Configuration
1. **Verify PATH addition** in `~/.bashrc`:
```bash
grep "task-manager" ~/.bashrc
# Should show: export PATH="$HOME/.claude/tools/task-manager:$PATH"
```
2. **Reload shell configuration**:
```bash
source ~/.bashrc
# Or open new terminal window/tab
```
3. **Test commands**:
```bash
which task
which task-dashboard
# Both should show: /home/cal/.claude/tools/task-manager/[command]
```
### Temporary Workaround
Use full paths until shell reloaded:
```bash
~/.claude/tools/task-manager/task
~/.claude/tools/task-manager/task-dashboard
```
## Dashboard Display Issues
### Problem: Dashboard not updating after CLI changes
**Symptoms**: Run `task dump "something"` but dashboard doesn't show new task
**Root Cause**: File system delay or dashboard not running
### Solution: Verify Dashboard Operation
1. **Check dashboard is running**:
```bash
ps aux | grep dashboard.py
```
2. **Dashboard auto-refreshes every 1 second** - wait briefly
3. **If stuck, restart dashboard**:
- Ctrl+C to stop
- Restart: `task-dashboard`
4. **Verify data file exists**:
```bash
ls -la ~/.claude/tools/task-manager/tasks.json
cat ~/.claude/tools/task-manager/tasks.json
```
### Problem: Dashboard rendering issues (broken characters, layout problems)
**Symptoms**: Terminal shows garbled text, boxes not aligned
**Cause**: Terminal too small or doesn't support Unicode
### Solution: Terminal Requirements
1. **Minimum terminal size**: 80x24 characters
2. **Resize terminal window** to be larger
3. **Ensure Unicode support**:
```bash
echo $LANG
# Should show UTF-8 (e.g., en_US.UTF-8)
```
4. **Test rich library**:
```bash
python3 -c "from rich.console import Console; Console().print('[green]✓[/green] Unicode works')"
```
## Task Management Issues
### Problem: High priority tasks not loading first
**Symptoms**: Low/medium priority task becomes current before high priority task
**Cause**: High priority task already loaded earlier or wrong priority flag used
### Solution: Priority System Understanding
**Priority Order** (automatic):
1. High priority tasks (🔴) - `--high` flag
2. Medium priority tasks (🟡) - `--medium` flag or default
3. Low priority tasks (🟢) - `--low` flag
**Check task priorities**:
```bash
task
# Shows all tasks with priority emojis
```
**Fix task priority** (workaround):
```bash
# Currently can't change priority - delete and re-add:
task next # Skip past wrong-priority task
task dump "correct description" --high # Re-add with right priority
```
### Problem: Task completed but still shows as current
**Symptoms**: After `task done`, same task still in current focus
**Cause**: Command failed or data file locked
### Solution: Verify Completion
1. **Check task status**:
```bash
task
# Should show different current task or empty
```
2. **Check data file**:
```bash
cat ~/.claude/tools/task-manager/tasks.json | python3 -m json.tool
# Look for tasks with status: "completed"
```
3. **Force move to next**:
```bash
task next # Skip to next task manually
```
### Problem: Snoozed task not waking up
**Symptoms**: Task snoozed hours ago but not appearing
**Cause**: Wake-up time not reached or system checks only on command execution
### Solution: Snooze Behavior
**How snooze works**:
- Task marked with future wake-up timestamp
- Next task auto-loaded immediately
- Snoozed task returns to queue when `snoozed_until` time passes
- **Only checked when commands run** (not automatic background wake)
**Trigger wake-up check**:
```bash
task done # Complete current task
# System checks snoozed tasks and loads if ready
```
**Check snoozed tasks manually**:
```bash
cat ~/.claude/tools/task-manager/tasks.json | grep -A 5 '"status": "snoozed"'
```
## Data and Persistence Issues
### Problem: Tasks disappearing or data loss
**Symptoms**: Tasks added but gone after restart
**Cause**: File write permissions or corrupted JSON
### Solution: Data File Verification
1. **Check file permissions**:
```bash
ls -la ~/.claude/tools/task-manager/tasks.json
# Should be writable by user
```
2. **Fix permissions**:
```bash
chmod 644 ~/.claude/tools/task-manager/tasks.json
```
3. **Validate JSON format**:
```bash
python3 -m json.tool ~/.claude/tools/task-manager/tasks.json
# Should show formatted JSON without errors
```
4. **Check for corruption**:
```bash
# Backup current file
cp ~/.claude/tools/task-manager/tasks.json ~/.claude/tools/task-manager/tasks.json.backup
# Try to load with Python
python3 -c "import json; json.load(open('$HOME/.claude/tools/task-manager/tasks.json'))"
```
### Problem: Data file doesn't exist
**Symptoms**: `task` command shows errors about missing file
**Cause**: First run or file accidentally deleted
### Solution: Initialize Data File
**Data file auto-creates on first use**:
```bash
task dump "First task"
# Creates tasks.json automatically
```
**Manual initialization** (if needed):
```bash
cat > ~/.claude/tools/task-manager/tasks.json << 'EOF'
{
"tasks": [],
"last_updated": "2025-01-01T00:00:00"
}
EOF
```
## Python Dependency Issues
### Problem: rich library not found
**Symptoms**: `ModuleNotFoundError: No module named 'rich'`
**Cause**: rich library not installed
### Solution: Install Dependencies
```bash
pip3 install --user rich
```
### Problem: Python version incompatibility
**Symptoms**: Syntax errors or import failures
**Cause**: Python version too old (requires 3.7+)
### Solution: Check Python Version
```bash
python3 --version
# Should be 3.7 or higher
```
If too old, use system Python 3 or install newer version
## Workflow and Usage Issues
### Problem: Forgetting to check dashboard
**Symptoms**: Dashboard running but never looking at it
**Cause**: Not visible enough, muscle memory not formed
### Solution: Visibility Improvements
1. **Use tmux/screen persistent split**:
```bash
tmux split-window -h task-dashboard
# Always visible in split pane
```
2. **Dedicated terminal on second monitor** - always visible
3. **Position terminal window** to always be on top/visible
4. **Build muscle memory**:
- Use `task` command frequently to check status
- Set reminders to check dashboard first 2 weeks
- Terminal placement habit formation
### Problem: Still context switching instead of brain dumping
**Symptoms**: Remembering something and immediately switching tasks
**Cause**: Old habit pattern, not yet muscle memory
### Solution: Habit Building
**Interrupt pattern**:
1. Notice you're about to switch → STOP
2. Type: `task dump "[the thing]"`
3. See it appear in brain dump
4. Continue current work
**Muscle memory building**:
- Put note on monitor: "DUMP DON'T SWITCH"
- First week: Expect to fail, keep practicing
- Second week: Start catching yourself
- Third week: Becomes automatic
**Track progress**:
```bash
task
# Check completed today - celebrate wins!
```
## Performance Issues
### Problem: Dashboard slow or laggy
**Symptoms**: Dashboard updates slowly or terminal sluggish
**Cause**: Too many tasks in JSON file or terminal performance
### Solution: Data Cleanup
1. **Archive completed tasks**:
```bash
# Manual cleanup (create archive script if needed)
# Edit tasks.json to remove old completed tasks
python3 -c "
import json
from datetime import datetime, timedelta
with open('$HOME/.claude/tools/task-manager/tasks.json', 'r') as f:
data = json.load(f)
# Keep only last 7 days of completed tasks
week_ago = (datetime.now() - timedelta(days=7)).isoformat()
data['tasks'] = [
t for t in data['tasks']
if t['status'] != 'completed' or t.get('completed_at', '') > week_ago
]
with open('$HOME/.claude/tools/task-manager/tasks.json', 'w') as f:
json.dump(data, f, indent=2)
"
```
2. **Check terminal performance**:
- Try different terminal emulator
- Reduce dashboard refresh rate (edit dashboard.py)
### Problem: Commands slow to execute
**Symptoms**: `task dump` takes several seconds
**Cause**: Large JSON file or slow file I/O
### Solution: Optimize Data Storage
- Keep tasks.json under 1000 tasks
- Archive old completed tasks monthly
- Consider SSD storage if on HDD
## Integration Issues
### Problem: Dashboard not visible in tmux/screen
**Symptoms**: Dashboard starts but can't see in multiplexer
**Cause**: Rich library rendering issues in some multiplexers
### Solution: Terminal Compatibility
1. **Set TERM variable**:
```bash
export TERM=xterm-256color
task-dashboard
```
2. **Try alternate terminal**:
```bash
# Instead of tmux, try dedicated terminal window
```
3. **Check tmux/screen version** - update if old
## Emergency Recovery
### Complete Reset
```bash
# Stop dashboard
pkill -f dashboard.py
# Backup current data
mv ~/.claude/tools/task-manager/tasks.json \
~/.claude/tools/task-manager/tasks.json.backup
# Reinitialize
task dump "Test task"
# Verify working
task
# Restart dashboard
task-dashboard
```
### Restore from Backup
```bash
# If you have backup file
cp ~/.claude/tools/task-manager/tasks.json.backup \
~/.claude/tools/task-manager/tasks.json
# Verify restore
task
```
### Reinstall System
```bash
# Scripts already installed at:
# ~/.claude/tools/task-manager/
# Just reload PATH
source ~/.bashrc
# Initialize fresh data
task dump "Fresh start"
```
## Common Error Patterns
### "No module named 'task_manager'"
**Cause**: Running Python scripts directly from wrong directory
**Solution**: Use `task` and `task-dashboard` commands, not direct Python execution
### "Permission denied"
**Cause**: Scripts not executable
**Solution**:
```bash
chmod +x ~/.claude/tools/task-manager/task*
```
### Dashboard exits immediately
**Cause**: Python exception or Ctrl+C pressed
**Solution**: Run with error output:
```bash
python3 ~/.claude/tools/task-manager/dashboard.py
# See any error messages
```
## Prevention Best Practices
1. **Keep dashboard running** in persistent pane
2. **Use `task dump` immediately** when thoughts appear
3. **Trust the system** - don't manually organize JSON file
4. **Regular status checks** - `task` command throughout day
5. **Weekly review** - clean up stale tasks
6. **Back up data** - tasks.json to cloud storage
7. **Build muscle memory** - takes 2-3 weeks of consistent use
## Debug Mode
### Enable Detailed Logging
Add to Python scripts if troubleshooting needed:
```python
# In task_manager.py or cli.py
import logging
logging.basicConfig(level=logging.DEBUG,
filename='/tmp/task-manager-debug.log')
```
### Check Logs
```bash
tail -f /tmp/task-manager-debug.log
```
This troubleshooting guide addresses common issues with the ADHD task management system and provides recovery procedures for all known failure modes.