# Setup and Configuration Guide: AI Recruitment Platform This guide covers the complete step-by-step setup process for the database (Supabase), the automation engine (n8n), the AI models, and the Next.js application. --- ## 1. Supabase (Database Setup) Supabase provides the relational database, vector store, and API services. ### A. Extensions Needed The semantic similarity matching uses vector calculations. You must enable the `vector` extension. * Go to **Database** -> **Extensions** -> Search for `vector` -> Click **Enable**. ### B. Table Schemas You need to create the following five tables. Run these SQL commands in the Supabase **SQL Editor**: ```sql -- 1. Jobs Table CREATE TABLE jobs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL, requirements JSONB NOT NULL DEFAULT '{}'::jsonb, -- Store raw text + lowercase buzzwords list created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- 2. Candidates Table CREATE TABLE candidates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, phone TEXT, embedding VECTOR(1536), -- 1536 dimensions matching gemini-embedding-001 contact_info JSONB DEFAULT '{}'::jsonb, -- Store A-Z summary, skills list, phone, etc. created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- 3. Scores Table (Decoupled Evaluations) CREATE TABLE scores ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL, job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL, ai_score INTEGER NOT NULL CHECK (ai_score >= 0 AND ai_score <= 100), risk_level TEXT NOT NULL, -- 'Low', 'Medium', 'High' evaluation JSONB NOT NULL DEFAULT '{}'::jsonb, -- Store { summary, classification, suggestions } created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- 4. Interviews Table CREATE TABLE interviews ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL, job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL, interview_date TIMESTAMP WITH TIME ZONE NOT NULL, stage TEXT DEFAULT 'Screening'::text NOT NULL, -- 'Screening', 'Technical', 'Cultural', 'Offer', 'Hired', 'Rejected' feedback TEXT, -- Stores JSON string comments timeline array [{ id, text, timestamp, author, stage, isAi }] pinned BOOLEAN DEFAULT false NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); ``` ### C. pgvector Similarity Match Function Next, create the PostgreSQL RPC function to rank candidates against job requirements using cosine distance. Run this in the SQL Editor: ```sql CREATE OR REPLACE FUNCTION match_candidates ( query_embedding VECTOR(1536), match_threshold DOUBLE PRECISION, match_count INT ) RETURNS TABLE ( id UUID, name TEXT, email TEXT, phone TEXT, contact_info JSONB, similarity DOUBLE PRECISION ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT candidates.id, candidates.name, candidates.email, candidates.phone, candidates.contact_info, 1 - (candidates.embedding <=> query_embedding) AS similarity FROM candidates WHERE 1 - (candidates.embedding <=> query_embedding) > match_threshold ORDER BY candidates.embedding <=> query_embedding LIMIT match_count; END; $$; ``` --- ## 2. n8n (Automation Engine Setup) n8n acts as the central automation orchestrator, receiving hooks from Next.js, calling LLM chains, and saving responses. ### A. Minimum Requirements * **n8n instance**: Self-hosted (Docker / npm package) or n8n Cloud. * **Version**: n8n v1.0.0 or later (v1.30+ recommended for advanced LangChain integration features). * **Network Access**: The n8n instance must be publicly reachable (using domain/tunnel like ngrok/Cloudflare) so Vercel can post webhook payloads. ### B. Supported LLM Models & Providers The n8n Kickstarter script (`deploy-n8n-v2.ts`) supports credentials setup and deployment for: | Provider | Choice ID | Default Model | Node Type | API Key Env Var | | :--- | :--- | :--- | :--- | :--- | | **Deepseek** | `1` | `deepseek-chat` | `@n8n/n8n-nodes-langchain.lmChatDeepSeek` | `DEEPSEEK_API_KEY` | | **OpenAI** | `2` | `gpt-4o-mini` | `@n8n/n8n-nodes-langchain.lmChatOpenAi` | `OPENAI_API_KEY` | | **Google Gemini** | `3` | `gemini-1.5-flash` | `@n8n/n8n-nodes-langchain.lmChatGoogleGemini` | `GEMINI_API_KEY` | | **Anthropic** | `4` | `claude-3-5-sonnet-latest` | `@n8n/n8n-nodes-langchain.lmChatAnthropic` | `ANTHROPIC_API_KEY` | ### C. Deploying via Script 1. Configure the `.env` variables (`N8N_HOST`, `N8N_API_KEY`). 2. Run the deployment script: ```bash npx ts-node -O '{"module": "commonjs"}' scripts/deploy-n8n-v2.ts \ --primary-provider=deepseek \ --fallback \ --fallback-provider=gemini ``` *Note: The script dynamically detects if a credential exists in n8n. If found, it safely reuses the credential to avoid entering keys repeatedly.* ### D. Email Ingestion & Notification Workflows To deploy the email-based workflows (receiving CVs via IMAP and notifying candidates via SMTP on stage changes), run the dedicated email deployment script: ```bash npx tsx scripts/deploy-email-automation.ts ``` This script will: 1. Create or update `Semillero2_IMAP_V2` credentials in n8n (utilizes standard secure port `993`). 2. Create or update `Semillero2_SMTP` credentials in n8n (utilizes `.env` SMTP variables). 3. Deploy and activate **Semillero2: Email Ingestion Listener** (listens for emails with PDF attachments, extracts text, and triggers candidate evaluation). 4. Deploy and activate **Semillero2: Stage Change Email Notifier** (listens for database webhooks on stage updates and sends templated HTML emails to candidates). ### E. Supabase Database Webhook Setup (Stage Changes) The email notifier workflow is triggered by a Supabase Database Webhook when candidate stages are updated. Run the following SQL in your Supabase SQL Editor to enable the trigger: ```sql -- Enable pg_net extension for network calls CREATE EXTENSION IF NOT EXISTS pg_net; -- Create trigger function CREATE OR REPLACE FUNCTION notify_interview_stage_change() RETURNS TRIGGER AS $$ DECLARE payload_body JSONB; BEGIN IF (TG_OP = 'INSERT' OR OLD.stage IS DISTINCT FROM NEW.stage) THEN payload_body := jsonb_build_object( 'old_record', CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE to_jsonb(OLD) END, 'new_record', to_jsonb(NEW) ); PERFORM net.http_post( url := 'https://n8n.gaboggamer.online/webhook/stage-changed', headers := '{"Content-Type": "application/json"}'::jsonb, body := payload_body ); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; -- Drop existing trigger if it exists DROP TRIGGER IF EXISTS tr_interview_stage_change ON public.interviews; -- Bind trigger to public.interviews table CREATE TRIGGER tr_interview_stage_change AFTER INSERT OR UPDATE ON public.interviews FOR EACH ROW EXECUTE FUNCTION notify_interview_stage_change(); ``` --- ## 3. Supported Webhooks / API Endpoints The n8n workflow exposes the following endpoints (automatically registered upon deployment): ### 1. `POST /webhook/evaluate-candidate` Triggered by Next.js when parsing CVs or running AI evaluations. * **Payload**: ```json { "candidateId": "uuid-here", "jobId": "uuid-here", "candidateName": "John Doe", "jobTitle": "React Developer", "jobRequirements": "Stack: React, TypeScript, Tailwind...", "text": "Extracted text contents of candidate resume..." } ``` * **Workflow Operations**: * Extracts resume data (Structured Output Parser). * Inserts parsed details (skills list, summary) into `candidates` table. * Runs the primary evaluation chain (Deepseek) with a fallback to Google Gemini. * Calculates calibrated scores (0-100), risk classifications (`Qualified`, `Unqualified`, `Review`), and appends next-step suggestions. * Stores result in `scores` table. ### 2. `POST /webhook/suggest-next-steps` Triggered by Next.js when clicking "Get AI Suggestion" inside Interviews. * **Payload**: ```json { "candidateName": "John Doe", "jobTitle": "React Developer", "currentStage": "Technical", "candidateSummary": "Extracted summary...", "candidateSkills": ["react", "typescript"], "jobRequirements": "Stack: React...", "commentHistory": [ ... ], "lang": "en" | "es" } ``` * **Workflow Operations**: * Assembles context of the candidate, job description, and interview timeline. * Queries LLM to output a brief, actionable list of next steps. * Instructs LLM to write in Spanish if `lang` is `"es"`, otherwise English. --- ## 4. Next.js (Application Setup) ### A. Environment variables Make sure all items in `.env` are configured: * `APP_PASSWORD`: Protects the lock screen. Default fallback is `Semillero2026!`. * `GEMINI_API_KEY`: Mandatory for client-side embedding generation (`models/gemini-embedding-001`) and candidate comparisons. * `NEXT_PUBLIC_SUPABASE_URL` & `SUPABASE_SECRET_KEY`: Service role keys to read/write without RLS checks. ### B. Deployment Deploy to Vercel with all environment variables matching your local setup. Ensure the API routes can freely reach the database and the n8n webhooks.