no-code-pipelines

SKILL.md

Skill: No-Code Pipelines

Generate importable n8n workflow JSON files that sync data between Personize and 400+ apps. The user describes their source/destination and you produce ready-to-import .json files they paste into n8n.


When to Use This Skill

  • User wants to connect Personize to HubSpot, Salesforce, Google Sheets, Slack, Postgres, or any app with an API
  • User asks for "no-code" or "n8n" or "workflow automation" integration
  • User wants scheduled syncs without writing code
  • User needs webhook-triggered data flows

When NOT to Use This Skill

  • Want TypeScript/SDK code you can test and version → use entity-memory (CRM sync section)
  • Need durable pipelines with retries, waits, and complex orchestration → use code-pipelines
  • Only need a single API call → use entity-memory directly

Prerequisites

  • An n8n instance (cloud or self-hosted)
  • A Personize secret key (sk_live_...) configured as an HTTP Header Auth credential in n8n:
    • Name: Authorization
    • Value: Bearer sk_live_...
    • Credential name: Personize API Key (referenced in all templates)

Personize API Endpoints for n8n

All requests go to https://agent.personize.ai with Authorization: Bearer sk_live_... header.

Action Method Path Use Case
Batch sync in (structured + AI) POST /api/v1/batch-memorize Sync CRM records into Personize with per-property AI control
Single memorize (AI) POST /api/v1/memorize Store one content item with AI extraction + vectors
Structured upsert POST /api/v1/upsert Store properties without AI extraction
Semantic search POST /api/v1/smart-recall Search memories by meaning
Export/filter records POST /api/v1/search Query records by property conditions
Entity context digest POST /api/v1/smart-memory-digest Get compiled context for an entity
Smart context POST /api/v1/ai/smart-guidelines Get relevant variables for a message
Auth check GET /api/v1/me Verify key and read plan limits

n8n Workflow JSON Structure

Every workflow is a JSON object with nodes, connections, and settings. Users import it via Menu > Import from File or Ctrl+V paste into the n8n canvas.

{
  "name": "Workflow Name",
  "nodes": [ /* array of node objects */ ],
  "connections": { /* source node name → target connections */ },
  "settings": { "executionOrder": "v1" }
}

Constraints

Keywords follow RFC 2119: MUST = non-negotiable, SHOULD = strong default (override with stated reasoning), MAY = agent discretion.

  1. MUST use unique node names -- because node names serve as connection keys in the connections object; duplicates cause wiring failures on import.
  2. MUST set "settings": { "executionOrder": "v1" } -- because omitting it causes non-deterministic node execution order in n8n.
  3. MUST reference credentials by the display name the user created in n8n -- because n8n resolves credentials by name, not by ID; mismatched names cause auth failures on import.
  4. SHOULD space nodes ~220px horizontally in position values [x, y] -- because consistent spacing produces readable canvas layouts that users can navigate without rearranging.
  5. SHOULD use UUID-like strings for node IDs (e.g., "pz-sync-001") -- because predictable IDs simplify debugging and log correlation.
  6. MUST connect Loop Over Items output 0 to the loop body and output 1 to the post-loop node -- because reversed connections cause infinite loops or skipped processing.

Common Node Reference

Node type typeVersion
Schedule Trigger n8n-nodes-base.scheduleTrigger 1.1
Manual Trigger n8n-nodes-base.manualTrigger 1
Webhook n8n-nodes-base.webhook 2
HTTP Request n8n-nodes-base.httpRequest 4.2
Loop Over Items n8n-nodes-base.splitInBatches 3
Code (JavaScript) n8n-nodes-base.code 2
IF n8n-nodes-base.if 2
Set / Edit Fields n8n-nodes-base.set 3.4
No Operation n8n-nodes-base.noOp 1
HubSpot n8n-nodes-base.hubspot 2
Salesforce n8n-nodes-base.salesforce 1
Google Sheets n8n-nodes-base.googleSheets 4.5
Slack n8n-nodes-base.slack 2.2
Postgres n8n-nodes-base.postgres 2.5
MySQL n8n-nodes-base.mySql 2.4

HTTP Request Node Pattern for Personize

All Personize API calls use the same HTTP Request node pattern:

{
  "id": "pz-api-001",
  "name": "Personize: Batch Memorize",
  "type": "n8n-nodes-base.httpRequest",
  "typeVersion": 4.2,
  "position": [860, 300],
  "parameters": {
    "method": "POST",
    "url": "https://agent.personize.ai/api/v1/batch-memorize",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "httpHeaderAuth",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify($json.payload) }}",
    "options": {
      "response": { "response": { "fullResponse": true } }
    }
  },
  "credentials": {
    "httpHeaderAuth": {
      "id": "pz-cred",
      "name": "Personize API Key"
    }
  }
}

