n8n Automations

60 designed automations (35 currently deployed) orchestrating content generation, lead nurturing, booking management, trip lifecycle, and operational monitoring — all running on a self-hosted n8n 2.6.3 instance at automations.traveltamers.com on the VPS.

Overview

Travel Tamers uses n8n (v2.6.3), a self-hosted workflow automation platform, to connect its services and automate repetitive business processes. The n8n instance runs as a Docker container on the Hostinger VPS, accessible at automations.traveltamers.com behind Traefik with automatic SSL.

There are 60 designed automations (numbered A-1 through A-60) covering the full business lifecycle: from generating daily social media content with AI, to nurturing leads through email sequences, to sending pre-trip destination guides and post-trip feedback requests. Of these, 35 are currently deployed in the n8n instance. The original 60 automation IDs are the canonical reference, but some workflows were consolidated or deduplicated during deployment — the actual workflow count on the VPS is 35. The remaining automations are designed and documented, ready to be built as the business scales.

60 Designed 35 Deployed 29 Migrated to Env Vars 9 External Services

Numbering convention: All automations are referenced as A-1 through A-60 throughout the codebase, architecture docs, and n8n workflow names. The master specification lives in architecture/automation-workflows.md.

Environment variable migration (R9): 29 of the 35 deployed workflows were migrated from hardcoded values (API keys, Slack channel IDs, webhook secrets) to n8n environment variables using the $env.VARIABLE_NAME syntax. This means secrets are no longer baked into workflow JSON files — they're injected via Docker Compose at container startup.

Connected Services

The n8n instance connects to nine external services plus two internal PostgreSQL databases. Each integration is configured via environment variables in Docker Compose or directly as n8n credentials.

Slack TT-API Nexus CRM Claude AI Perplexity Unsplash FLUX (Replicate) Buffer Gmail
ServicePurposeAuth Method
SlackNotifications, content review, approvals, monitoring alertsBot Token (OAuth)
TT-API (Hono)CRUD on contacts, deals, bookings, analyticsX-Webhook-Secret header ($env.WEBHOOK_SECRET)
Nexus CRMMarketing sequences, social posts, CRM dataDirect PostgreSQL + X-Webhook-Secret for HTTP callbacks
Claude AIContent filtering, writing, analysis, recommendationsAPI key ($env.ANTHROPIC_API_KEY)
PerplexityReal-time research on trending topicsAPI key ($env.PERPLEXITY_API_KEY)
UnsplashStock photography for social postsAccess key ($env.UNSPLASH_ACCESS_KEY)
FLUX (Replicate)AI image generation ($0.01–0.05/image)API token ($env.REPLICATE_API_TOKEN)
BufferSocial scheduling via GraphQL API at api.buffer.comAccess token ($env.BUFFER_ACCESS_TOKEN)
GmailEmail sequences and notificationsService account

Architecture

The n8n container sits at the center of the Travel Tamers automation ecosystem, connecting inbound channels (LinkedIn, website forms, email replies) to outbound actions (Slack messages, social posts, CRM updates, email sends). It communicates with the TT-API via authenticated HTTP requests and connects directly to both PostgreSQL databases for queries and writes.

INBOUND CHANNELS LinkedIn Website (Miles, forms) Email (replies) | | | +-------+-------+--------+-----------+ | | v v +--------+--------+ +--+-------------+ | n8n ORCHESTRATOR |<>| Slack API | | - Webhooks | | (Events + | | - Crons | | Commands) | | - Event handlers| +--+------------+ +---+--------+---+ | | | | Slack Connect channels +------+---+ +---+------+ | | Resend | | Claude | | | (email) | | (AI) | | +----------+ +---+------+ | +----------+ +---+------+ | | Ollama | | Replicate| | | (local) | | (FLUX) | | +----------+ +----------+ | +----------+ +----------+ | | Perplexity| | Buffer | | +----------+ | (GraphQL)| | +----------+ | v +-----------------------+ | PostgreSQL 16 (VPS) | | tt_fresh_db (52 tbl) | | nexus_crm (54 tbl) | | groups_db (13 tbl) | +-----------------------+ OUTBOUND CHANNELS Slack Connect Email (Resend) Social (Buffer GraphQL)

Webhook Endpoints

