# N8N API Key Mismatch - 401 Unauthorized ## ๐Ÿ” Problem Identified **Error**: `401 - "Unauthorized"` **Log**: `Invalid API key { received: 'present', expected: 'configured' }` **Status**: - โœ… Endpoint is being called (`Mission Created Webhook Received`) - โœ… API key is being sent (`received: 'present'`) - โŒ **API key values don't match** --- ## ๐Ÿ” Root Cause The API key sent by N8N in the `x-api-key` header **does not match** the `N8N_API_KEY` environment variable on the server. ### How It Works 1. **Server sends to N8N** (line 420 in `app/api/missions/route.ts`): ```typescript config: { N8N_API_KEY: process.env.N8N_API_KEY, // From server environment MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL } ``` 2. **N8N uses this value** in "Save Mission To API" node: ``` x-api-key: {{ $node['Process Mission Data'].json.config.N8N_API_KEY }} ``` 3. **Server receives and validates** (line 42 in `app/api/missions/mission-created/route.ts`): ```typescript if (apiKey !== expectedApiKey) { // Keys don't match โ†’ 401 error } ``` ### The Problem **If `process.env.N8N_API_KEY` is `undefined` or empty** when sending to N8N: - N8N receives `undefined` or empty string - N8N sends empty string in header - Server expects the actual key value - **Keys don't match โ†’ 401 error** --- ## โœ… Solution ### Step 1: Verify N8N_API_KEY is Set **Check your environment variables**: ```bash # In your terminal (if running locally) echo $N8N_API_KEY # Or check in your application # Create a test endpoint to verify ``` **Expected**: Should show the actual API key value (not empty) ### Step 2: Ensure Same Key in Both Places **The key must be the same in**: 1. **Server environment variable**: `N8N_API_KEY=your-key-here` 2. **N8N workflow config**: The value sent in `config.N8N_API_KEY` **If they're different**, they won't match! ### Step 3: Check What N8N is Sending **In N8N workflow "Save Mission To API" node**, verify: 1. **Header `x-api-key` value**: ``` {{ $node['Process Mission Data'].json.config.N8N_API_KEY }} ``` 2. **What this resolves to**: - If `config.N8N_API_KEY` is `undefined` โ†’ N8N sends empty string - If `config.N8N_API_KEY` has a value โ†’ N8N sends that value 3. **Check N8N execution logs**: - Look at the actual request being sent - Check the `x-api-key` header value - Compare with your server's `N8N_API_KEY` ### Step 4: Fix the Mismatch **Option A: If server's N8N_API_KEY is undefined** Add to `.env.local` (or production environment): ```env N8N_API_KEY=LwgeE1ntADD20OuWC88S3pR0EaO7FtO4 ``` Restart the application. **Option B: If N8N is sending wrong value** Check what value N8N has in `config.N8N_API_KEY`: - It should match the server's `N8N_API_KEY` - If different, update one to match the other **Option C: Hardcode in N8N (not recommended)** If you can't sync the values, you could hardcode in N8N: ``` x-api-key: LwgeE1ntADD20OuWC88S3pR0EaO7FtO4 ``` But this is less secure - better to use environment variable. --- ## ๐Ÿงช Testing ### Test 1: Check Server Environment **Create test endpoint**: ```typescript // app/api/test-n8n-key/route.ts import { NextResponse } from 'next/server'; export async function GET() { return NextResponse.json({ hasN8NApiKey: !!process.env.N8N_API_KEY, keyLength: process.env.N8N_API_KEY?.length || 0, keyPrefix: process.env.N8N_API_KEY ? process.env.N8N_API_KEY.substring(0, 4) + '...' : 'none' }); } ``` **Visit**: `http://localhost:3000/api/test-n8n-key` **Expected**: ```json { "hasN8NApiKey": true, "keyLength": 32, "keyPrefix": "Lwge..." } ``` ### Test 2: Check What N8N Sends **In N8N execution logs**, check the "Save Mission To API" node: - Look at the request headers - Find `x-api-key` header - Note the value **Compare** with server's `N8N_API_KEY` - they must match exactly. ### Test 3: Manual Test **Test the endpoint with the correct key**: ```bash curl -X POST https://hub.slm-lab.net/api/missions/mission-created \ -H "Content-Type: application/json" \ -H "x-api-key: LwgeE1ntADD20OuWC88S3pR0EaO7FtO4" \ -d '{ "missionId": "test-id", "name": "Test", "creatorId": "user-id" }' ``` **Expected**: 200 OK (if mission exists) or 404 (if mission doesn't exist) **If 401**: The key in the curl command doesn't match server's `N8N_API_KEY` --- ## ๐Ÿ”ง Common Issues ### Issue 1: Key is undefined when sending to N8N **Symptom**: N8N receives `undefined` or empty string in `config.N8N_API_KEY` **Cause**: `process.env.N8N_API_KEY` is not set when creating mission **Fix**: Add `N8N_API_KEY` to environment and restart ### Issue 2: Different keys in different environments **Symptom**: Works in development but not production (or vice versa) **Cause**: Different `N8N_API_KEY` values in different environments **Fix**: Use the same key in all environments, or update N8N to use environment-specific keys ### Issue 3: Key has extra spaces or characters **Symptom**: Keys look the same but don't match **Cause**: Extra spaces, newlines, or special characters **Fix**: ```env # Correct N8N_API_KEY=LwgeE1ntADD20OuWC88S3pR0EaO7FtO4 # Wrong (with quotes) N8N_API_KEY="LwgeE1ntADD20OuWC88S3pR0EaO7FtO4" # Wrong (with spaces) N8N_API_KEY = LwgeE1ntADD20OuWC88S3pR0EaO7FtO4 ``` --- ## ๐Ÿ“‹ Debugging Checklist - [ ] `N8N_API_KEY` is set in server environment - [ ] Key value matches what N8N is sending - [ ] No extra spaces or characters in key - [ ] Server has been restarted after adding key - [ ] Test endpoint shows key is loaded - [ ] N8N execution logs show correct key in header - [ ] Manual curl test works with the key --- ## ๐ŸŽฏ Expected Flow After Fix 1. **Mission created** โœ… 2. **N8N workflow triggered** โœ… 3. **Server sends `config.N8N_API_KEY` to N8N** โœ… 4. **N8N creates integrations** โœ… 5. **N8N calls `/api/missions/mission-created`** โœ… 6. **N8N sends `x-api-key` header with same value** โœ… 7. **Server validates key matches** โœ… 8. **IDs saved to database** โœ… --- ## ๐Ÿ“ Summary **Problem**: 401 Unauthorized - API key mismatch **Root Cause**: The API key sent by N8N doesn't match the server's `N8N_API_KEY` **Solution**: 1. Ensure `N8N_API_KEY` is set in server environment 2. Ensure N8N uses the same key value 3. Verify keys match exactly (no spaces, same value) **After Fix**: The endpoint should return 200 OK and save integration IDs. --- **Document Created**: $(date) **Priority**: CRITICAL - Blocks integration IDs from being saved