# Sendy + n8n Integration Guide for Rust Meetup 2025\n\n## Overview\n\nComplete integration guide for connecting Sendy (self-hosted email marketing) with your event management stack using n8n for workflow automation on Kubernetes.\n\n## Architecture Overview\n\n```plaintext\nEvent Registration Flow:\nQR Code/Landing Page → Typebot → n8n → Sendy + Baserow + Formbricks\n ↓\nLive Event Polls → Claper → n8n → Sendy Segments + Analytics\n ↓\nPost-Event Follow-up → Automated Email Sequences + Survey Distribution\n```\n\n## Sendy API Integration\n\n### Authentication & Setup\n\n```bash\n# Sendy API Configuration\nSENDY_API_KEY="your-api-key"\nSENDY_INSTALLATION_URL="https://sendy.yourdomain.com"\nSENDY_LIST_ID="your-list-id"\n```\n\n### Key API Endpoints\n\n```javascript\n// Subscribe endpoint\nPOST /api/subscribe.php\n{\n "api_key": "your-api-key",\n "email": "attendee@example.com",\n "list": "list-id",\n "name": "Attendee Name",\n "custom_fields": {\n "registration_source": "rust-meetup-2025",\n "interest_level": "high",\n "experience": "intermediate"\n }\n}\n\n// Unsubscribe endpoint\nPOST /api/unsubscribe.php\n\n// Get subscriber status\nPOST /api/subscriber-status.php\n\n// Create campaign\nPOST /api/create-send.php\n```\n\n## n8n Kubernetes Deployment\n\n### Namespace and ConfigMap\n\n```yaml\n# n8n-namespace.yaml\napiVersion: v1\nkind: Namespace\nmetadata:\n name: n8n-automation\n labels:\n name: n8n-automation\n app: event-automation\n\n---\n# n8n-config.yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n name: n8n-config\n namespace: n8n-automation\ndata:\n N8N_BASIC_AUTH_ACTIVE: "true"\n N8N_BASIC_AUTH_USER: "admin"\n N8N_HOST: "n8n.rust-meetup.local"\n N8N_PORT: "5678"\n N8N_PROTOCOL: "https"\n WEBHOOK_URL: "https://n8n.rust-meetup.local"\n GENERIC_TIMEZONE: "Europe/Madrid"\n N8N_METRICS: "true"\n N8N_LOG_LEVEL: "info"\n N8N_EDITOR_BASE_URL: "https://n8n.rust-meetup.local"\n N8N_DISABLE_UI: "false"\n N8N_PERSONALIZATION_ENABLED: "false"\n N8N_VERSION_NOTIFICATIONS_ENABLED: "false"\n N8N_DIAGNOSTICS_ENABLED: "false"\n N8N_PUBLIC_API_DISABLED: "false"\n EXECUTIONS_PROCESS: "main"\n EXECUTIONS_MODE: "regular"\n QUEUE_BULL_REDIS_HOST: "redis-cluster"\n QUEUE_BULL_REDIS_PORT: "6379"\n DB_TYPE: "postgresdb"\n DB_POSTGRESDB_HOST: "postgresql-ha-primary"\n DB_POSTGRESDB_PORT: "5432"\n DB_POSTGRESDB_DATABASE: "n8n"\n DB_POSTGRESDB_USER: "n8n"\n```\n\n### Secrets\n\n```yaml\n# n8n-secrets.yaml\napiVersion: v1\nkind: Secret\nmetadata:\n name: n8n-secrets\n namespace: n8n-automation\ntype: Opaque\nstringData:\n N8N_BASIC_AUTH_PASSWORD: "secure-password-here"\n DB_POSTGRESDB_PASSWORD: "postgres-password"\n ENCRYPTION_KEY: "your-encryption-key-32-chars-long"\n # Sendy Configuration\n SENDY_API_KEY: "your-sendy-api-key"\n SENDY_INSTALLATION_URL: "https://sendy.yourdomain.com"\n SENDY_LIST_ID: "rust-meetup-list-id"\n # Integration Keys\n TYPEBOT_API_KEY: "your-typebot-api-key"\n FORMBRICKS_API_KEY: "your-formbricks-api-key"\n BASEROW_API_KEY: "your-baserow-api-key"\n CLAPER_WEBHOOK_SECRET: "claper-webhook-secret"\n```\n\n### PersistentVolumeClaim\n\n```yaml\n# n8n-storage.yaml\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n name: n8n-data\n namespace: n8n-automation\nspec:\n accessModes:\n - ReadWriteOnce\n resources:\n requests:\n storage: 10Gi\n storageClassName: fast-ssd\n```\n\n### Deployment\n\n```yaml\n# n8n-deployment.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: n8n\n namespace: n8n-automation\n labels:\n app: n8n\n version: v1\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: n8n\n version: v1\n template:\n metadata:\n labels:\n app: n8n\n version: v1\n spec:\n containers:\n - name: n8n\n image: n8nio/n8n:1.57.0\n ports:\n - containerPort: 5678\n name: http\n envFrom:\n - configMapRef:\n name: n8n-config\n - secretRef:\n name: n8n-secrets\n volumeMounts:\n - name: n8n-data\n mountPath: /home/node/.n8n\n resources:\n requests:\n memory: "512Mi"\n cpu: "250m"\n limits:\n memory: "2Gi"\n cpu: "1000m"\n livenessProbe:\n httpGet:\n path: /healthz\n port: 5678\n initialDelaySeconds: 60\n periodSeconds: 30\n readinessProbe:\n httpGet:\n path: /healthz\n port: 5678\n initialDelaySeconds: 30\n periodSeconds: 10\n securityContext:\n runAsNonRoot: true\n runAsUser: 1000\n allowPrivilegeEscalation: false\n readOnlyRootFilesystem: false\n volumes:\n - name: n8n-data\n persistentVolumeClaim:\n claimName: n8n-data\n securityContext:\n fsGroup: 1000\n```\n\n### Service\n\n```yaml\n# n8n-service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n name: n8n\n namespace: n8n-automation\n labels:\n app: n8n\nspec:\n selector:\n app: n8n\n ports:\n - name: http\n port: 80\n targetPort: 5678\n protocol: TCP\n type: ClusterIP\n```\n\n### Ingress (Traditional)\n\n```yaml\n# n8n-ingress.yaml\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: n8n-ingress\n namespace: n8n-automation\n annotations:\n kubernetes.io/ingress.class: "nginx"\n nginx.ingress.kubernetes.io/ssl-redirect: "true"\n nginx.ingress.kubernetes.io/force-ssl-redirect: "true"\n nginx.ingress.kubernetes.io/proxy-body-size: "50m"\n nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"\n nginx.ingress.kubernetes.io/proxy-send-timeout: "300"\n nginx.ingress.kubernetes.io/proxy-read-timeout: "300"\n cert-manager.io/cluster-issuer: "letsencrypt-prod"\nspec:\n tls:\n - hosts:\n - n8n.rust-meetup.local\n secretName: n8n-tls\n rules:\n - host: n8n.rust-meetup.local\n http:\n paths:\n - path: /\n pathType: Prefix\n backend:\n service:\n name: n8n\n port:\n number: 80\n```\n\n### Istio VirtualService\n\n```yaml\n# n8n-virtualservice.yaml\napiVersion: networking.istio.io/v1beta1\nkind: VirtualService\nmetadata:\n name: n8n-vs\n namespace: n8n-automation\nspec:\n hosts:\n - n8n.rust-meetup.local\n gateways:\n - rust-meetup-gateway\n http:\n - match:\n - uri:\n prefix: /\n route:\n - destination:\n host: n8n.n8n-automation.svc.cluster.local\n port:\n number: 80\n timeout: 300s\n headers:\n request:\n add:\n X-Forwarded-Proto: "https"\n X-Forwarded-For: "%DOWNSTREAM_REMOTE_ADDRESS%"\n\n---\napiVersion: networking.istio.io/v1beta1\nkind: DestinationRule\nmetadata:\n name: n8n-dr\n namespace: n8n-automation\nspec:\n host: n8n.n8n-automation.svc.cluster.local\n trafficPolicy:\n connectionPool:\n tcp:\n maxConnections: 50\n http:\n http1MaxPendingRequests: 100\n maxRequestsPerConnection: 10\n outlierDetection:\n consecutiveErrors: 3\n interval: 30s\n baseEjectionTime: 30s\n```\n\n## n8n Workflow Templates\n\n### 1. Event Registration Workflow\n\n```json\n{\n "name": "Rust Meetup Registration Flow",\n "nodes": [\n {\n "parameters": {\n "httpMethod": "POST",\n "path": "registration",\n "responseMode": "responseNode"\n },\n "id": "webhook-registration",\n "name": "Registration Webhook",\n "type": "n8n-nodes-base.webhook",\n "position": [240, 300]\n },\n {\n "parameters": {\n "url": "={{ $env.SENDY_INSTALLATION_URL }}/api/subscribe.php",\n "sendHeaders": true,\n "headerParameters": {\n "parameters": [\n {\n "name": "Content-Type",\n "value": "application/x-www-form-urlencoded"\n }\n ]\n },\n "sendBody": true,\n "bodyParameters": {\n "parameters": [\n {\n "name": "api_key",\n "value": "={{ $env.SENDY_API_KEY }}"\n },\n {\n "name": "email",\n "value": "={{ $json.email }}"\n },\n {\n "name": "list",\n "value": "={{ $env.SENDY_LIST_ID }}"\n },\n {\n "name": "name",\n "value": "={{ $json.name }}"\n },\n {\n "name": "custom_fields",\n "value": "{{ JSON.stringify({ registration_source: 'rust-meetup-2025', interest_level: $json.interest_level, experience: $json.experience, registration_date: new Date().toISOString() }) }}"\n }\n ]\n }\n },\n "id": "sendy-subscribe",\n "name": "Add to Sendy List",\n "type": "n8n-nodes-base.httpRequest",\n "position": [460, 300]\n },\n {\n "parameters": {\n "url": "={{ $env.BASEROW_API_URL }}/api/database/rows/table/{{ $env.BASEROW_ATTENDEES_TABLE_ID }}/",\n "sendHeaders": true,\n "headerParameters": {\n "parameters": [\n {\n "name": "Authorization",\n "value": "Token {{ $env.BASEROW_API_KEY }}"\n }\n ]\n },\n "sendBody": true,\n "bodyParameters": {\n "parameters": [\n {\n "name": "name",\n "value": "={{ $json.name }}"\n },\n {\n "name": "email",\n "value": "={{ $json.email }}"\n },\n {\n "name": "registration_source",\n "value": "typebot"\n },\n {\n "name": "registration_date",\n "value": "={{ new Date().toISOString() }}"\n },\n {\n "name": "status",\n "value": "registered"\n }\n ]\n }\n },\n "id": "baserow-create",\n "name": "Save to Baserow",\n "type": "n8n-nodes-base.httpRequest",\n "position": [680, 300]\n }\n ],\n "connections": {\n "Registration Webhook": {\n "main": [\n [\n {\n "node": "Add to Sendy List",\n "type": "main",\n "index": 0\n }\n ]\n ]\n },\n "Add to Sendy List": {\n "main": [\n [\n {\n "node": "Save to Baserow",\n "type": "main",\n "index": 0\n }\n ]\n ]\n }\n }\n}\n```\n\n### 2. Pre-Event Email Sequence\n\n```json\n{\n "name": "Pre-Event Email Automation",\n "nodes": [\n {\n "parameters": {\n "rule": {\n "interval": [\n {\n "field": "cronExpression",\n "value": "0 9 * * *"\n }\n ]\n }\n },\n "id": "daily-trigger",\n "name": "Daily Check Trigger",\n "type": "n8n-nodes-base.cron",\n "position": [240, 300]\n },\n {\n "parameters": {\n "url": "={{ $env.SENDY_INSTALLATION_URL }}/api/create-send.php",\n "sendBody": true,\n "bodyParameters": {\n "parameters": [\n {\n "name": "api_key",\n "value": "={{ $env.SENDY_API_KEY }}"\n },\n {\n "name": "from_name",\n "value": "Rust Meetup Team"\n },\n {\n "name": "from_email",\n "value": "hello@rust-meetup.com"\n },\n {\n "name": "reply_to",\n "value": "hello@rust-meetup.com"\n },\n {\n "name": "subject",\n "value": "¡Solo quedan {{ $json.days_remaining }} días para el Rust Meetup!"\n },\n {\n "name": "html_text",\n "value": "{{ $json.email_template }}"\n },\n {\n "name": "list_ids",\n "value": "={{ $env.SENDY_LIST_ID }}"\n },\n {\n "name": "send_campaign",\n "value": "1"\n }\n ]\n }\n },\n "id": "send-reminder",\n "name": "Send Reminder Email",\n "type": "n8n-nodes-base.httpRequest",\n "position": [680, 300]\n }\n ]\n}\n```\n\n### 3. Live Event Integration\n\n```json\n{\n "name": "Live Event Poll Integration",\n "nodes": [\n {\n "parameters": {\n "httpMethod": "POST",\n "path": "poll-response",\n "responseMode": "responseNode"\n },\n "id": "poll-webhook",\n "name": "Poll Response Webhook",\n "type": "n8n-nodes-base.webhook",\n "position": [240, 300]\n },\n {\n "parameters": {\n "url": "={{ $env.BASEROW_API_URL }}/api/database/rows/table/{{ $env.BASEROW_LIVE_ENGAGEMENT_TABLE_ID }}/",\n "sendHeaders": true,\n "headerParameters": {\n "parameters": [\n {\n "name": "Authorization",\n "value": "Token {{ $env.BASEROW_API_KEY }}"\n }\n ]\n },\n "sendBody": true,\n "bodyParameters": {\n "parameters": [\n {\n "name": "attendee_email",\n "value": "={{ $json.email }}"\n },\n {\n "name": "poll_question",\n "value": "={{ $json.question }}"\n },\n {\n "name": "response",\n "value": "={{ $json.answer }}"\n },\n {\n "name": "timestamp",\n "value": "={{ new Date().toISOString() }}"\n },\n {\n "name": "engagement_score",\n "value": "={{ $json.engagement_points || 10 }}"\n }\n ]\n }\n },\n "id": "save-engagement",\n "name": "Save Engagement Data",\n "type": "n8n-nodes-base.httpRequest",\n "position": [460, 300]\n },\n {\n "parameters": {\n "conditions": {\n "string": [\n {\n "value1": "={{ $json.engagement_score }}",\n "operation": "largerEqual",\n "value2": "50"\n }\n ]\n }\n },\n "id": "check-engagement",\n "name": "High Engagement?",\n "type": "n8n-nodes-base.if",\n "position": [680, 300]\n },\n {\n "parameters": {\n "url": "={{ $env.SENDY_INSTALLATION_URL }}/api/subscribe.php",\n "sendBody": true,\n "bodyParameters": {\n "parameters": [\n {\n "name": "api_key",\n "value": "={{ $env.SENDY_API_KEY }}"\n },\n {\n "name": "email",\n "value": "={{ $json.email }}"\n },\n {\n "name": "list",\n "value": "={{ $env.SENDY_HIGH_ENGAGEMENT_LIST_ID }}"\n },\n {\n "name": "custom_fields",\n "value": "{{ JSON.stringify({ segment: 'high_engagement', last_interaction: new Date().toISOString() }) }}"\n }\n ]\n }\n },\n "id": "segment-high-engagement",\n "name": "Add to High Engagement Segment",\n "type": "n8n-nodes-base.httpRequest",\n "position": [900, 200]\n }\n ]\n}\n```\n\n## QR Code Integration Strategy\n\n### 1. Dynamic QR Code Generation\n\n```javascript\n// QR Code Generation Service\nconst QRCode = require('qrcode');\n\nasync function generateEventQR(attendeeEmail, source = 'general') {\n const registrationUrl = `https://typebot.rust-meetup.local/rust-meetup-registration?email=${encodeURIComponent(attendeeEmail)}&source=${source}&utm_campaign=rust-meetup-2025`;\n\n const qrOptions = {\n errorCorrectionLevel: 'M',\n type: 'image/png',\n quality: 0.92,\n margin: 1,\n color: {\n dark: '#000000',\n light: '#FFFFFF',\n },\n width: 256\n };\n\n return await QRCode.toDataURL(registrationUrl, qrOptions);\n}\n```\n\n### 2. Landing Page Integration\n\n```html\n\n\n\n
\n \n \nInfrastructure Automation with Rust, Nushell & KCL
\n| \n \n | \n