Skip to content

N8N Integration ​

N8N is used as the workflow orchestration engine for document processing, AI extraction, and embedding generation.

Architecture ​

mermaid
sequenceDiagram
    participant App as PIP AI Server
    participant N8N as N8N Workflow
    participant AI as OpenAI / LLM
    participant DB as Supabase DB
    participant Store as Supabase Storage

    App->>N8N: Webhook trigger (with secret)
    N8N->>Store: Download document (signed URL)
    N8N->>AI: OCR / Extract / Embed
    N8N->>DB: Upsert structured data
    N8N->>App: Callback (status + results)

Workflows ​

1. Spec Processing ​

Trigger: POST {N8N_BASE_URL}/webhook/pip-ai-spec-upsert

Payload:

json
{
  "spec_upload_id": "uuid",
  "document_id": "uuid",
  "storage_path": "specs/brand/upload_id/file.pdf",
  "brand_name": "Holiday Inn Express",
  "area": "guest-room",
  "property_name": "San Francisco Airport",
  "job_id": "uuid"
}

Steps:

  1. Download spec PDF via signed URL
  2. OCR/LLM extraction → structured sections
  3. Upsert pip_ai_spec_sections with unique (spec_upload_id, spec_number)
  4. Generate embeddings via OpenAI text-embedding-3-small
  5. Update processing status
  6. Callback to app

2. PIP Processing ​

Trigger: POST {N8N_BASE_URL}/webhook/pip-ai-pip-processor

Payload:

json
{
  "document_id": "uuid",
  "project_id": "uuid",
  "storage_path": "pips/project_id/doc_id/file.pdf",
  "job_id": "uuid",
  "callback_url": "https://app.example/api/pip/callback"
}

Steps:

  1. Resolve the Supabase project from the Supabase Projects data table by callback_url.extractDomain()
  2. Validate inbound x-webhook-secret against Supabase Projects.Webhook_Secret
  3. Download PIP PDF
  4. OCR/LLM extraction → structured line items
  5. Upsert pip_ai_pip_items with unique (document_id, code)
  6. For each item, query pip_ai_search_specs() (filtered by brand + areas)
  7. Upsert candidate matches into pip_ai_matches with unique (pip_item_id, spec_section_id)
  8. Update processing status
  9. Callback to app with x-webhook-secret and { document_id, project_id, job_id, status, items_count }

The PIP workflow resolves Supabase config from the Supabase Projects data table by callback_url.extractDomain(). The row must include Webhook_Secret, which must match the app-side N8N_WEBHOOK_SECRET. The workflow intentionally does not read $env.* in n8n.

3. Floor Plan Processing ​

Trigger: POST {N8N_BASE_URL}/webhook/pip-ai-floor-plan-processor

Steps:

  1. Download floor plan
  2. Render to PNG + generate thumbnail
  3. OCR visible labels → extract spec codes
  4. Update pip_ai_floor_plans.extracted_spec_codes[]
  5. Link to known spec sections
  6. Callback to app

4. Submittal PDF Export ​

Trigger: POST {N8N_BASE_URL}/webhook/pip-ai-submittal-exportWorkflow JSON: instructions/n8n-workflows/pip-ai-submittal-export.json

Payload:

json
{
  "job_id": "uuid",
  "project_id": "uuid",
  "format": "pdf",
  "storage_bucket": "pip-ai-docs",
  "storage_path": "exports/submittals/project_id/job_id.pdf",
  "callback_url": "https://app.example/api/submittal-export/callback"
}

Worker contract:

  1. Resolve the Supabase project from the Supabase Projects data table by callback_url.extractDomain()
  2. Validate x-webhook-secret against Supabase Projects.Webhook_Secret
  3. Load pip_ai_submittal_export_jobs by job_id through PIP AI - Supabase Router
  4. Read manifest_snapshot as the immutable export source
  5. Render the PDF through Supabase Projects.Gotenberg_URL and upload it to storage_bucket/storage_path
  6. Mark pip_ai_submittal_export_jobs as completed or failed through PIP AI - Supabase Router
  7. Call callback_url with x-webhook-secret as a best-effort notification

The Supabase Projects data-table row for the callback domain must include Domain, Supabase_URL, Webhook_Secret, and Gotenberg_URL. The workflow intentionally does not read $env.* in n8n. The direct Supabase Router update is the authoritative job state; callback delivery must not turn a successful storage upload into a failed export.

Callback payloads:

json
{
  "job_id": "uuid",
  "project_id": "uuid",
  "status": "completed",
  "storage_path": "exports/submittals/project_id/job_id.pdf"
}
json
{
  "job_id": "uuid",
  "project_id": "uuid",
  "status": "failed",
  "error": "Renderer failed"
}

The callback also accepts status: "processing" and normalizes legacy N8N status: "succeeded" to completed.

5. Legacy Document Builder PDF Export ​

Handles the original document builder PDF generation.

6. AI Image Generation (Nano Banana) ​

See Nano Banana integration.

Webhook Security ​

All webhooks use a shared secret for authentication:

Header: x-webhook-secret: <N8N_WEBHOOK_SECRET>

Use the same header in both directions: Nuxt server routes send it when triggering n8n workers, and n8n sends it back when calling Nuxt callbacks. Never expose N8N webhook URLs or secrets through NUXT_PUBLIC_* runtime config.

Job Queue Pattern ​

N8N can also poll the pip_ai_upload_jobs table:

sql
-- Claim a job
UPDATE pip_ai_upload_jobs
SET status = 'processing', locked_at = now(), locked_by = 'n8n-worker'
WHERE id = '<job_id>' AND status = 'queued'
RETURNING *;

Idempotency ​

All N8N workflows are designed to be safely re-runnable:

  • Upserts use unique constraints
  • Job status tracking prevents double-processing
  • retry_count tracks reprocessing attempts

Configuration ​

ini
N8N_BASE_URL=https://your-n8n-instance.com
N8N_WEBHOOK_SPEC_PROCESSOR=/webhook/pip-ai-spec-upsert
N8N_WEBHOOK_PIP_PROCESSOR=/webhook/pip-ai-pip-processor
N8N_WEBHOOK_FLOOR_PLAN_PROCESSOR=/webhook/pip-ai-floor-plan-processor
N8N_WEBHOOK_SUBMITTAL_EXPORT=/webhook/pip-ai-submittal-export
N8N_WEBHOOK_SECRET=your-shared-secret
SUPABASE_SERVICE_ROLE_KEY=server-only-service-role-key

These Nuxt variables are server-side only. The submittal export n8n workflow itself does not use $env.*; configure the matching secret in the n8n Supabase Projects data table.

Built with VitePress