EndpointMethodPurposeWorkflows
/webhook/form-submitPOSTProposal/contact form submissionsA-17, A-18, A-20
/webhook/buddy-intentPOSTMiles high-intent signalsA-6
/webhook/page-viewPOSTProposal page visit eventsA-7, A-14
/webhook/slack-eventsPOSTAll Slack events (reactions, messages)A-22, A-38, A-50
/webhook/booking-updatePOSTBooking status changesA-24, A-25, A-26, A-44
/webhook/group-updatePOSTGroup membership changesA-56, A-58, A-59
/webhook/survey-responsePOSTNPS/CSAT survey submissionsA-41, A-51
/webhook/buffer-publishPOSTBuffer callback after publishingA-2

Cron Schedules

ScheduleWorkflowPurpose
0 7 * * * (7am ET)A-1Social media content pipeline
0 6 * * * (6am ET)A-33Departure date trigger scanner (master)
0 8 * * 1 (Mon 8am)A-4Target company news monitoring
0 9 1 * * (1st, 9am)A-48Quarterly newsletter
0 6 * * * (6am ET)A-45/A-49Anniversary + birthday scanner
0 10 * * 1,4 (Mon/Thu)A-57Payment deadline reminder check

Webhook authentication: All workflows that call back to TT-API or Nexus send an X-Webhook-Secret header using $env.WEBHOOK_SECRET. Nexus validates this at /api/n8n/webhook/:action with a timing-safe comparison. See the Webhook Secrets section for the full secret topology.

Master trigger pattern: A-33 (departure date cron) is the master trigger for 12 downstream workflows. It scans all bookings daily and fires the appropriate workflow based on how many days until departure or after return: A-28 at -180d, A-29 at -90d, A-30 at -14d, A-31 at -3d, A-32 at -7d, A-35 on departure day, A-36 at +1d, A-39 at return +1d, A-40 at return +3d, A-41 at return +7d, A-45 at return +365d.

Buffer GraphQL API

All social media scheduling goes through the Buffer GraphQL API at api.buffer.com. The old v1 REST API at api.bufferapp.com is dead — it didn't support Instagram and has been fully retired. Every workflow that publishes to social media was migrated to the GraphQL endpoint during R8-R10.

API Details

PropertyValue
Endpointhttps://api.buffer.com/graphql
AuthBearer $env.BUFFER_ACCESS_TOKEN
MutationCreatePost
PlatformsLinkedIn, Facebook, Instagram
Required HeaderUser-Agent: TravelTamers-n8n/1.0

Cloudflare protection: Every Buffer HTTP node must include a User-Agent: TravelTamers-n8n/1.0 header. Without it, Cloudflare blocks the request with a 403. This is a hard requirement — n8n's default user agent gets rejected.

Channel Profile IDs

Three Buffer profile IDs are set as n8n environment variables. These identify which Buffer channels receive published posts.

VariablePlatform
BUFFER_PROFILE_LINKEDINLinkedIn company page
BUFFER_PROFILE_FACEBOOKFacebook business page
BUFFER_PROFILE_INSTAGRAMInstagram business profile

GraphQL Mutation Example

mutation CreatePost($input: CreatePostInput!) {
  createPost(input: $input) {
    post {
      id
      text
      scheduledAt
      channels { id name }
    }
  }
}

# Variables:
{
  "input": {
    "text": "Post content here...",
    "channelIds": ["$env.BUFFER_PROFILE_LINKEDIN"],
    "scheduledAt": "2026-03-16T14:00:00Z",
    "media": [{ "url": "https://..." }]
  }
}

Workflow Categories

All 60 automations are organized into six functional categories across three priority tiers. Tier 1 are launch blockers (day 1). Tier 2 activate within 30 days. Tier 3 build when revenue starts. Click each category to see individual workflows. Of the 60 designed, 35 are currently deployed on the VPS (some consolidation occurred during implementation).

AI-powered content generation, Slack-based approval, and Buffer GraphQL publishing across LinkedIn, Facebook, and Instagram.

IDWorkflowTriggerTierStatus
A-1Social Media Content PipelineCron 7am / Manual / APIT1Active
A-2Emoji Approve → Buffer PublishSlack reaction in #content-reviewT1Active
A-3Website Visit Tracking (UTM Analytics)Page view beaconT2Active
A-4Target Company News MonitoringWeekly cron (Mon 8am)T3Planned

Onboarding new contacts, Slack channel creation, pre-trip preparation, during-trip check-ins, post-trip feedback, and anniversary touchpoints. Many are driven by the A-33 master departure date cron.

