This workflow corresponds to n8n.io template #18131 — we link there as the canonical source.
This workflow follows the Airtable → 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 →
{
"id": "AyE4WBqHjRwC5Lqp",
"name": "Deliver photography galleries only after payment clears",
"tags": [],
"nodes": [
{
"id": "9ec8bbb8-8929-41bf-b4b2-a357b92e0f5c",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-32,
-16
],
"parameters": {
"width": 480,
"height": 992,
"content": "## Deliver photography galleries only after payment clears\n\n### How it works\n\nThis workflow lets clients submit photography proof selections, calculates the order total, logs the order in Airtable, and emails a Razorpay payment link. When Razorpay sends a payment webhook, it verifies the signature, marks the matching order as paid, and delivers the gallery immediately if it is ready. A scheduled daily sweep also finds orders that are both paid and ready, then sends any galleries that were not delivered at payment time.\n\n### Setup steps\n\n- Configure the proof selection form fields so they capture the client details, selected images or products, email address, and any data needed for pricing.\n- Set up Airtable credentials and confirm the configured base ID and table name match the order-tracking table used by all Airtable nodes.\n- Add Razorpay API credentials for creating payment links, and set the Razorpay webhook secret used to verify payment callbacks.\n- Configure the Razorpay webhook URL to point to the Payment Webhook node and subscribe it to the relevant payment-success event.\n- Connect Gmail credentials and review the payment-request and gallery-delivery email templates.\n- Confirm the Airtable table has fields for payment status, gallery readiness, delivery status, gallery link, order total, and any Razorpay identifiers used for matching payments.\n- Set the Daily Delivery Sweep schedule to the studio\u2019s preferred delivery check frequency.\n\n### Customization\n\nAdjust the pricing logic in Calculate Order Total, the Airtable schema/filters for paid and ready orders, the email copy, and the daily sweep timing to match the studio\u2019s delivery process."
},
"typeVersion": 1
},
{
"id": "a4c1ef67-2e5b-4334-a188-cbc211e3591e",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
0
],
"parameters": {
"color": 7,
"width": 672,
"height": 304,
"content": "## Proof selection intake\n\nStarts the client proof-selection flow, loads studio/payment configuration, and calculates the total amount due from the submitted selections."
},
"typeVersion": 1
},
{
"id": "07b98820-7522-4ab3-84e5-c59e7fc16d60",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1280,
0
],
"parameters": {
"color": 7,
"width": 672,
"height": 304,
"content": "## Create payment request\n\nRecords the new order in Airtable, creates a Razorpay payment link, and emails the client with payment instructions."
},
"typeVersion": 1
},
{
"id": "34d79851-73dd-4fcf-9739-728ce32633a3",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
432
],
"parameters": {
"color": 7,
"width": 912,
"height": 304,
"content": "## Verify payment webhook\n\nReceives Razorpay payment notifications, loads webhook configuration, validates the webhook signature, and only continues when the callback is trusted."
},
"typeVersion": 1
},
{
"id": "b4426906-3a13-4b00-b4ec-164db49b0a8d",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1536,
384
],
"parameters": {
"color": 7,
"width": 432,
"height": 336,
"content": "## Mark order paid\n\nFinds the Airtable order that matches the verified Razorpay payment and updates the order record to reflect cleared payment."
},
"typeVersion": 1
},
{
"id": "114aa841-69c0-44b1-823e-a9188b1d2a40",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2000,
320
],
"parameters": {
"color": 7,
"width": 688,
"height": 320,
"content": "## Immediate gallery delivery\n\nChecks whether the paid order\u2019s gallery is ready, sends the gallery email immediately when possible, and marks the order as delivered in Airtable."
},
"typeVersion": 1
},
{
"id": "c5c58418-6fed-4af2-9b09-9a82dd55635a",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
768
],
"parameters": {
"color": 7,
"width": 432,
"height": 352,
"content": "## Daily sweep setup\n\nRuns on a schedule and loads the Airtable configuration needed to find delayed gallery deliveries."
},
"typeVersion": 1
},
{
"id": "d40341f3-bfb9-4d3c-ae5d-9ca3d002e1d1",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1040,
800
],
"parameters": {
"color": 7,
"width": 672,
"height": 304,
"content": "## Sweep gallery delivery\n\nFetches orders that are paid and now gallery-ready, emails the gallery links, and marks those records as delivered."
},
"typeVersion": 1
},
{
"id": "5637192f-9f94-42b0-b273-d53a44726834",
"name": "When New Proof Selected",
"type": "n8n-nodes-base.formTrigger",
"position": [
608,
128
],
"parameters": {
"options": {},
"formTitle": "Proof Selection",
"formFields": {
"values": [
{
"fieldLabel": "Client Name",
"requiredField": true
},
{
"fieldType": "email",
"fieldLabel": "Client Email",
"requiredField": true
},
{
"fieldType": "dropdown",
"fieldLabel": "Package",
"fieldOptions": {
"values": [
{
"option": "Basic"
},
{
"option": "Standard"
},
{
"option": "Premium"
}
]
},
"requiredField": true
},
{
"fieldLabel": "Selected Proof IDs",
"placeholder": "e.g. IMG_014, IMG_027, IMG_101",
"requiredField": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a2366de8-b0e9-4d10-978d-ae64e518a8cb",
"name": "Set Initial Configurations",
"type": "n8n-nodes-base.set",
"position": [
848,
128
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfgA-1",
"name": "studio_name",
"type": "string",
"value": "REPLACE_WITH_STUDIO_NAME"
},
{
"id": "cfgA-2",
"name": "airtable_base_id",
"type": "string",
"value": "REPLACE_WITH_AIRTABLE_BASE_ID"
},
{
"id": "cfgA-3",
"name": "airtable_table_name",
"type": "string",
"value": "Orders"
},
{
"id": "cfgA-4",
"name": "razorpay_key_id",
"type": "string",
"value": "REPLACE_WITH_RAZORPAY_KEY_ID"
},
{
"id": "cfgA-5",
"name": "razorpay_key_secret",
"type": "string",
"value": "REPLACE_WITH_RAZORPAY_KEY_SECRET"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "2e08e21b-daca-4a16-9c05-5ea0b5ff12d9",
"name": "Compute Order Total",
"type": "n8n-nodes-base.code",
"position": [
1088,
128
],
"parameters": {
"jsCode": "const prices = {\n 'Basic': 5000,\n 'Standard': 9000,\n 'Premium': 15000\n};\n\nconst packageName = $json['Package'];\nconst totalAmount = prices[packageName] || 0;\nconst orderReference = 'ORD-' + Date.now();\n\nreturn [{\n json: {\n order_reference: orderReference,\n client_name: $json['Client Name'],\n client_email: $json['Client Email'],\n package: packageName,\n selected_proof_ids: $json['Selected Proof IDs'],\n total_amount: totalAmount\n }\n}];"
},
"typeVersion": 2
},
{
"id": "56a540fd-5981-43a7-803b-be49ffefea67",
"name": "Record Order in Airtable",
"type": "n8n-nodes-base.airtable",
"position": [
1328,
128
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Initial Configurations').item.json.airtable_base_id }}"
},
"table": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Initial Configurations').item.json.airtable_table_name }}"
},
"columns": {
"value": {
"paid": false,
"package": "={{ $json.package }}",
"delivered": false,
"client_name": "={{ $json.client_name }}",
"client_email": "={{ $json.client_email }}",
"gallery_link": "",
"total_amount": "={{ $json.total_amount }}",
"order_reference": "={{ $json.order_reference }}",
"selected_proof_ids": "={{ $json.selected_proof_ids }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "create"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "c091c374-79a0-4bc6-8471-7aaa59d5116d",
"name": "Post Payment Link to Razorpay",
"type": "n8n-nodes-base.httpRequest",
"position": [
1568,
128
],
"parameters": {
"url": "https://api.razorpay.com/v1/payment_links",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ amount: $json.total_amount * 100, currency: 'INR', accept_partial: false, reference_id: $json.order_reference, customer: { name: $json.client_name, email: $json.client_email }, notify: { email: true } }) }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "e0a5820e-7716-4642-b70c-4d13045253fd",
"name": "Email Payment Request",
"type": "n8n-nodes-base.gmail",
"position": [
1808,
128
],
"parameters": {
"sendTo": "={{ $('Compute Order Total').item.json.client_email }}",
"message": "=Hi {{ $('Compute Order Total').item.json.client_name }},\n\nThanks for selecting your proofs! Your {{ $('Compute Order Total').item.json.package }} package total is \u20b9{{ $('Compute Order Total').item.json.total_amount }}.\n\nComplete payment here to start final editing: {{ $json.short_url }}\n\nYour gallery will be delivered as soon as payment clears and editing is finished.",
"options": {},
"subject": "=Your {{ $('Set Initial Configurations').item.json.studio_name }} gallery is ready to order"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "d623bbff-82f6-45e3-8333-5696601686fb",
"name": "On Payment Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
608,
560
],
"parameters": {
"path": "razorpay-payment-webhook",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 2
},
{
"id": "c88707b6-f9d8-4060-abe5-e30491e9e9f9",
"name": "Configure Webhook Secrets",
"type": "n8n-nodes-base.set",
"position": [
848,
560
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfgB-1",
"name": "studio_name",
"type": "string",
"value": "REPLACE_WITH_STUDIO_NAME"
},
{
"id": "cfgB-2",
"name": "airtable_base_id",
"type": "string",
"value": "REPLACE_WITH_AIRTABLE_BASE_ID"
},
{
"id": "cfgB-3",
"name": "airtable_table_name",
"type": "string",
"value": "Orders"
},
{
"id": "cfgB-4",
"name": "razorpay_webhook_secret",
"type": "string",
"value": "REPLACE_WITH_RAZORPAY_WEBHOOK_SECRET"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e29baa1e-7eec-42fe-aa56-3299136e042d",
"name": "Check Webhook Signature",
"type": "n8n-nodes-base.code",
"position": [
1088,
560
],
"parameters": {
"jsCode": "const crypto = require('crypto');\n\nconst secret = $('Configure Webhook Secrets').first().json.razorpay_webhook_secret;\nconst receivedSignature = $('On Payment Webhook').first().json.headers['x-razorpay-signature'];\nconst rawBody = JSON.stringify($('On Payment Webhook').first().json.body);\n\nconst expectedSignature = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');\nconst isValid = expectedSignature === receivedSignature;\n\nconst paymentEntity = $('On Payment Webhook').first().json.body.payload.payment.entity;\n\nreturn [{\n json: {\n is_valid_signature: isValid,\n order_reference: paymentEntity.notes && paymentEntity.notes.reference_id ? paymentEntity.notes.reference_id : paymentEntity.order_id,\n payment_status: paymentEntity.status\n }\n}];"
},
"typeVersion": 2
},
{
"id": "31ff7a77-18ef-42bc-9632-30ffb46922b3",
"name": "If Signature Valid",
"type": "n8n-nodes-base.if",
"position": [
1328,
560
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "2c33ceae-adfa-41bd-b1ee-113c88eac688",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.is_valid_signature }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "38990504-4041-4eef-8526-a3b3a116e679",
"name": "Lookup Matching Order",
"type": "n8n-nodes-base.airtable",
"position": [
1584,
544
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Webhook Secrets').item.json.airtable_base_id }}"
},
"table": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Webhook Secrets').item.json.airtable_table_name }}"
},
"options": {},
"operation": "search",
"filterByFormula": "={{ \"{order_reference} = '\" + $json.order_reference + \"'\" }}"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "66978582-7a1c-4d3c-a40c-f1da49d90bbd",
"name": "If Gallery Prepared",
"type": "n8n-nodes-base.if",
"position": [
2048,
464
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "bc511397-3490-4563-adb4-cf50063ee527",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json.gallery_link }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "39c8f198-afd3-4193-a052-98cbebcc77ad",
"name": "Deliver Gallery via Email",
"type": "n8n-nodes-base.gmail",
"position": [
2320,
448
],
"parameters": {
"sendTo": "={{ $json.client_email }}",
"message": "=Hi {{ $json.client_name }}, your payment is confirmed and your final gallery is ready: {{ $json.gallery_link }}",
"options": {},
"subject": "=Your gallery is ready!"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "3e832b62-3059-419b-b61c-71aca6153572",
"name": "Update Delivery Status",
"type": "n8n-nodes-base.airtable",
"position": [
2544,
448
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Webhook Secrets').item.json.airtable_base_id }}"
},
"table": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Webhook Secrets').item.json.airtable_table_name }}"
},
"columns": {
"value": {
"id": "={{ $json.id }}",
"delivered": true
},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "7757fc3b-8ea2-43e8-8abb-6883a70b00e0",
"name": "When Daily Sweep at 10am",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
608,
928
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 10
}
]
}
},
"typeVersion": 1.2
},
{
"id": "0b39cc2d-c181-46ab-ab2a-1e1135c435c0",
"name": "Setup Airtable Credentials",
"type": "n8n-nodes-base.set",
"position": [
848,
928
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfgC-1",
"name": "airtable_base_id",
"type": "string",
"value": "REPLACE_WITH_AIRTABLE_BASE_ID"
},
{
"id": "cfgC-2",
"name": "airtable_table_name",
"type": "string",
"value": "Orders"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "58289fa6-0e0f-4f14-b430-d385af3d6999",
"name": "Retrieve Paid Orders",
"type": "n8n-nodes-base.airtable",
"position": [
1088,
928
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $('Setup Airtable Credentials').item.json.airtable_base_id }}"
},
"table": {
"__rl": true,
"mode": "id",
"value": "={{ $('Setup Airtable Credentials').item.json.airtable_table_name }}"
},
"options": {},
"operation": "search",
"filterByFormula": "AND({paid} = TRUE(), {delivered} = FALSE(), {gallery_link} != '')"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "27e3fda0-1e92-4260-afea-eceeef0fa6ce",
"name": "Sweep Deliver via Email",
"type": "n8n-nodes-base.gmail",
"position": [
1328,
928
],
"parameters": {
"sendTo": "={{ $json.client_email }}",
"message": "=Hi {{ $json.client_name }}, your final gallery is ready: {{ $json.gallery_link }}",
"options": {},
"subject": "=Your gallery is ready!"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "978c9e47-cd5b-4d58-bd45-68edeaff0e48",
"name": "Sweep Update Delivered Status",
"type": "n8n-nodes-base.airtable",
"position": [
1568,
928
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $('Setup Airtable Credentials').item.json.airtable_base_id }}"
},
"table": {
"__rl": true,
"mode": "id",
"value": "={{ $('Setup Airtable Credentials').item.json.airtable_table_name }}"
},
"columns": {
"value": {
"id": "={{ $json.id }}",
"delivered": true
},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "214bc434-b1f3-4b1d-87d7-b405699d6f66",
"name": "Airtable Update Mark Paid",
"type": "n8n-nodes-base.airtable",
"position": [
1824,
544
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Webhook Secrets').item.json.airtable_base_id }}"
},
"table": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Webhook Secrets').item.json.airtable_table_name }}"
},
"columns": {
"value": {
"id": "={{ $json.id }}",
"paid": true
},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "17e24aa4-d9b0-4228-8b06-449df350d883",
"nodeGroups": [],
"connections": {
"If Signature Valid": {
"main": [
[
{
"node": "Lookup Matching Order",
"type": "main",
"index": 0
}
]
]
},
"On Payment Webhook": {
"main": [
[
{
"node": "Configure Webhook Secrets",
"type": "main",
"index": 0
}
]
]
},
"Compute Order Total": {
"main": [
[
{
"node": "Record Order in Airtable",
"type": "main",
"index": 0
}
]
]
},
"If Gallery Prepared": {
"main": [
[
{
"node": "Deliver Gallery via Email",
"type": "main",
"index": 0
}
]
]
},
"Retrieve Paid Orders": {
"main": [
[
{
"node": "Sweep Deliver via Email",
"type": "main",
"index": 0
}
]
]
},
"Lookup Matching Order": {
"main": [
[
{
"node": "Airtable Update Mark Paid",
"type": "main",
"index": 0
}
]
]
},
"Check Webhook Signature": {
"main": [
[
{
"node": "If Signature Valid",
"type": "main",
"index": 0
}
]
]
},
"Sweep Deliver via Email": {
"main": [
[
{
"node": "Sweep Update Delivered Status",
"type": "main",
"index": 0
}
]
]
},
"When New Proof Selected": {
"main": [
[
{
"node": "Set Initial Configurations",
"type": "main",
"index": 0
}
]
]
},
"Record Order in Airtable": {
"main": [
[
{
"node": "Post Payment Link to Razorpay",
"type": "main",
"index": 0
}
]
]
},
"When Daily Sweep at 10am": {
"main": [
[
{
"node": "Setup Airtable Credentials",
"type": "main",
"index": 0
}
]
]
},
"Airtable Update Mark Paid": {
"main": [
[
{
"node": "If Gallery Prepared",
"type": "main",
"index": 0
}
]
]
},
"Configure Webhook Secrets": {
"main": [
[
{
"node": "Check Webhook Signature",
"type": "main",
"index": 0
}
]
]
},
"Deliver Gallery via Email": {
"main": [
[
{
"node": "Update Delivery Status",
"type": "main",
"index": 0
}
]
]
},
"Set Initial Configurations": {
"main": [
[
{
"node": "Compute Order Total",
"type": "main",
"index": 0
}
]
]
},
"Setup Airtable Credentials": {
"main": [
[
{
"node": "Retrieve Paid Orders",
"type": "main",
"index": 0
}
]
]
},
"Post Payment Link to Razorpay": {
"main": [
[
{
"node": "Email Payment Request",
"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.
airtableTokenApigmailOAuth2
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow collects proof selections via an n8n Form, logs orders in Airtable, creates Razorpay payment links, and delivers a final gallery link by Gmail only after Razorpay confirms payment and the gallery link has been added. Receives a client’s proof selection submission…
Source: https://n8n.io/workflows/18131/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Echo Brand Voice Analysis (Processor) - TASK-074 Dec 10 Fix. Uses formTrigger, httpRequest, executeWorkflowTrigger, moveBinaryData. Event-driven trigger; 40 nodes.
Recruiting agency. Uses typeformTrigger, airtable, httpRequest, googleDrive. Event-driven trigger; 36 nodes.
Streamline your content pipeline by bridging Notion and Instagram with a professional "review-before-publish" safeguard. This workflow allows team members to submit content via a simple form, generate
🎥 Analyze YouTube Video for Summaries, Transcripts & Content + Google Gemini AI. Uses stickyNote, httpRequest, googleDrive, gmail. Event-driven trigger; 33 nodes.
This workflow audits a list of URLs from a CSV upload or an XML sitemap using the Google PageSpeed Insights API, writes Lighthouse and Core Web Vitals metrics into a new Google Sheets report, and emai