AutomationFlowsAI & RAG › Upload & Categorize Files with Supabase Storage and Secure URL Generation

Upload & Categorize Files with Supabase Storage and Secure URL Generation

ByJan Willem Altink @jwa91 on n8n.io

works with selfhosted Supabase

Event trigger★★★★☆ complexity15 nodesExecute Workflow TriggerForm TriggerHTTP Request
AI & RAG Trigger: Event Nodes: 15 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #4920 — we link there as the canonical source.

This workflow follows the Execute Workflow Trigger → Form Trigger recipe pattern — see all workflows that pair these two integrations.

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
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "ece7684b-64fa-4c80-9697-c6e7a4999eeb",
      "name": "When Executed by Another Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -1420,
        620
      ],
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "mime_type"
            },
            {
              "name": "original_filename"
            },
            {
              "name": "binary_data_base64"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "6d600da3-a9e7-4015-bc73-a0fb5e400724",
      "name": "Prepare Upload Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -940,
        440
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "bucket-mapping",
              "name": "bucket_name",
              "type": "string",
              "value": "={{ \n  (() => {\n    const mimeType = $json.mime_type || 'application/octet-stream';\n    if (mimeType.startsWith('image/')) return 'image-files';\n    if (mimeType.startsWith('audio/')) return 'audio-files';\n    if (mimeType.startsWith('video/')) return 'video-files';\n    return 'document-files';\n  })()\n}}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "5b5b91e9-b082-4d57-a191-c0fd71c84eae",
      "name": "Success Response",
      "type": "n8n-nodes-base.set",
      "position": [
        200,
        440
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "action-type",
              "name": "action",
              "type": "string",
              "value": "s3_upload"
            },
            {
              "id": "success-status",
              "name": "status",
              "type": "string",
              "value": "success"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "aa6355d3-5c2e-4b55-9f04-fcd1f5246428",
      "name": "Convert to File",
      "type": "n8n-nodes-base.convertToFile",
      "notes": "Instead of the code note i would prefer to use the n8n dedicated note for this. only thing i am not sure of now is if i map the fields correct. ",
      "position": [
        -720,
        440
      ],
      "parameters": {
        "options": {
          "fileName": "={{ $json.original_filename }}",
          "mimeType": "={{ $json.mime_type }}"
        },
        "operation": "toBinary",
        "sourceProperty": "binary_data_base64"
      },
      "typeVersion": 1.1
    },
    {
      "id": "2c1f3cf0-3726-4dd9-9121-e060e684d633",
      "name": "temp form to test workflow",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -1420,
        300
      ],
      "parameters": {
        "options": {
          "path": "action-workflows-testform",
          "appendAttribution": false
        },
        "formTitle": "test workflow form",
        "formFields": {
          "values": [
            {
              "fieldLabel": "original_filename",
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "binary_data_base64",
              "requiredField": true
            },
            {
              "fieldLabel": "mime_type"
            }
          ]
        },
        "formDescription": "use this form to test action workflows without having to use another workflow"
      },
      "typeVersion": 2.2
    },
    {
      "id": "64932e5f-b7fc-46ad-9946-41703fec5823",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1800,
        -100
      ],
      "parameters": {
        "color": 4,
        "width": 1100,
        "height": 260,
        "content": "## \ud83d\udce6 Upload files to Supabase Storage\n\n- Link to [Supabase docs](https://supabase.com/docs/guides/storage)\n\nThis workflow shows how you could upload files to a Supabase Storage Instance. It's primarily designed to be called by other workflows, as there wasn't a default node for this yet, and this could be usefull in quite a lot of other workflows. \n\nIn this particular example we use mime-type to sort files to specific buckets, but this is of course dependent on your specific storage structure. \n\nThe output is a signed url for the object, to avoid having to share secrets for entire buckets to applications that need the object as input"
      },
      "typeVersion": 1
    },
    {
      "id": "96202c5f-d481-4b20-a526-7ba0c79396a1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1800,
        180
      ],
      "parameters": {
        "width": 340,
        "height": 340,
        "content": "## \ud83e\uddea  Quick Test Form during building\n\n\nThis form allowed me to quickly check this flow without having to execute another workflow.\n\nOf course you could also pin data, but given the fact i wanted to check different filetypes i opted for this temporary form, you can populate the base64 field with base64 encoded string of testfile (using for example `base64 -i /path/to/file | pbcopy`)\n\n\u26a0\ufe0f Remove before putting flow live."
      },
      "typeVersion": 1
    },
    {
      "id": "c95e5ad4-0bad-4670-814b-198f51ecb1bb",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1800,
        540
      ],
      "parameters": {
        "color": 3,
        "width": 340,
        "height": 280,
        "content": "## \u2696\ufe0f Pro's and Con's of base64 encoding here\n\nI used the encoding step to be able to define input schema in this node below, it comes with a trade off though: file size of base 64 encoded files are 33% larger, also execution data is becoming quite large. \n\n\u26a0\ufe0fCarefully consider if this is usable if you are working with large files. "
      },
      "typeVersion": 1
    },
    {
      "id": "36278709-336e-43a7-8f66-6f7b29cdca74",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1020,
        180
      ],
      "parameters": {
        "color": 5,
        "width": 460,
        "height": 240,
        "content": "## \ud83d\udd27Highlevel dataprep explanation\n\n\n**Prepare Upload Data:** maps inputs to buckets based on mimetype - passes through all other inputs.\n\n**Convert to file:** converts base64 encoded files back to actual files. (see red sticky node at start)"
      },
      "typeVersion": 1
    },
    {
      "id": "0a2490ad-78ce-4252-884d-900e9848d865",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -540,
        180
      ],
      "parameters": {
        "color": 6,
        "width": 640,
        "height": 240,
        "content": "## \ud83d\udd11 About Supabase Storage\n\nSupabase Storage uses HTTP API with Bearer YOUR_TOKEN_HERE authentication, not traditional AWS S3 SDK protocols.\n\nIn this set up we use an anon key/service key. We dont want to use that key all the time, so we generate a signed url to the object.\n\n\u26a0\ufe0f Replace the url in these nodes with your own."
      },
      "typeVersion": 1
    },
    {
      "id": "309c7e12-bcfa-4756-85e8-2d54639296a6",
      "name": "Upload to Supabase Storage",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        -460,
        440
      ],
      "parameters": {
        "url": "=https://api-sb.janwillemaltink.com/storage/v1/object/{{ $('Prepare Upload Data').item.json.bucket_name }}/{{ $('Prepare Upload Data').item.json.original_filename }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "binaryData",
        "authentication": "predefinedCredentialType",
        "inputDataFieldName": "data",
        "nodeCredentialType": "supabaseApi"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "ad17d932-adc3-4a93-8e02-1f52fe8a8196",
      "name": "Generate Signed Url",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        -220,
        440
      ],
      "parameters": {
        "url": "=https://api-sb.janwillemaltink.com/storage/v1/object/sign/{{ $json.Key }}",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n\t\"expiresIn\": 2592000\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "supabaseApi"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "9be14ebe-84f1-4d91-8c54-392fabf89c05",
      "name": "Upload Error Response",
      "type": "n8n-nodes-base.set",
      "position": [
        -260,
        620
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "action-type-error",
              "name": "action",
              "type": "string",
              "value": "supabase_upload"
            },
            {
              "id": "error-status",
              "name": "status",
              "type": "string",
              "value": "error"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "8dbc6a1e-1a6b-4cc1-ac9f-51fe38c386b8",
      "name": "add domain",
      "type": "n8n-nodes-base.set",
      "position": [
        -20,
        440
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d346fd86-3905-4f89-8754-6966c9725a77",
              "name": "full_signedURL",
              "type": "string",
              "value": "=https://api-sb.janwillemaltink.com/storage/v1{{ $json.signedURL }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e21970f6-23d8-4e23-b42b-f5636dd52358",
      "name": "Sign Error Response",
      "type": "n8n-nodes-base.set",
      "position": [
        -20,
        620
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "action-type-error",
              "name": "action",
              "type": "string",
              "value": "supabase_sign"
            },
            {
              "id": "error-status",
              "name": "status",
              "type": "string",
              "value": "error"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    }
  ],
  "connections": {
    "add domain": {
      "main": [
        [
          {
            "node": "Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to File": {
      "main": [
        [
          {
            "node": "Upload to Supabase Storage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Success Response": {
      "main": [
        []
      ]
    },
    "Generate Signed Url": {
      "main": [
        [
          {
            "node": "add domain",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sign Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Upload Data": {
      "main": [
        [
          {
            "node": "Convert to File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Supabase Storage": {
      "main": [
        [
          {
            "node": "Generate Signed Url",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Upload Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "temp form to test workflow": {
      "main": [
        [
          {
            "node": "Prepare Upload Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Executed by Another Workflow": {
      "main": [
        [
          {
            "node": "Prepare Upload Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

works with selfhosted Supabase

Source: https://n8n.io/workflows/4920/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

The AI-Powered Shopify SEO Content Automation is an enterprise-grade workflow that transforms product content creation for e-commerce stores. This sophisticated multi-agent system integrates GPT-4o, C

Perplexity Tool, Memory Buffer Window, Agent +15
AI & RAG

This template attempts to replicate OpenAI's DeepResearch feature which, at time of writing, is only available to their pro subscribers.

Output Parser Structured, OpenAI Chat, Form Trigger +8
AI & RAG

How it Works

Memory Buffer Window, Agent, Output Parser Structured +9
AI & RAG

Deep Research new (fr). Uses outputParserStructured, formTrigger, chainLlm, form. Event-driven trigger; 82 nodes.

Output Parser Structured, Form Trigger, Chain Llm +8
AI & RAG

Lection 9 main. Uses formTrigger, chatTrigger, agent, lmChatOpenAi. Event-driven trigger; 55 nodes.

Form Trigger, Chat Trigger, Agent +7