IDWorkflowTriggerTierStatus
A-5Miles High-Intent DetectionReal-time (built-in)T1Active
A-6Auto-Create CRM Contact from MilesA-5 webhookT1Active
A-17CRM Update on Form SubmissionForm webhookT1Active
A-18Auto-Create Slack Channel + WelcomeForm submitT1Active
A-19Preference Questionnaire (24h)A-18 + delayT2Active
A-20Auto-Create CRM Records from FormA-17 chainT1Active
A-22Slack Connect Acceptance TriggerSlack eventsT2Planned
A-28Passport Expiry CheckA-33 at -180dT1Active
A-29Document Checklist (90d pre-dep)A-33 at -90dT2Active
A-30Packing List (14d pre-dep)A-33 at -14dT3Planned
A-3172-Hour Check-InA-33 at -3dT2Active
A-32Pin Final Itinerary (7d)A-33 at -7dT3Planned
A-35Departure Day Bon VoyageA-33 at day 0T2Active
A-36Day 1 Check-In at DestinationA-33 at +1dT3Planned
A-39Welcome Home MessageA-33 at return +1dT2Active
A-40Feedback Survey (return +3d)A-33 chainT2Active
A-45Trip Anniversary MessagesA-33 at return +365dT3Planned
A-49Birthday MessagesDaily cron (6am)T3Planned

Lead scoring, deal stage transitions, proposal generation, booking creation, commission calculation, and deal completion tracking.

IDWorkflowTriggerTierStatus
A-7Proposal Page Visit NotificationPage view webhookT1Active
A-8LinkedIn DM Logging to CRMManual / APIT3Planned
A-9Auto-Score LeadsDaily cronT2Active
A-10Auto-Enrich Company (Perplexity)New company webhookT2Active
A-11Auto-Disqualify Below ThresholdA-9 chainT3Planned
A-12Auto-Create Deal on QualificationA-9 thresholdT2Active
A-13Auto-Generate Proposal PageDeal stage changeT2Planned
A-14Proposal Engagement TrackingA-7 chainT2Active
A-15Follow-Up Email (3d post-proposal)A-13 + delayT2Planned
A-24Auto-Create Booking on ConfirmVendor confirm webhookT2Active
A-25Auto-Calculate CommissionA-24 chainT2Active
A-26Booking Confirmation (Slack + Email)A-24 chainT2Active
A-44Deal Completed + Commission TrackTrip completedT2Active

Health monitoring, error alerting, backup verification, and system reporting.

IDWorkflowTriggerStatus
A-33Departure Date Trigger Scanner (Master)Daily cron (6am)Active
A-34CDC/State Dept AlertsA-33 chainPlanned
A-37Safety Alerts for Active DestinationsA-34 chainPlanned
A-38Log During-Trip InteractionsSlack eventsPlanned
A-42Auto-Update Traveler StatsTrip completedActive

Email sequences, newsletters, social scheduling, engagement tracking, referral programs, and re-engagement campaigns.

IDWorkflowTriggerStatus
A-41Review Request (NPS ≥ 9)A-40 chainActive
A-43Social Content from Client PhotosPermission workflowPlanned
A-47Auto-Invite to Group TripsCRM matchPlanned
A-48Quarterly NewsletterMonthly cron (1st, 9am)Planned
A-50Engagement Score TrackingDaily cronActive
A-51Referral Request (high NPS)A-40 chainPlanned
A-52Referral Chain TrackingReferral webhookPlanned
A-53Referral Reward NotificationA-52 chainPlanned
A-54Case Study Draft (AI)Manual triggerPlanned
A-60Group Trip Summary GeneratorTrip finalizedPlanned

Opportunity scoring, group trip management, trip recommendations, and intelligence dashboards.

IDWorkflowTriggerStatus
A-16Group Advantage CalculatorBuilt-inActive
A-21Auto-Schedule Intro CallDeal stage changePlanned
A-23Auto-Suggest Trips (AI)CRM + trend dataPlanned
A-27Pin Itinerary in SlackBooking confirmedPlanned
A-46Intelligence: Top OpportunitiesWeekly cronActive
A-55Auto-Create Group on Groups AppCRM dealPlanned
A-56Group Threshold NotificationGroup update webhookPlanned
A-57Payment Deadline RemindersMon/Thu cronActive
A-58Group Savings on Headcount ChangeGroup updatePlanned
A-59Milestone Messages in Group SlackGroup updatePlanned
n8n workflow editor showing Social Media Content Pipeline with 14 connected nodes in dark mode
Social Media Content Pipeline — 14-node workflow in n8n editor

Social Media Pipeline Deep Dive

The flagship automation. Every morning at 7am ET (or via manual/API trigger), A-1 generates a full day of social media content: five curated travel stories, each with custom images, written for three platforms — LinkedIn, Facebook, and Instagram. It combines real-time news research, AI filtering, professional photography, and AI-generated imagery into a cohesive content package delivered to Slack for human review.

