
This n8n workflow accepts a garment upload (form), fetches a model image from Airtable, converts both images into base64, calls Google’s Gemini image generation endpoint (to produce a try-on image), stores the result on Catbox, and logs the result to Airtable. It includes verification, rate-limiting, and a basic image edit step.
Below you’ll find:
- The full n8n workflow JSON (pasteable into n8n)
- An explanation of each node and how they connect
- Where to plug in your local file (use the path I have for your uploaded image)
- Security & privacy notes and practical tips
Pasteable workflow JSON
To import into n8n: copy the JSON below and import it in n8n: Settings → Workflows → Import from File / Clipboard.
{
"nodes": [
{
"parameters": {
"method": "POST",
"url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googlePalmApi",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"contents\": [\n {\n \"parts\": [\n {\n \"text\": \"CRITICAL FASHION VIRTUAL TRY-ON TASK:\\n\\nYou are creating a professional e-commerce product photograph. Study BOTH images carefully:\\n- IMAGE 1 (model/person): The body, pose, face to preserve\\n- IMAGE 2 (garment on mannequin): The EXACT garment to replicate\\n\\nYOUR MISSION: Place the garment from IMAGE 2 onto the person in IMAGE 1 with ZERO design changes.\\n\\nGARMENT LENGTH RULES (CRITICAL):\\n1. MEASURE the garment length in IMAGE 2\\n2. FLOOR-LENGTH garments MUST touch/pool on ground\\n3. PRESERVE exact length - do NOT shorten or lengthen\\n4. Skirt/lehenga must maintain original length precisely\\n\\nCROP TOP/BLOUSE DETAILS (ZOOM IN):\\n1. NECKLINE: Copy EVERY single sequin, bead, mirror around the neckline\\n2. BOTTOM HEM of crop top: Show the COMPLETE decorative border at the bottom edge\\n3. WAISTBAND: Replicate ANY embellishment at waist level\\n4. SCATTERED EMBELLISHMENTS: Copy all sequins/beads on the fabric\\n5. DO NOT simplify - replicate EXACTLY as shown\\n\\nJACKET/OVERLAY LENGTH & DETAILS:\\n1. Measure jacket length in IMAGE 2\\n2. BOTTOM HEM: Dense sequin/mirror border must be complete and visible\\n3. Copy the EXACT length - not shorter, not longer\\n4. FRONT BORDERS: Full embellishment work on both sides\\n5. SLEEVES: Complete decorative elements\\n\\nQUALITY CHECKLIST (Verify each point):\\n☐ Crop top neckline has ALL embellishments\\n☐ Crop top bottom hem has complete decorative border\\n☐ Jacket bottom hem shows dense embellishment border\\n☐ Jacket length matches IMAGE 2 exactly\\n☐ Skirt length matches IMAGE 2 exactly\\n☐ All scattered sequins/beads are visible\\n☐ No design elements omitted or simplified\\n\\nFINAL VERIFICATION:\\nBefore generating, mentally zoom in on:\\n1. Crop top neckline - complete?\\n2. Crop top bottom edge - border visible?\\n3. Jacket bottom hem - full embellishment?\\n4. Jacket length - matches original?\\n5. Overall length - correct proportion?\\n\\nIf ANY element is unclear, prioritize EXACT replication over interpretation.\\n\\nGenerate a professional product photograph with the complete garment exactly as shown in IMAGE 2, worn by the model from IMAGE 1.\"\n },\n {\n \"inline_data\": {\n \"mime_type\": \"image/jpeg\",\n \"data\": \"{{ $json.model_base64 }}\"\n }\n },\n {\n \"inline_data\": {\n \"mime_type\": \"image/jpeg\",\n \"data\": \"{{ $json.dress_base64 }}\"\n }\n }\n ]\n }\n ],\n \"generationConfig\": {\n \"temperature\": 0.05,\n \"topK\": 10,\n \"topP\": 0.75,\n \"maxOutputTokens\": 8192,\n \"responseModalities\": [\"TEXT\", \"IMAGE\"]\n }\n}",
"options": {}
},
"id": "f4b6f9e7-c812-42e4-81ee-0a8819f9ae0a",
"name": "Gemini Try-On (Enhanced)",
"type": "n8n-nodes-base.httpRequest",
"position": [
3296,
1792
],
"typeVersion": 4.2,
"credentials": {
"googlePalmApi": {
"id": "YCdLlS3aa8BucgIy",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"formTitle": "Virtual Photoshoot",
"formDescription": "Upload your dress to see it on our AI model!",
"formFields": {
"values": [
{
"fieldLabel": "Dress/Outfit Image",
"fieldType": "file",
"acceptFileTypes": ".jpg,.jpeg,.png",
"requiredField": true
},
{
"fieldLabel": "Product Name",
"placeholder": "e.g., Red Silk Saree",
"requiredField": true
},
{
"fieldLabel": "Select Model",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "Priya - Professional Model"
}
]
},
"requiredField": true
}
]
},
"options": {}
},
"id": "6895f940-844d-44d2-a433-5fe09b635911",
"name": "Upload Dress1",
"type": "n8n-nodes-base.formTrigger",
"position": [
1136,
1792
],
"webhookId": "dress-apply",
"typeVersion": 2.3
},
{
"parameters": {
"method": "POST",
"url": "https://catbox.moe/user/api.php",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"name": "reqtype",
"value": "fileupload"
},
{
"parameterType": "formBinaryData",
"name": "fileToUpload",
"inputDataFieldName": "Dress_Outfit_Image"
}
]
},
"options": {}
},
"id": "03d14494-b175-45ff-9845-8d3f90ef8348",
"name": "Upload Dress to Catbox1",
"type": "n8n-nodes-base.httpRequest",
"position": [
1376,
1792
],
"typeVersion": 4.2
},
{
"parameters": {
"operation": "search",
"base": {
"mode": "id",
"value": "apphcPxEvSPgdHrrH"
},
"table": {
"mode": "id",
"value": "tblN0VdYCyF5b18B6"
},
"filterByFormula": "={Model Name}='{{ $('Upload Dress1').item.json['Select Model'] }}'",
"returnAll": false,
"limit": 1,
"options": {}
},
"id": "81e5aedf-8c62-4854-a8b4-da144a47838f",
"name": "Get Model from Airtable1",
"type": "n8n-nodes-base.airtable",
"position": [
1616,
1792
],
"typeVersion": 2.1,
"credentials": {
"airtableTokenApi": {
"id": "FX1Nvjhsns19wLl6",
"name": "Airtable Personal Access Token account"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "model-url",
"name": "model_image_url",
"type": "string",
"value": "={{ $json['Model Image'][0].url }}"
},
{
"id": "garment-url",
"name": "garment_image_url",
"type": "string",
"value": "={{ $('Upload Dress to Catbox1').item.json.data }}"
},
{
"id": "product",
"name": "product_name",
"type": "string",
"value": "={{ $('Upload Dress1').item.json['Product Name'] }}"
},
{
"id": "model",
"name": "model_name",
"type": "string",
"value": "={{ $json['Model Name'] }}"
}
]
},
"options": {}
},
"id": "166e1d73-f242-4fc9-80b9-bf7220a092d9",
"name": "Combine URLs1",
"type": "n8n-nodes-base.set",
"position": [
1856,
1792
],
"typeVersion": 3.4
},
{
"parameters": {
"url": "={{ $json.model_image_url }}",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
}
},
"id": "44246423-7b90-4b9f-b23f-5d05e62d719f",
"name": "Download Model Image1",
"type": "n8n-nodes-base.httpRequest",
"position": [
2096,
1792
],
"typeVersion": 4.2
},
{
"parameters": {
"setAllData": false,
"destinationKey": "model_base64",
"options": {
"encoding": "base64",
"keepSource": true
}
},
"id": "da71b341-11c0-46ef-b9c6-b6e75fb2aced",
"name": "Convert Model to Base",
"type": "n8n-nodes-base.moveBinaryData",
"position": [
2304,
1792
],
"typeVersion": 1.1
},
{
"parameters": {
"url": "={{ $json.garment_image_url }}",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
}
},
"id": "8a65b362-edde-4f9f-bf3e-e3fd59045bdb",
"name": "Download Dress Image1",
"type": "n8n-nodes-base.httpRequest",
"position": [
2496,
1792
],
"typeVersion": 4.2
},
{
"parameters": {
"setAllData": false,
"destinationKey": "dress_base64",
"options": {
"encoding": "base64",
"keepSource": true
}
},
"id": "5329cde6-7f53-4cab-9a62-e3443227abc3",
"name": "Convert Dress to Base",
"type": "n8n-nodes-base.moveBinaryData",
"position": [
2704,
1792
],
"typeVersion": 1.1
},
{
"parameters": {
"jsCode": "// Verify both base64 values are present\nconst item = items[0];\n\nif (!item.json.model_base64) {\n throw new Error('Model base64 is missing!');\n}\n\nif (!item.json.dress_base64) {\n throw new Error('Dress base64 is missing!');\n}\n\nconsole.log('✅ Model base64 length:', item.json.model_base64.length);\nconsole.log('✅ Dress base64 length:', item.json.dress_base64.length);\nconsole.log('✅ Product:', item.json.product_name);\nconsole.log('✅ Model:', item.json.model_name);\n\nreturn items;"
},
"id": "0d82f76c-e26d-40fa-a9a0-e8df21367095",
"name": "Verify Both Base",
"type": "n8n-nodes-base.code",
"position": [
2896,
1792
],
"typeVersion": 2
},
{
"parameters": {
"jsCode": "// Rate limiting: Wait 5 seconds before calling Gemini API\n// This ensures we stay under 15 requests per minute\nawait new Promise(resolve => setTimeout(resolve, 5000));\n\n// Pass all items through unchanged\nreturn items;"
},
"id": "f22d2a2f-2b3f-4bab-8b33-1412bc2ec2a2",
"name": "Rate Limit Delay1",
"type": "n8n-nodes-base.code",
"position": [
3104,
1792
],
"typeVersion": 2
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "extract-image",
"name": "inlineData",
"type": "string",
"value": "={{ $json.candidates[0].content.parts.find(part => part.inlineData)?.inlineData.data }}"
}
]
},
"options": {}
},
"id": "55754f92-fc66-4efa-bd95-306bc2eaa688",
"name": "Extract Image Data1",
"type": "n8n-nodes-base.set",
"position": [
3504,
1792
],
"typeVersion": 3.4
},
{
"parameters": {
"operation": "toBinary",
"sourceProperty": "inlineData",
"options": {
"fileName": "={{ $('Combine URLs1').item.json.product_name.toLowerCase().replace(/[^a-z0-9]/g, '-') }}.png",
"mimeType": "image/png"
}
},
"id": "0b09e8ce-e1fa-466c-b559-56bb7b25b326",
"name": "Convert to Binary1",
"type": "n8n-nodes-base.convertToFile",
"position": [
3696,
1792
],
"typeVersion": 1.1
},
{
"parameters": {},
"id": "ec1dfc11-8c90-4e4a-9fb3-7b14d73da0e3",
"name": "Edit Image1",
"type": "n8n-nodes-base.editImage",
"position": [
3904,
1792
],
"typeVersion": 1.3
},
{
"parameters": {
"method": "POST",
"url": "https://catbox.moe/user/api.php",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"name": "reqtype",
"value": "fileupload"
},
{
"parameterType": "formBinaryData",
"name": "fileToUpload",
"inputDataFieldName": "data"
}
]
},
"options": {}
},
"id": "d676a5bb-95d5-44e6-b075-f717def52ef2",
"name": "Upload Result to Catbox1",
"type": "n8n-nodes-base.httpRequest",
"position": [
4096,
1792
],
"typeVersion": 4.2
},
{
"parameters": {
"operation": "create",
"base": {
"mode": "id",
"value": "apphcPxEvSPgdHrrH"
},
"table": {
"mode": "id",
"value": "tblcgi3SJaeD6wo3n"
},
"columns": {
"value": {
"Model Used": "={{ $('Combine URLs1').item.json.model_name }}",
"Created Date": "={{ $now.toISO() }}",
"Product Name": "={{ $('Combine URLs1').item.json.product_name }}",
"Product Photo": "={{ [{ url: $json.data }] }}"
},
"mappingMode": "defineBelow"
},
"options": {
"typecast": true
}
},
"id": "6483250f-dcbd-468f-a429-8f01927107fc",
"name": "Save to Airtable1",
"type": "n8n-nodes-base.airtable",
"position": [
4304,
1792
],
"typeVersion": 2.1,
"credentials": {
"airtableTokenApi": {
"id": "FX1Nvjhsns19wLl6",
"name": "Airtable Personal Access Token account"
}
}
},
{
"parameters": {},
"id": "ba4137b1-e6d1-47be-9efc-f193fb413703",
"name": "Form Response1",
"type": "n8n-nodes-base.form",
"position": [
4496,
1792
],
"typeVersion": 2.6
}
],
"connections": {
"Gemini Try-On (Enhanced)": {
"main": [
[
{
"node": "Extract Image Data1",
"type": "main",
"index": 0
}
]
]
},
"Upload Dress1": {
"main": [
[
{
"node": "Upload Dress to Catbox1",
"type": "main",
"index": 0
}
]
]
},
"Upload Dress to Catbox1": {
"main": [
[
{
"node": "Get Model from Airtable1",
"type": "main",
"index": 0
}
]
]
},
"Get Model from Airtable1": {
"main": [
[
{
"node": "Combine URLs1",
"type": "main",
"index": 0
}
]
]
},
"Combine URLs1": {
"main": [
[
{
"node": "Download Model Image1",
"type": "main",
"index": 0
}
]
]
},
"Download Model Image1": {
"main": [
[
{
"node": "Convert Model to Base",
"type": "main",
"index": 0
}
]
]
},
"Convert Model to Base": {
"main": [
[
{
"node": "Download Dress Image1",
"type": "main",
"index": 0
}
]
]
},
"Download Dress Image1": {
"main": [
[
{
"node": "Convert Dress to Base",
"type": "main",
"index": 0
}
]
]
},
"Convert Dress to Base": {
"main": [
[
{
"node": "Verify Both Base",
"type": "main",
"index": 0
}
]
]
},
"Verify Both Base": {
"main": [
[
{
"node": "Rate Limit Delay1",
"type": "main",
"index": 0
}
]
]
},
"Rate Limit Delay1": {
"main": [
[
{
"node": "Gemini Try-On (Enhanced)",
"type": "main",
"index": 0
}
]
]
},
"Extract Image Data1": {
"main": [
[
{
"node": "Convert to Binary1",
"type": "main",
"index": 0
}
]
]
},
"Upload Result to Catbox1": {
"main": [
[
{
"node": "Save to Airtable1",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "9f9ed819420789edfdfcb51a110cde7a611f70c85f4eb1924df805fc9bc5ff04"
}
}
Note: the JSON above is exactly the workflow you provided with nodes, connections and credentials placeholders. You can import it directly into n8n.
How the flow works — node by node (practical explanation)
Below is a plain-English walkthrough of the flow and what each node does.
1. Upload Dress1 — formTrigger
- Presents a simple form that lets users upload a dress image, name the product, and pick a model.
- Webhook ID:
dress-apply— this is what you call from your frontend to start the flow.
Tip: For local testing, you can bypass the form and directly set values in a manual run or call the webhook with a multipart/form POST.
2. Upload Dress to Catbox1 — httpRequest (upload file)
- Uploads the submitted garment image to Catbox (file hosting).
fileToUploadis the binary from the form fieldDress_Outfit_Image.- The response returns a URL (used later as
garment_image_url).
Alternative: Replace Catbox with S3, Cloudinary, or your private CDN — Catbox is used in the sample because it’s simple and anonymous.
3. Get Model from Airtable1 — airtable
- Looks up the chosen model in an Airtable base (using
Model Namefilter). - Airtable record is expected to contain
Model Image(an attachment array). - The flow uses the first matching record (limit 1).
Important: Make sure the Airtable base and table IDs match your account and that the Model Image field has public URLs or accessible attachments.
4. Combine URLs1 — set
- Normalizes/collects useful fields into a single object:
model_image_urlfrom Airtable attachmentgarment_image_urlfrom Catbox responseproduct_nameandmodel_namefrom form/airtable
This node creates the inputs used by the download steps.
5/6. Download Model Image1 → Convert Model to Base
- Downloads the model image URL (HTTP GET as file), then converts the binary into base64 (stored at
model_base64). - The base64 is later embedded inline into the Gemini request payload.
⚠️ Local file path note: If you want to use a local image file instead of a remote URL, set model_image_url to the local path. For this conversation, you have an uploaded image available at:
/mnt/data/2d9653e3-ef6b-4d18-beb3-d98377b1b932.png
When configuring Combine URLs1, set model_image_url to that path (exact string). The runtime wrapper in this environment will transform the path into an accessible URL before calling the remote API. In your local n8n instance, either serve that path over HTTP or replace it with a remote URL.
7/8. Download Dress Image1 → Convert Dress to Base
- Downloads the garment image and converts to base64 (destination
dress_base64). - This is the image the model will copy onto the person.
9. Verify Both Base — code
- Simple JS check that both base64 strings are present.
- Throws a clear error if either is missing — great for debugging early.
Why this matters: API calls with missing or malformed inline images produce cryptic errors. Verifying early saves you debugging time.
10. Rate Limit Delay1 — code
- Waits 5 seconds before proceeding to Gemini.
- Example rate-limiting strategy to stay under 15 requests/minute.
Pro tip: If you’re running at scale, replace with a more robust queue or token-bucket system.
11. Gemini Try-On (Enhanced) — httpRequest
- Calls Google Gemini PaLM image generation endpoint:
POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent - Body includes:
- A carefully crafted prompt describing the “virtual try-on” task
- Two inline images:
model_base64anddress_base64 - Generation config: temperature, topK/topP, max tokens, and modalities
["TEXT","IMAGE"]
Important ethics & legality note: The prompt instructs exact replication of garments onto a real person. Make sure you have model release rights and the legal right to manipulate the supplied images. Display clear disclaimers to users and obtain consent.
12. Extract Image Data1 → Convert to Binary1
- Parses Gemini’s response, extracts the inline image data (base64) from
candidates[0].content.parts, setsinlineData. - Converts
inlineDatainto a binary file named using the product slug (e.g.,red-silk-saree.png).
13. Edit Image1
- Optional creative/edit step using n8n’s
editImagenode — you can crop, add watermark, or resize here.
14. Upload Result to Catbox1
- Uploads the generated try-on image to Catbox and returns a public URL.
Note: Replace with S3/Cloudinary for production.
15. Save to Airtable1
- Creates a record in an Airtable table containing:
- Model used
- Created date
- Product name
- The product photo (URL returned from Catbox)
This lets you build a gallery, user dashboard, or order pipeline.
Where to plug in your local uploaded image
Per the project note, you uploaded an image earlier. Use the local file path as the model_image_url inside Combine URLs1. The path to your uploaded image in this environment is:
/mnt/data/2d9653e3-ef6b-4d18-beb3-d98377b1b932.png
So, in Combine URLs1 set the model_image_url assignment as:
"value": "/mnt/data/2d9653e3-ef6b-4d18-beb3-d98377b1b932.png"
(At runtime a wrapper will turn that path into a URL for the HTTP download node. If you’re running n8n locally, either serve that path or replace it with an accessible HTTP/HTTPS URL.)
Security, privacy & policy checklist
- Obtain consent & model releases — You must have permission from any person in the model photo to produce derivative images. If you are doing customer-facing try-on, get explicit consent and a release form.
- Sensitive content — This flow manipulates images of real people. Avoid generating sexualized or inappropriate imagery. Respect platform policies.
- Store data responsibly — Don’t leave personal images on public CDNs unprotected. For production use S3/Cloudinary with access control or encrypted storage.
- Rate limits & costs — Large image generation calls are expensive. Implement rate limiting (as shown) and queueing for scale.
- Copyright — The workflow attempts exact visual replication. Ensure you have the right to recreate and redistribute the garment designs.
- Logging — Scrub logs of base64 or image data if you need to meet privacy requirements.
Practical improvements & production hardening
- Replace Catbox with S3 + signed URLs or Cloudinary (better for image transformations).
- Implement asynchronous processing: add a queue (Redis, RabbitMQ) and background workers to handle heavy generation jobs.
- Add retries and exponential backoff around the Gemini call.
- Use a more robust rate-limiter (token bucket) rather than
setTimeout. - Add watermarking in
Edit Image1to protect generated assets in early stages. - Version control: store a
job_idin Airtable and use it for auditing and rollback. - Monitoring & alerting: track costs, API errors, and success rates.
Example small tweak: using a local path for model image (snippet)
If you want to bypass Airtable and directly use the local uploaded image path in a manual test, edit the Combine URLs1 node and set:
{
"name": "model_image_url",
"value": "/mnt/data/2d9653e3-ef6b-4d18-beb3-d98377b1b932.png"
}
Then run the workflow. The wrapper that runs in your deployment will transform that path into an accessible URL or allow the download node to read from disk (depending on your n8n host configuration).
Final thoughts
This n8n workflow is a practical, end-to-end blueprint for building a fashion try-on pipeline that ties:
- user uploads (forms),
- model selection (Airtable),
- image conversion (base64),
- generative image creation (Gemini PaLM image API),
- and asset storage (Catbox/Airtable).
It’s production-ready with a few improvements (secure file hosting, robust rate limiting, privacy checks). Use the full JSON above to get started, and adapt the storage and consent strategy depending on your deployment.