Expression Syntax

  • {{ $json.fieldName }} — field from previous node
  • {{ $('Node Name').item.json.field }} — field from a specific node
  • {{ JSON.stringify($json) }} — entire previous output as JSON string
  • ={{ expression }} — prefix for expression mode in parameter values

Workflow Pattern 1: Sync IN (Source App → Personize)

Flow: [Trigger] → [Fetch Records] → [Build Payload] → [Batch Memorize] → [Done]

For sources that return many records, use the batch-memorize endpoint which handles both structured storage and AI extraction in one call.

When to use Loop Over Items vs. Single Call

  • Single batch-memorize call (preferred): When the source returns all rows at once and total rows < 500. The Code node builds the entire { source, mapping, rows } payload and one HTTP Request sends it.
  • Loop Over Items with chunking: When source returns thousands of rows. Split into chunks of 50-100, send each chunk as a separate batch-memorize call with a Wait node for rate limits.

Code Node: Build batch-memorize Payload

This Code node transforms source records into the Personize batch-memorize payload:

// Input: array of source records from previous node
const items = $input.all();

const rows = items.map(item => item.json);

const payload = {
  source: 'n8n-sync',
  mapping: {
    entityType: 'contact',
    email: 'email',           // source field name containing email
    runName: 'n8n-sync-' + Date.now(),
    properties: {
      // Structured fields — stored directly
      email:      { sourceField: 'email',      collectionId: 'YOUR_COL_ID', collectionName: 'Contacts Standard Schema' },
      first_name: { sourceField: 'firstname',  collectionId: 'YOUR_COL_ID', collectionName: 'Contacts Standard Schema' },
      last_name:  { sourceField: 'lastname',   collectionId: 'YOUR_COL_ID', collectionName: 'Contacts Standard Schema' },
      company:    { sourceField: 'company',    collectionId: 'YOUR_COL_ID', collectionName: 'Contacts Standard Schema' },
      // AI extraction fields — extractMemories: true
      notes: {
        sourceField: 'notes',
        collectionId: 'YOUR_GEN_COL_ID',
        collectionName: 'Generated Content',
        extractMemories: true,
      },
    },
  },
  rows: rows,
  chunkSize: 1,
};

return [{ json: { payload } }];

Important: Replace YOUR_COL_ID and YOUR_GEN_COL_ID with actual collection IDs. The user can find these by calling GET /api/v1/collections or using client.collections.list() from the SDK.

Per-Property extractMemories Decision

Rule of thumb: Set extractMemories: true on free-form text (notes, transcripts, emails). Omit it for structured fields (email, name, dates, counts). See the entity-memory skill's reference/memorize.md for the complete decision table and examples.


Workflow Pattern 2: Sync OUT (Personize → Destination App)

Flow: [Trigger] → [Export from Personize] → [Loop Over Items] → [Push to Destination] → [Done]

Use the export endpoint to query records by filters, then push each to the destination.

Code Node: Build Export Query

return [{
  json: {
    groups: [{
      id: 'g1',
      logic: 'AND',
      conditions: [
        { property: 'company', operator: 'IS_SET' }
      ]
    }],
    type: 'Contact',
    returnRecords: true,
    pageSize: 100,
    includeMemories: false
  }
}];

Export Filter Operators

Operator Description
EQ Equals
NEQ Not equals
CONTAINS Contains substring
GT / LT Greater / less than
IS_SET Field has a value
IS_NOT_SET Field is empty
IN Value in array

Workflow Pattern 3: Per-Record AI Enrichment

Flow: [Trigger] → [Fetch Record] → [Smart Digest] → [AI Prompt] → [Push Result]

Use smart-memory-digest to compile all known context for a contact, then feed it to the Personize prompt API for AI-generated content.

Code Node: Build Smart Digest Request

const email = $json.email;
return [{
  json: {
    email: email,
    type: 'Contact',
    token_budget: 2000,
    include_properties: true,
    include_memories: true
  }
}];

Code Node: Build AI Prompt Request

const context = $('Personize: Smart Digest').item.json.data.compiledContext;
const email = $('Personize: Smart Digest').item.json.data.properties?.email || '';

return [{
  json: {
    prompt: `Using the context below, write a personalized outreach email for ${email}.\n\nContext:\n${context}`,
    model: 'claude-sonnet-4-6'
  }
}];

Workflow Pattern 4: Webhook → Memorize (Real-time Sync)

Flow: [Webhook] → [Transform] → [Memorize Pro] → [Respond]

For real-time data capture — CRM webhooks, form submissions, Zapier/Make triggers.

Webhook Node

{
  "id": "pz-webhook-001",
  "name": "Webhook",
  "type": "n8n-nodes-base.webhook",
  "typeVersion": 2,
  "position": [200, 300],
  "parameters": {
    "path": "personize-ingest",
    "httpMethod": "POST",
    "responseMode": "lastNode"
  }
}