Full walkthrough available: For a comprehensive guide covering every node, prompt template, and data transformation in detail, see the dedicated Social Media Content Pipeline Guide.

Node Chain (14 nodes)

Cron 7am / Manual / API Fetch Headlines Parse Google Trends Claude Filter Claude Select Top 5 Parse Selection Perplexity Research Unsplash Photos FLUX Images Claude Write Posts Assemble Output Slack Summary Slack Full Content

Trigger

Three entry points: daily cron at 7am EST, manual button in n8n, or API trigger via webhook URL.

Cron / Manual / Webhook

Fetch → Parse → Google Trends

Pulls last 24 hours of messages from #feed-travel-news Slack channel, parses headlines, then cross-references with Google Trends to find what resonates.

Slack API + HTTP Request + Code Node

Claude Filter → Claude Select Top 5

Two-stage AI curation. First pass sends headlines to Ollama (num_ctx: 16384) for classification, filtering to ~20–40 candidates. Second pass uses Claude Sonnet to select the top 5 stories for Travel Tamers' tech-executive audience.

Ollama + Anthropic API (Claude Sonnet)

Perplexity Research (x5 parallel)

Each selected story gets real-time web research via Perplexity to add context, statistics, and angles for credibility.

Perplexity API

Unsplash Photos + FLUX Images (x5 each)

Each story gets two image options: professional stock photo from Unsplash and a custom AI image from FLUX via Replicate ($0.01–0.05 per image, 12s sleep between calls for rate limiting).

Unsplash API + Replicate API

Claude Write Posts (x5)

Claude writes platform-specific versions: LinkedIn gets thought-leadership, Facebook gets community engagement, Instagram gets visual-first captions with hashtags. Character limits enforced (LinkedIn 3000, IG 2200, FB 63206).

Anthropic API (Claude Sonnet)

Assemble → Slack Summary → Slack Full Content

Posts to #content-review in two messages: summary card with all 5 headlines, then individual posts with full content and images. Reviewers approve with a checkmark emoji to trigger A-2 (Emoji Approve → Buffer Publish).

Slack Bot API
n8n execution showing Parse Headlines node error with Cannot assign to read only property message
Parse Headlines error — the regex corruption bug that broke the pipeline

Output per Run

MetricCount
Stories generated5
Platform variations3 (LinkedIn, Facebook, Instagram)
Total post drafts15
Images per story2 (Unsplash + FLUX)
Approx. Claude API cost~$0.15 per full run

Error Handling

  • Ollama timeout (>5 min): Retry once, then skip classification and send raw headlines to Claude.
  • Perplexity failure: Skip that story, proceed with remaining.
  • FLUX rate limit: Use Unsplash image only, skip AI image generation.
  • Full pipeline failure: Post error to #automation-errors for manual review.

Emoji Approve → Buffer Publish

The companion workflow to the content pipeline. When a reviewer in Slack reacts to a generated post with a checkmark emoji in #content-review, this workflow picks it up, parses the post content, queues it in Buffer via the GraphQL API for LinkedIn, Facebook, and Instagram, saves it to the Nexus social_posts table, and confirms with a rocket emoji reaction.

"Active version not found" fix: After deploying workflows via the n8n CLI, the emoji approve workflow (and others) threw "Active version not found" errors on execution. This was caused by workflows being imported but not published. The fix was running n8n publish:workflow --id=ID on all 35 deployed workflows. In n8n 2.x, update:workflow --active=true is deprecated — you must use publish:workflow instead. See the n8n 2.x Changes section.

Node Chain (10 nodes)

Webhook (reaction_added) Route Respond OK + Should Process Check Approve/Rewrite Fetch Message Parse Post Content Buffer GraphQL Post Save to Nexus React with Rocket

Slack Webhook

Triggered by reaction_added event from Slack. Listens for the heavy_check_mark emoji on messages in #content-review.

Slack Events API

Route → Respond OK → Should Process

Immediately responds with 200 OK to Slack (required within 3 seconds), then filters out duplicate reactions, bot reactions, and non-content messages.

Code Node + IF Node

Check Approve vs. Rewrite

Distinguishes between approval (checkmark) and rewrite request (pencil emoji). Approvals proceed to Buffer; rewrites route back to Claude for content revision.

Switch Node

Fetch Message → Parse Post Content

Retrieves the full Slack message via conversations.history API, then parses structured post content from message blocks — extracting platform-specific text, images, and the story title from header blocks.

