AutomationFlowsData & Sheets › Job.5_fsm_decision_engine

Job.5_fsm_decision_engine

job.5_fsm_decision_engine. Uses postgres. Event-driven trigger; 8 nodes.

Event trigger★★★★☆ complexity8 nodesPostgres
Data & Sheets Trigger: Event Nodes: 8 Complexity: ★★★★☆ Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "job.5_fsm_decision_engine",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "id": "fb65a333-c105-4b3e-9976-e795324690a5",
      "name": "When clicking \u2018Execute workflow\u2019"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT\n    a.application_id,\n    a.current_state,\n    e.payload->>'email_id' AS email_id,\n    MAX(ec.payload->>'category') AS email_category\nFROM job_applications a\nJOIN events e\n    ON e.event_type = 'EMAIL_LINKED'\n    AND e.application_id = a.application_id\nJOIN events ec\n    ON ec.event_type = 'EMAIL_CLASSIFIED'\n    AND ec.payload->>'email_id' = e.payload->>'email_id'\n    AND ec.payload->>'category' != 'NON_JOB'\nWHERE NOT EXISTS (\n    SELECT 1\n    FROM events ed\n    WHERE ed.event_type = 'FSM_TRANSITION_ELIGIBLE'\n      AND ed.payload->>'email_id' = e.payload->>'email_id'\n)\nGROUP BY a.application_id, a.current_state, e.payload->>'email_id';",
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        208,
        0
      ],
      "id": "5675ee4d-50a9-408c-809b-fba3aa06355f",
      "name": "New linked but No FSM",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const FSM = {\n  terminalStates: new Set([\"OFFER\", \"REJECTED\", \"ARCHIVED\"]),\n\n  transitions: {\n    DISCOVERED: {\n      MANUAL: { APPLY: \"APPLIED\" },\n    },\n\n    APPLIED: {\n      EMAIL: { ACKNOWLEDGED: \"ACKNOWLEDGED\",\n               ASSESSMENT: \"ASSESSMENT\",\n             },\n      TIMEOUT: { NO_RESPONSE: \"NO_RESPONSE\" },\n    },\n\n    ACKNOWLEDGED: {\n      EMAIL: {\n        ASSESSMENT: \"ASSESSMENT\",\n        INTERVIEW: \"INTERVIEW\",\n        REJECTED: \"REJECTED\",\n      },\n      TIMEOUT: { NO_RESPONSE: \"NO_RESPONSE\" },\n    },\n\n    ASSESSMENT: {\n      EMAIL: {\n        INTERVIEW: \"INTERVIEW\",\n        REJECTED: \"REJECTED\",\n      },\n    },\n\n    INTERVIEW: {\n      EMAIL: {\n        OFFER: \"OFFER\",\n        REJECTED: \"REJECTED\",\n      },\n    },\n\n    NO_RESPONSE: {\n      EMAIL: {\n        ACKNOWLEDGED: \"ACKNOWLEDGED\",\n        ASSESSMENT: \"ASSESSMENT\",\n        INTERVIEW: \"INTERVIEW\",\n      },\n      TIMEOUT: { ARCHIVED: \"ARCHIVED\" },\n      MANUAL: { REJECTED: \"REJECTED\" },\n    },\n\n    ANY: {\n      MANUAL: { WITHDRAWN: \"WITHDRAWN\" },\n    },\n  },\n};\n\n// Strict inputs \u2014 no guessing allowed\nconst {\n  application_id,\n  email_id,\n  current_state,\n  trigger_type,\n  intent,\n} = $json;\n\nif (!trigger_type || !intent) {\n  throw new Error(\n    \"FSM rules contract violation: trigger_type and intent are required\"\n  );\n}\n\n// Terminal guard\nif (FSM.terminalStates.has(current_state)) {\n  return [{\n    json: {\n      application_id,\n      email_id,\n      from_state: current_state,\n      to_state: null,\n      reason: \"TERMINAL_STATE\",\n    },\n  }];\n}\n\n// Resolve transition\nconst toState =\n  FSM.transitions.ANY?.[trigger_type]?.[intent] ??\n  FSM.transitions[current_state]?.[trigger_type]?.[intent] ??\n  null;\n\nreturn [{\n  json: {\n    application_id,\n    email_id,\n    from_state: current_state,\n    to_state: toState,\n    trigger_type,\n    intent,\n  },\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        992,
        0
      ],
      "id": "f49345eb-6c8d-45ec-a6da-beb7127fba21",
      "name": "FSM rules"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "57bb395a-ecdc-4d1a-8663-aeeff292d24a",
              "leftValue": "={{ $json.to_state !== null }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1168,
        0
      ],
      "id": "f0646a5a-ba44-400e-ba96-1c33d5501ffc",
      "name": "Next state is not NULL"
    },
    {
      "parameters": {
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "table": {
          "__rl": true,
          "value": "events",
          "mode": "list",
          "cachedResultName": "events"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "application_id": "={{ $json.application_id }}",
            "event_type": "FSM_TRANSITION_ELIGIBLE",
            "payload": "={{\n  {\n    email_id: $json.email_id,\n    from_state: $json.from_state,\n    to_state: $json.to_state\n  }\n}}",
            "triggered_by": "system"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "event_id",
              "displayName": "event_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "event_type",
              "displayName": "event_type",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "application_id",
              "displayName": "application_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true
            },
            {
              "id": "payload",
              "displayName": "payload",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "object",
              "canBeUsedToMatch": true
            },
            {
              "id": "triggered_by",
              "displayName": "triggered_by",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "dateTime",
              "canBeUsedToMatch": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        1392,
        -192
      ],
      "id": "50a2076f-1897-4594-b714-fae54f0c4d37",
      "name": "Emit ELIGIBLE FSM",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "table": {
          "__rl": true,
          "value": "events",
          "mode": "list",
          "cachedResultName": "events"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "application_id": "={{ $json.application_id }}",
            "event_type": "FSM_NO_TRANSITION",
            "payload": "={{\n  {\n    email_id: $json.email_id,\n    reason: \"no_valid_transition\"\n  }\n}}",
            "triggered_by": "system"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "event_id",
              "displayName": "event_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "event_type",
              "displayName": "event_type",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "application_id",
              "displayName": "application_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true
            },
            {
              "id": "payload",
              "displayName": "payload",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "object",
              "canBeUsedToMatch": true
            },
            {
              "id": "triggered_by",
              "displayName": "triggered_by",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "dateTime",
              "canBeUsedToMatch": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        1392,
        160
      ],
      "id": "785e8644-76a6-4e1c-9724-b85543ab4f84",
      "name": "Emil NO FSM ELIGIBLE",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const EMAIL_CATEGORY_TO_INTENT = {\n  APPLICATION_ACK: \"ACKNOWLEDGED\",\n  INTERVIEW_SIGNAL: \"INTERVIEW\",\n  ASSESSMENT_SIGNAL: \"ASSESSMENT\",\n  OFFER_SIGNAL: \"OFFER\",\n  REJECTION_SIGNAL: \"REJECTED\",\n};\n\n// Inputs\nconst {\n  application_id,\n  email_id,\n  current_state,\n  email_category,\n} = $json;\n\n// Translate strictly\nconst intent = EMAIL_CATEGORY_TO_INTENT[email_category];\n\nif (!intent) {\n  throw new Error(\n    `FSM normalization failed: unmapped email_category '${email_category}'`\n  );\n}\n\nreturn [{\n  json: {\n    application_id,\n    email_id,\n    current_state,\n\n    trigger_type: \"EMAIL\",\n    intent,\n  },\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        816,
        0
      ],
      "id": "381cf251-ba2c-4f04-87de-3c06cd4d17ff",
      "name": "Normalize intent"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "1b6d6ab5-080b-4975-b13c-c86db3ebeadc",
              "leftValue": "={{ $json.email_category == 'NON_JOB'}}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        416,
        0
      ],
      "id": "863fe75a-e7eb-4b3f-b2f9-8c408f6e48fa",
      "name": "If"
    }
  ],
  "connections": {
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "New linked but No FSM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New linked but No FSM": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FSM rules": {
      "main": [
        [
          {
            "node": "Next state is not NULL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Next state is not NULL": {
      "main": [
        [
          {
            "node": "Emit ELIGIBLE FSM",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Emil NO FSM ELIGIBLE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize intent": {
      "main": [
        [
          {
            "node": "FSM rules",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [],
        [
          {
            "node": "Normalize intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false
  },
  "versionId": "ecc5e18c-9d2f-4023-9371-f854c2b2bea8",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "UHQyM7KDr9TOxUh3",
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

job.5_fsm_decision_engine. Uses postgres. Event-driven trigger; 8 nodes.

Source: https://github.com/daemon-Ad/Job-Aggregation-and-Email-Intelligence-System/blob/main/workflows/job.5_fsm_decision_engine.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

Reagendamiento_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 89 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and prod

HTTP Request, Google Drive, Google Docs +5
Data & Sheets

Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.

N8N Nodes Evolution Api, Redis, HTTP Request +3
Data & Sheets

Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

Components. Uses postgres, readWriteFile. Event-driven trigger; 42 nodes.

Postgres, Read Write File