Code Node: Build memorize_pro Payload

const data = $json.body || $json;
return [{
  json: {
    content: `Name: ${data.name}\nEmail: ${data.email}\nCompany: ${data.company}\nNotes: ${data.notes || ''}`,
    speaker: data.source || 'webhook',
    enhanced: true,
    tags: ['webhook', 'real-time'],
    email: data.email
  }
}];

Rate Limit Handling in n8n

Personize returns HTTP 429 when rate limited. Handle this in n8n:

Option A: Retry on Fail (Recommended)

Set on the HTTP Request node:

{
  "retryOnFail": true,
  "maxTries": 3,
  "waitBetweenTries": 60000
}

Option B: Wait Node Between Batches

Add a Wait node (type n8n-nodes-base.wait) after each batch call:

{
  "id": "pz-wait-001",
  "name": "Rate Limit Wait",
  "type": "n8n-nodes-base.wait",
  "typeVersion": 1.1,
  "position": [1080, 300],
  "parameters": {
    "amount": 62,
    "unit": "seconds"
  }
}

Always call GET /api/v1/me first to read the actual limits for the user's plan.


Building a Workflow: Step-by-Step

When the user asks for an n8n workflow, follow these steps:

Step 1: Identify the direction

  • Sync IN = data flows into Personize (use batch-memorize or memorize_pro)
  • Sync OUT = data flows out of Personize (use export or recall_pro or smart-memory-digest)
  • Bidirectional = combine both patterns in one workflow

Step 2: Choose the trigger

  • Schedule — for periodic batch syncs (hourly, daily)
  • Webhook — for real-time event-driven ingestion
  • Manual — for testing and one-time imports

Step 3: Choose source/destination nodes

If n8n has a built-in node for the app (HubSpot, Salesforce, Google Sheets, Slack, Postgres), use it. Otherwise, use the HTTP Request node.

Step 4: Build the mapping

Ask the user which fields to sync. For each field, determine:

  • The source field name (from the source app)
  • The target property name (in Personize)
  • Whether it needs AI extraction (extractMemories: true)

Step 5: Generate the workflow JSON

Use the templates in templates/ as starting points. Customize:

  • Node names (descriptive of the actual source/destination)
  • API URLs and endpoints
  • Code node logic for field mapping
  • Credential references
  • Schedule interval

Step 6: Add error handling

  • Set retryOnFail: true on all HTTP Request nodes hitting Personize
  • Add Wait nodes between batches if syncing > 50 records
  • Optionally add an IF node after the API call to check {{ $json.success }} and handle errors

Step 7: Output the JSON

Save the workflow JSON to a .json file. Tell the user to:

  1. Open n8n
  2. Create a new workflow
  3. Menu > Import from File (or Ctrl+V to paste)
  4. Create the Personize API Key credential (Header Auth with Authorization: Bearer sk_live_...)
  5. Create credentials for the source/destination app
  6. Test with Manual Trigger first, then activate the Schedule Trigger

Template Workflows

Ready-to-customize templates are in the templates/ folder:

Template File Description
HubSpot → Personize templates/hubspot-to-personize.json Sync HubSpot contacts into Personize memory
Personize → Slack templates/personize-to-slack.json Export records and post digest to Slack channel
Webhook → Personize templates/webhook-to-personize.json Real-time ingest from any webhook source
Google Sheets → Personize templates/gsheets-to-personize.json Batch import rows from a Google Sheet

Each template is a complete, importable n8n workflow JSON with placeholder credentials.


n8n App Integrations (400+)

n8n has built-in nodes for these categories — use them instead of HTTP Request when available:

Category Apps
CRM HubSpot, Salesforce, Pipedrive, Zoho CRM
Spreadsheets Google Sheets, Airtable, Microsoft Excel
Communication Slack, Microsoft Teams, Discord, Telegram, Email (SMTP/IMAP)
Project Mgmt Jira, Asana, Trello, Monday.com, Notion, Linear
Databases Postgres, MySQL, MongoDB, Redis
Dev Tools GitHub, GitLab
Marketing Mailchimp, SendGrid, ActiveCampaign
E-Commerce Shopify, Stripe, WooCommerce
Cloud AWS S3/SES/Lambda, Google Cloud
AI OpenAI, Anthropic Claude, Google Gemini

For any app not listed, use the HTTP Request node with the app's REST API.


Reference Documentation & Search Queries

For n8n official docs URLs, app-specific node docs, trigger node docs, community templates, suggested search queries, and version notes — see reference/n8n-reference.md.

Weekly Installs
12
First Seen
12 days ago
Installed on
opencode12
github-copilot12
codex12
kimi-cli12
gemini-cli12
amp12