Slack API + Code Node

Buffer GraphQL → Save to Nexus → Rocket React

Uses the Buffer GraphQL API (api.buffer.com) with the CreatePost mutation to schedule posts for LinkedIn, Facebook, and Instagram with attached images. Requires a User-Agent: TravelTamers-n8n/1.0 header to avoid Cloudflare 403 blocks. Writes the approved post to Nexus social_posts table for tracking. Adds a rocket emoji to confirm the post is queued.

Buffer GraphQL API + PostgreSQL + Slack API

Slack Approval Emojis

EmojiCodeActionStatus
✔️:heavy_check_mark:Approve → queue to BufferWired
:x:Reject (discard)Reserved
:alphabet-white-question:Needs clarificationReserved
n8n pipeline showing Post Full Content node error with JSON parameter needs to be valid JSON
Post Full Content error — JSON validation failure in execution logs

The Parse Post Content Regex Fix

The "Parse Post Content" node in the Emoji Approve workflow is the most critical and most-debugged piece of code in the automation stack. It must reliably extract platform-specific post text and image URLs from Slack message blocks — a format that changed several times during development.

The Problem

Slack messages use a block-based format where each platform's post appears as a section block with a bold header (e.g., *:linkedin: LinkedIn*). The original parser used simple string matching that broke when Slack's block format changed or when emoji shortcodes were used inconsistently. Posts would fail to parse, and Buffer would receive empty content.

The Solution: fix-parse-regex.py

Rather than editing the node directly in the n8n UI (which is error-prone for complex JavaScript), a Python script was created at automations/fix-parse-regex.py. This script reads a workflow JSON from stdin, replaces the "Parse Post Content" node's jsCode with a battle-tested version, strips the JSON to only API-allowed fields, and outputs the result for a PUT request to the n8n API.

# Usage: pipe workflow JSON through the fixer, then PUT to n8n API
curl -s "https://automations.traveltamers.com/api/v1/workflows/WORKFLOW_ID" \
  -H "X-N8N-API-KEY: $N8N_API_KEY" \
  | python3 automations/fix-parse-regex.py \
  | curl -X PUT "https://automations.traveltamers.com/api/v1/workflows/WORKFLOW_ID" \
    -H "X-N8N-API-KEY: $N8N_API_KEY" \
    -H "Content-Type: application/json" \
    -d @-

What the Fixed Parser Does

The replacement Code node iterates through every block in the Slack message:

  • Image blocks (type === "image"): Collects all image_url values into an array.
  • Section blocks: Uses flexible matching with multiple patterns per platform:
    • LinkedIn: *:linkedin: or *LinkedIn
    • Instagram: *:camera: or *Instagram
    • Facebook: *:facebook: or *Facebook
  • Header blocks: Extracts the story title from header type blocks.
  • Fallback: If no platform-specific blocks found, uses the raw message.text truncated to platform limits.

Key pattern: The regex new RegExp("^[*][^*]+[*]\\n?") strips the bold platform header from each section's text, leaving just the post body. This is the line that broke most often and prompted the Python fix script approach.

Iteration History

The automations/ directory contains the debug trail: emoji-raw.json, emoji-raw2.json, emoji-raw3.json, emoji-raw4.json, and their corresponding fixed versions. Each iteration addressed a different edge case in Slack's block format. The final version (emoji-with-nexus.json) adds the Nexus CRM save step.

Credentials

Three credentials are configured directly in the n8n UI. These are stored encrypted in n8n's internal SQLite database and referenced by workflows via credential ID. Actual credential values aren't stored in the codebase.

CredentialTypeUsed By
Slack Bot Token OAuth2 / Bot Token All Slack notification, approval, and monitoring workflows
TT Fresh DB PostgreSQL (direct) Lead scoring, booking queries, analytics, contact lookups
Nexus CRM DB PostgreSQL (direct) Social posts, marketing sequences, CRM data writes

Data volume warning: Credentials are stored in n8n's encrypted database, not in environment variables. If you reset the n8n container without preserving its data volume, all three credentials will need to be re-created manually in the n8n UI.

OAuth callbacks (n8n 2.6.3): OAuth callbacks now enforce user authentication. You must be logged into the n8n UI before initiating an OAuth flow, or the callback will fail with an auth error. This is a change from n8n 1.x where OAuth callbacks were unauthenticated.

Additional API keys are passed via environment variables in Docker Compose (see Environment Variables below). These are accessed in workflows via the $env variable in Code nodes or as HTTP header values.

Environment Variables

The n8n container receives environment variables through docker-compose.production.yml, including API keys for all external services, 3 Buffer profile IDs, and 12 Slack channel IDs for routing notifications. After the R9 migration, 29 workflows reference these via $env.VARIABLE_NAME instead of hardcoding values in workflow JSON.

API Keys & Tokens

VariableServicePurpose
ANTHROPIC_API_KEYClaude AIContent generation, filtering, analysis
BUFFER_ACCESS_TOKENBufferSocial media scheduling (GraphQL API at api.buffer.com)
UNSPLASH_ACCESS_KEYUnsplashStock photography search
REPLICATE_API_TOKENReplicate (FLUX)AI image generation
PERPLEXITY_API_KEYPerplexityReal-time web research
TT_API_KEYTT-API (Hono)Authenticated webhook calls to the API
SLACK_BOT_TOKENSlackBot API access for messages and reactions
SLACK_SIGNING_SECRETSlackWebhook signature verification
WEBHOOK_SECRETTT-API / NexusShared secret for n8n → TT-API callback auth (sent as X-Webhook-Secret header)
N8N_WEBHOOK_SECRETNexusNexus-specific n8n callback auth (separate from WEBHOOK_SECRET)

Buffer Profile IDs

VariablePlatform
BUFFER_PROFILE_LINKEDINLinkedIn company page
BUFFER_PROFILE_FACEBOOKFacebook business page
BUFFER_PROFILE_INSTAGRAMInstagram business profile

Slack Channel IDs (12 vars)

Twelve Slack channels are configured by ID rather than name, ensuring workflows don't break if channels are renamed. Both Shane and Debby are invited to all channels.

VariableChannelPurpose
SLACK_CHANNEL_CONTENT_REVIEW#content-reviewAI-generated social posts for approval
SLACK_CHANNEL_MONITORING#monitoringHealth check alerts and system status
SLACK_CHANNEL_SALES_ALERTS#sales-alertsNew leads, deals, pipeline changes
SLACK_CHANNEL_AUTOMATION_ERRORS#automation-errorsn8n workflow failures and error details
SLACK_CHANNEL_BOOKINGS#bookingsBooking confirmations and updates
SLACK_CHANNEL_TRIP_UPDATES#trip-updatesPre-trip, during-trip, post-trip messages
SLACK_CHANNEL_GROUPS#groupsGroup trip lifecycle events
SLACK_CHANNEL_REFERRALS#referralsReferral tracking and rewards
SLACK_CHANNEL_PAYMENTS#paymentsPayment reminders and confirmations
SLACK_CHANNEL_LEADS#leadsLead scoring and qualification updates
SLACK_CHANNEL_GENERAL#generalGeneral notifications and digests
SLACK_CHANNEL_RETENTION#retentionRe-engagement and loyalty notifications

n8n-specific vars: The container also receives standard n8n configuration: N8N_BASIC_AUTH_ACTIVE, N8N_BASIC_AUTH_USER, N8N_BASIC_AUTH_PASSWORD, and WEBHOOK_URL (set to https://automations.traveltamers.com).

Webhook Secrets

Two separate webhook secrets handle authentication between n8n and the backend services. They're distinct values with distinct purposes — don't mix them up.

SecretSet InUsed ForValidated By
WEBHOOK_SECRET n8n env + TT-API .env n8n → TT-API webhook calls (sent as X-Webhook-Secret header) TT-API middleware at /webhooks/*
N8N_WEBHOOK_SECRET Nexus .env n8n → Nexus callback auth (sent as X-Webhook-Secret header) Nexus at /api/n8n/webhook/:action with timing-safe comparison

How it works in workflows: n8n workflows reference $env.WEBHOOK_SECRET in HTTP Request nodes that call back to TT-API. For Nexus callbacks, they use the same $env.WEBHOOK_SECRET value, but Nexus validates it against its own N8N_WEBHOOK_SECRET environment variable. Both sides must have matching values for the timing-safe comparison to pass.

Secret Flow Diagram

n8n Container WEBHOOK_SECRET = "abc123" (from docker-compose env) | | X-Webhook-Secret: abc123 | +----+----+ +--------+ | TT-API | | Nexus | | WEBHOOK_ | | N8N_WEB | | SECRET | | HOOK_SE | | = abc123| | CRET | +---------+ | = abc..| +--------+ Both validate with crypto.timingSafeEqual()

Email Templates

Transactional email templates are designed for use with React Email and sent via Resend. The templates directory is at automations/emails/ and uses the React Email component library to build responsive HTML emails that match the Dark Sanctuary brand.

Template registry: The full list of planned email templates is documented in section 5 of architecture/automation-workflows.md. Templates cover welcome sequences, booking confirmations, pre-trip guides, feedback surveys, review requests, referral invitations, and more.

Template Categories

CategoryTemplatesUsed By
OnboardingWelcome email, preference questionnaireA-18, A-19
ProposalsProposal link, follow-up (3-day)A-13, A-15
BookingConfirmation, itinerary, payment receiptA-24, A-26
Pre-TripDocument checklist, packing list, bon voyageA-29, A-30, A-35
Post-TripWelcome home, feedback survey, review requestA-39, A-40, A-41
RetentionAnniversary, birthday, referral inviteA-45, A-49, A-53
NewsletterQuarterly newsletterA-48

Tech Stack

  • React Email for building templates as React components with responsive layouts.
  • Resend as the email delivery service (replaces raw SMTP).
  • Templates use the Dark Sanctuary color palette: navy backgrounds, off-white text, gold accents.
  • The automations/package.json manages React Email dependencies.

Development

The n8n instance is accessible at automations.traveltamers.com (basic-auth protected). Here are the key concepts for working with workflows effectively.

The n8n Editor

n8n provides a visual node-based workflow builder. Each node represents an operation — an HTTP request, a code block, a database query, or a service integration. Nodes pass data along edges. You can click any node to inspect configuration, test in isolation, or view the data from the last execution.

Critical gotcha: When editing a workflow in the browser, the editor runs the version loaded in the editor, NOT the saved/active version. If you deploy a workflow update via the API, you must refresh the browser to pick up changes. Otherwise "Execute" will run the stale version.

n8n expression engine: n8n's expression engine does NOT support optional chaining (?.). If you write {{$json.contact?.email}} it will silently evaluate to an empty string. Use $json.contact.email with a preceding filter node, or flatten the data in a Code node first.

Workflow Versioning

Workflow JSONs are stored in the automations/ directory of the monorepo. When you make changes in the n8n UI, export the updated workflow JSON and commit it to the repo. This ensures the codebase remains the source of truth and workflows can be redeployed.

JSON Export/Import

# Export a workflow from n8n API
curl -s "https://automations.traveltamers.com/api/v1/workflows/WORKFLOW_ID" \
  -H "X-N8N-API-KEY: $N8N_API_KEY" > automations/my-workflow.json

# Import a workflow via CLI (inside the n8n container)
n8n import:workflow --input=/path/to/workflow.json

# Or via API
curl -X POST "https://automations.traveltamers.com/api/v1/workflows" \
  -H "X-N8N-API-KEY: $N8N_API_KEY" \
  -H "Content-Type: application/json" \
  -d @automations/my-workflow.json

# IMPORTANT: After importing, you must PUBLISH the workflow (n8n 2.x)
n8n publish:workflow --id=WORKFLOW_ID

The fix-parse-regex.py Approach

For complex Code nodes where precision matters (like the Parse Post Content node), editing in the n8n UI is error-prone. The pattern established by fix-parse-regex.py is the recommended approach: write the JavaScript in a file, then use a script to inject it into the workflow JSON and deploy via the n8n API. This gives you proper version control, syntax highlighting, and the ability to test the code outside of n8n.

Best Practice

Test Individual Nodes First

Click any node and use "Execute this node" to test it in isolation with sample data. This avoids burning API credits on full pipeline runs. A full content pipeline run costs ~$0.15 in Claude credits — use node-level testing when iterating on prompts.

Cron Workflow Safety

Workflows imported from JSON are imported as inactive by default. This is intentional — cron-triggered workflows fire immediately upon activation, so all environment variables and credentials must be verified first. Review each cron workflow's schedule, confirm it makes sense, then publish it manually.

Key Files in automations/

FilePurpose
emoji-approve-buffer.jsonPrimary emoji approval workflow (production)
fixed-workflow.jsonContent pipeline with fixes applied
fix-parse-regex.pyPython script to inject fixed parser into workflow JSON
emoji-with-nexus.jsonFinal emoji workflow with Nexus CRM integration
fixed-content-pipeline.jsonContent pipeline with all fixes
package.jsonReact Email dependencies for email templates
lead-generation.jsonLead generation workflow (migrated from Buffer v1 REST to GraphQL API)
*-payload.jsonDebug payloads captured during development

Slack approval emojis: The standard approval emoji set for all Slack-driven workflows is: :heavy_check_mark: ✔️ (approve), :x: ❌ (reject), :alphabet-white-question: ❓ (needs clarification). Currently only :heavy_check_mark: triggers automation; reject and question are reserved for future use.

n8n 2.x Breaking Changes

The Travel Tamers instance runs n8n 2.6.3. Several breaking changes from the 1.x → 2.x upgrade affect how workflows are managed and deployed. If you're coming from n8n 1.x documentation or tutorials, these are the gotchas that'll bite you.

CLI Command Changes

Old (1.x)New (2.x)Notes
n8n update:workflow --id=ID --active=true n8n publish:workflow --id=ID update:workflow is deprecated. Use publish:workflow to activate.
n8n update:workflow --id=ID --active=false n8n unpublish:workflow --id=ID Use unpublish:workflow to deactivate.
workflow.activeChange hook workflow.published hook Internal event name changed. Affects custom hooks only.
--tunnel option (removed) The built-in tunnel for webhook testing is gone. Use ngrok or similar.

Other 2.x Changes

  • OAuth callbacks enforce user auth: You must be logged into the n8n UI before starting an OAuth flow. Unauthenticated callbacks are rejected.
  • Task runners removed from main image: Python/shell task runners are no longer bundled in the n8nio/n8n Docker image. If you need Python execution nodes, you must run a separate n8n/runners container.
  • Stricter settings file permissions: n8n now checks file permissions on its settings/config files. Make sure the n8n user owns the data volume.
  • "Active version not found" errors: Workflows imported via CLI or API that haven't been explicitly published will throw this error when triggered. After importing, always run n8n publish:workflow --id=ID.

Bulk publish after import: When deploying multiple workflows at once, you must publish each one. On the Travel Tamers instance, all 35 deployed workflows were bulk-published after the initial import to resolve "Active version not found" errors across the board.

Deployment

The n8n instance runs as a Docker container on the VPS, managed through Docker Compose. Workflows are deployed by importing JSON files into the running instance, then publishing them via the CLI.

Docker Container

PropertyValue
Imagen8nio/n8n:latest (currently 2.6.3)
Port5678 (internal)
Domainautomations.traveltamers.com (via Traefik)
Data VolumePersistent volume for workflows, credentials, execution history
AuthBasic auth (N8N_BASIC_AUTH_USER / N8N_BASIC_AUTH_PASSWORD)

Deploy Workflow (n8n 2.x Process)

Copy workflow JSON to VPS

scp workflow.json shane@76.13.120.235:/tmp/

Import into n8n container

sudo docker cp /tmp/workflow.json n8n:/tmp/
sudo docker exec n8n n8n import:workflow --input=/tmp/workflow.json

Check for duplicates

import:workflow creates a new copy — it doesn't update existing workflows. Check the n8n UI for duplicates and note the old and new workflow IDs.

Unpublish the old workflow, publish the new one

sudo docker exec n8n n8n unpublish:workflow --id=OLD_ID
sudo docker exec n8n n8n publish:workflow --id=NEW_ID

Restart n8n

sudo docker restart n8n

Don't use update:workflow: The old n8n 1.x command update:workflow --active=true/false is deprecated in n8n 2.x. It may still work but it's unreliable — use publish:workflow and unpublish:workflow instead.

Full Deployment Checklist

Verify environment variables

Ensure all env vars (API keys, Buffer profile IDs, Slack channel IDs, webhook secrets, n8n config) are set in the Docker Compose file.

Import workflow JSONs

Import all workflow JSONs using the deploy process above. Workflows are created as inactive by default.

Configure n8n credentials

Create the 3 credentials (Slack Bot Token, TT Fresh DB, Nexus CRM DB) in the n8n UI if they don't already exist.

Publish all workflows

Run n8n publish:workflow --id=ID for each workflow. Without this step, webhook and cron triggers will throw "Active version not found" errors.

Test each workflow

Open each workflow and run manually once to verify all nodes succeed with real credentials.

Monitor first automated runs

Watch #automation-errors in Slack for the first 24 hours after deployment.

Webhook URL Configuration

The WEBHOOK_URL environment variable must be set to https://automations.traveltamers.com for webhook-triggered workflows to generate correct callback URLs. This URL is used by Slack event subscriptions and all inbound webhooks.

Current status (March 2026): 35 workflows deployed and published, 3 API credentials configured, all 12 Slack channel IDs set, 29 workflows migrated to environment variables, Buffer GraphQL API fully integrated. The system has been running stable on n8n 2.6.3 since initial deployment.

n8n sign in page with pink accent button and email password fields
n8n login at automations.traveltamers.com — self-hosted automation platform