This workflow corresponds to n8n.io template #8950 — we link there as the canonical source.
This workflow follows the Airtable → Google Sheets 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": "2ko7gxYzw5Uy6lFh",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Airtable Order to Stripe Invoice (B2B/Manual Capture)",
"tags": [],
"nodes": [
{
"id": "7e7fd28b-de0f-4be1-806f-ff05fe0d383d",
"name": "Workflow Description",
"type": "n8n-nodes-base.stickyNote",
"position": [
-528,
80
],
"parameters": {
"width": 320,
"height": 708,
"content": "## \ud83d\ude80 B2B Invoice Automation Workflow\n\nThis workflow automatically creates Stripe invoices for B2B paid orders and logs them to Google Sheets.\n\n### What it does:\n- Triggers every hour to check for new B2B orders\n- Filters for paid B2B orders only\n- Creates Stripe customers and invoices\n- Logs invoice data to Google Sheets for tracking\n\n### Requirements:\n- Airtable with Orders table\n- Stripe account with API access\n- Google Sheets for logging\n\n### Setup Instructions:\n1. Configure your Airtable credentials\n2. Set up Stripe API credentials\n3. Connect Google Sheets with proper permissions\n4. Update the record ID in \"Fetch B2B Order\" node\n5. Update Google Sheets document ID\n"
},
"typeVersion": 1
},
{
"id": "ff6b94d0-1292-4413-a374-72c9304d2bb2",
"name": "Schedule Setup Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
-96,
-160
],
"parameters": {
"width": 280,
"height": 340,
"content": "## \u23f0 Schedule Setup\n\n**Purpose:** Triggers the workflow every hour to check for new orders\n\n**Configuration:**\n- Interval: 1 hour\n- Can be changed based on your needs\n\n**Tips:**\n- For testing, use manual trigger first\n- Consider timezone settings for your business hours"
},
"typeVersion": 1
},
{
"id": "9d95e100-66c2-4bad-b3a3-9aafcdc6ea52",
"name": "Hourly Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
80,
208
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "1e771a28-cf1d-4f20-bb4a-5f212bc5ef81",
"name": "Airtable Setup Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
208,
368
],
"parameters": {
"width": 300,
"height": 376,
"content": "## \ud83d\udccb Airtable Setup\n\n**Purpose:** Fetch order data from your Airtable\n\n**Required Steps:**\n1. Replace the hardcoded record ID with your actual record ID\n2. Update base ID and table ID to match your setup\n3. Ensure your Airtable has these fields:\n - Customer Name\n - Email\n - Phone Number\n - financial_status\n - tags\n\n**Security:** Never commit your actual record IDs to public repos"
},
"typeVersion": 1
},
{
"id": "fdd91819-6baa-4c0c-bf05-2ef2ae931625",
"name": "Fetch B2B Order",
"type": "n8n-nodes-base.airtable",
"position": [
288,
208
],
"parameters": {
"id": "rec5GbGenP8Wurf4W",
"base": {
"__rl": true,
"mode": "list",
"value": "appxjEpOgye5YQG1J",
"cachedResultUrl": "https://airtable.com/appxjEpOgye5YQG1J",
"cachedResultName": "Lead Manager"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tbl56YM9iZKaIhAB3",
"cachedResultUrl": "https://airtable.com/appxjEpOgye5YQG1J/tbl56YM9iZKaIhAB3",
"cachedResultName": "Orders"
},
"options": {}
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "87e76054-5d3e-4f98-b565-b2be586673ac",
"name": "Order Filter Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
416,
-176
],
"parameters": {
"width": 280,
"height": 336,
"content": "## \ud83d\udd0d Order Filtering\n\n**Purpose:** Only process B2B orders that are already paid\n\n**Filter Conditions:**\n- financial_status = \"paid\"\n- tags contains \"B2B\"\n- Both conditions must be true (AND logic)\n\n**Customization:**\n- Modify conditions based on your order structure\n- Add additional filters if needed"
},
"typeVersion": 1
},
{
"id": "72e1a601-e9c4-4e65-93db-f4dcb23d9530",
"name": "Filter B2B Paid Orders",
"type": "n8n-nodes-base.if",
"position": [
512,
208
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.financial_status }}",
"rightValue": "paid"
},
{
"id": "condition2",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.tags }}",
"rightValue": "B2B"
}
]
}
},
"typeVersion": 2
},
{
"id": "aa6e2bc9-1e47-4a4a-9236-cd2066c8b1ac",
"name": "Stripe Customer Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
656,
384
],
"parameters": {
"width": 300,
"height": 452,
"content": "## \ud83d\udc64 Stripe Customer Creation\n\n**Purpose:** Create or update customer in Stripe\n\n**Configuration:**\n- Maps customer data from Airtable\n- Uses continueOnFail to handle existing customers\n- Stores customer ID for invoice creation\n\n**Required Fields:**\n- Customer Name\n- Email\n- Phone Number (optional)\n\n**Note:** Stripe will return existing customer if email already exists"
},
"typeVersion": 1
},
{
"id": "162bda8f-8ab2-467b-a856-ee7796da8e81",
"name": "Create Stripe Customer",
"type": "n8n-nodes-base.stripe",
"position": [
736,
208
],
"parameters": {
"name": "={{ $json[\"Customer Name\"] }}",
"resource": "customer",
"operation": "create",
"additionalFields": {
"email": "={{ $json.Email }}",
"phone": "={{ $json[\"Phone Number\"] }}"
}
},
"credentials": {
"stripeApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"continueOnFail": true
},
{
"id": "bc462aa9-f034-4e7f-8a07-5668b462396a",
"name": "Line Items Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
880,
-208
],
"parameters": {
"width": 280,
"height": 384,
"content": "## \ud83d\udcc4 Line Item Processing\n\n**Purpose:** Split orders into individual line items for invoicing\n\n**What it does:**\n- Processes each product in the order separately\n- Formats data for Stripe invoice creation\n- Handles missing data gracefully\n- Creates test data if no order items found\n\n**Output:** One item per product for detailed invoicing"
},
"typeVersion": 1
},
{
"id": "8d23c8a5-9ca5-43ac-a69c-c0ae0e71d1f5",
"name": "Process Line Items",
"type": "n8n-nodes-base.code",
"position": [
960,
208
],
"parameters": {
"jsCode": "// Get the current input (Stripe customer)\nconst stripeCustomer = $input.item.json;\n\n// Try to get Shopify order from various sources\nlet shopifyOrder = null;\n\n// Method 1: Try from execution data\ntry {\n const executionData = $workflow.data;\n console.log('Execution Data:', JSON.stringify(executionData, null, 2));\n} catch (e) {\n console.log('Cannot access execution data');\n}\n\n// Method 2: Try from items\ntry {\n const items = $items();\n console.log('Items:', JSON.stringify(items, null, 2));\n \n // Look for Shopify order in items\n for (const item of items) {\n if (item.line_items) {\n shopifyOrder = item;\n break;\n }\n }\n} catch (e) {\n console.log('Cannot access items');\n}\n\n// Method 3: Create a test line item if no Shopify data found\nif (!shopifyOrder || !shopifyOrder.line_items || shopifyOrder.line_items.length === 0) {\n console.log('No Shopify order found, creating test data');\n \n // Return test data to verify the node works\n return [\n {\n stripe_customer: stripeCustomer,\n line_item: {\n id: 'test_item_1',\n title: 'Test Product',\n price: '99.99',\n quantity: 1\n },\n price: 99.99,\n quantity: 1,\n title: 'Test Product',\n customer_id: stripeCustomer.id,\n currency: 'USD'\n }\n ];\n}\n\n// If we have Shopify order, process it\nconst lineItems = shopifyOrder.line_items || [];\nconsole.log('Processing line items:', lineItems);\n\n// Return processed line items\nreturn lineItems.map((item, index) => ({\n shopify_order: shopifyOrder,\n stripe_customer: stripeCustomer,\n line_item: item,\n price: parseFloat(item.price || '0'),\n quantity: parseInt(item.quantity || '1'),\n title: item.title || `Item ${index + 1}`,\n customer_id: stripeCustomer.id,\n currency: shopifyOrder.currency || 'USD'\n}));"
},
"typeVersion": 2
},
{
"id": "188a61b7-5c0f-4bab-a2e9-8c15ebf18d7b",
"name": "Invoice Creation Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
1136,
384
],
"parameters": {
"width": 300,
"height": 372,
"content": "## \ud83e\uddfe Invoice Creation\n\n**Purpose:** Create draft invoice in Stripe\n\n**Configuration:**\n- Creates invoice for the customer\n- Sets 30-day payment terms\n- Uses send_invoice collection method\n- Includes all pending invoice items\n\n**Parameters:**\n- customer: Stripe customer ID\n- currency: Order currency\n- days_until_due: 30 days\n- auto_advance: false (manual finalization)"
},
"typeVersion": 1
},
{
"id": "8e7661ce-e5e0-4011-96ad-c63f561ddfee",
"name": "Create Stripe Invoice",
"type": "n8n-nodes-base.httpRequest",
"position": [
1184,
208
],
"parameters": {
"url": "https://api.stripe.com/v1/invoices",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "customer",
"value": "={{ $json.customer_id }}"
},
{
"name": "currency",
"value": "={{ $json.currency }}"
},
{
"name": "auto_advance",
"value": "false"
},
{
"name": "collection_method",
"value": "send_invoice"
},
{
"name": "days_until_due",
"value": "30"
},
{
"name": "pending_invoice_items_behavior",
"value": "include"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
]
},
"nodeCredentialType": "stripeApi"
},
"credentials": {
"stripeApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.1
},
{
"id": "4f617671-ca7c-4da7-8d32-f15e5845431a",
"name": "Finalization Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
1312,
-192
],
"parameters": {
"width": 280,
"height": 384,
"content": "## \u2705 Invoice Finalization\n\n**Purpose:** Finalize the draft invoice to make it payable\n\n**What happens:**\n- Converts draft to final invoice\n- Generates invoice number\n- Makes invoice payable by customer\n- Triggers email sending (if configured)\n\n**Parameters:**\n- auto_advance: true (automatically send)\n- Invoice ID from previous step"
},
"typeVersion": 1
},
{
"id": "fdf51683-a973-4a01-9872-3df221a34526",
"name": "Finalize Invoice",
"type": "n8n-nodes-base.httpRequest",
"position": [
1392,
208
],
"parameters": {
"url": "=https://api.stripe.com/v1/invoices/{{ $json.id }}/finalize ",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "auto_advance",
"value": "true"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
]
},
"nodeCredentialType": "stripeApi"
},
"credentials": {
"stripeApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.1
},
{
"id": "6811b3d3-4b24-4637-97be-d5d32b9576e8",
"name": "Data Formatting Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
1536,
400
],
"parameters": {
"width": 280,
"height": 368,
"content": "## \ud83d\udcca Data Formatting\n\n**Purpose:** Format invoice data for Google Sheets logging\n\n**Data includes:**\n- Invoice details (ID, number, status)\n- Customer information\n- Financial amounts (converted from cents)\n- Important URLs (hosted invoice, PDF)\n- Timestamps for tracking\n\n**Output:** Clean, formatted data ready for spreadsheet"
},
"typeVersion": 1
},
{
"id": "60bf510e-5a75-4e55-af01-0ca305ddeee5",
"name": "Format Data for Sheets",
"type": "n8n-nodes-base.code",
"position": [
1584,
208
],
"parameters": {
"jsCode": "// Get the invoice data from the HTTP response\nconst invoice = $input.item.json;\n\n// Format the data for Google Sheets\nconst formattedData = {\n // Invoice Information\n stripe_invoice_id: invoice.id,\n invoice_number: invoice.number,\n invoice_status: invoice.status,\n \n // Customer Information\n customer_id: invoice.customer,\n customer_name: invoice.customer_name,\n customer_email: invoice.customer_email,\n customer_phone: invoice.customer_phone,\n \n // Financial Information\n currency: invoice.currency.toUpperCase(),\n amount_due: (invoice.amount_due / 100).toFixed(2), // Convert from cents to dollars\n amount_paid: (invoice.amount_paid / 100).toFixed(2),\n amount_remaining: (invoice.amount_remaining / 100).toFixed(2),\n subtotal: (invoice.subtotal / 100).toFixed(2),\n total: (invoice.total / 100).toFixed(2),\n \n // Invoice URLs\n hosted_invoice_url: invoice.hosted_invoice_url,\n invoice_pdf: invoice.invoice_pdf,\n \n // Dates (converted to readable format)\n created_date: new Date(invoice.created * 1000).toISOString(),\n due_date: invoice.due_date ? new Date(invoice.due_date * 1000).toISOString() : null,\n finalized_at: invoice.status_transitions.finalized_at ? new Date(invoice.status_transitions.finalized_at * 1000).toISOString() : null,\n paid_at: invoice.status_transitions.paid_at ? new Date(invoice.status_transitions.paid_at * 1000).toISOString() : null,\n \n // Additional Information\n collection_method: invoice.collection_method,\n billing_reason: invoice.billing_reason,\n line_items_count: invoice.lines.total_count,\n \n // Timestamp for when this record was processed\n processed_timestamp: new Date().toISOString(),\n \n // Original Shopify Order Reference (if available from previous nodes)\n shopify_order_id: null // This will be populated if you have Shopify data in workflow\n};\n\n// Try to get Shopify order ID from previous nodes if available\ntry {\n const shopifyData = $('Process Line Items').item.json.shopify_order;\n if (shopifyData && shopifyData.id) {\n formattedData.shopify_order_id = shopifyData.id;\n }\n} catch (e) {\n console.log('No Shopify order data found');\n}\n\n// Log the formatted data for debugging\nconsole.log('Formatted data for Google Sheets:', formattedData);\n\nreturn formattedData;"
},
"typeVersion": 2
},
{
"id": "57fcfb57-b703-4f89-baa9-d784b1f3dd48",
"name": "Sheets Logging Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
1760,
-304
],
"parameters": {
"width": 300,
"height": 488,
"content": "## \ud83d\udccb Google Sheets Logging\n\n**Purpose:** Log invoice data to spreadsheet for tracking\n\n**Setup Required:**\n1. Replace YOUR_SPREADSHEET_ID with your actual Google Sheets ID\n2. Ensure proper column headers in your sheet\n3. Grant n8n access to your Google account\n\n**Logged Data:**\n- Invoice details, customer info, financial amounts\n- URLs for invoice access\n- Timestamps for tracking\n\n**Security:** Never commit your actual spreadsheet ID to public repos"
},
"typeVersion": 1
},
{
"id": "c36e89ac-03af-42c7-9c0e-6ca4c1780171",
"name": "Log to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
1792,
208
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "stripe_invoice_id",
"type": "string",
"display": true,
"required": false,
"displayName": "stripe_invoice_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "invoice_number",
"type": "string",
"display": true,
"required": false,
"displayName": "invoice_number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "invoice_status",
"type": "string",
"display": true,
"required": false,
"displayName": "invoice_status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "customer_id",
"type": "string",
"display": true,
"required": false,
"displayName": "customer_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "customer_name",
"type": "string",
"display": true,
"required": false,
"displayName": "customer_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "customer_email",
"type": "string",
"display": true,
"required": false,
"displayName": "customer_email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "customer_phone",
"type": "string",
"display": true,
"required": false,
"displayName": "customer_phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "currency",
"type": "string",
"display": true,
"required": false,
"displayName": "currency",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "amount_due",
"type": "string",
"display": true,
"required": false,
"displayName": "amount_due",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "amount_paid",
"type": "string",
"display": true,
"required": false,
"displayName": "amount_paid",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "amount_remaining",
"type": "string",
"display": true,
"required": false,
"displayName": "amount_remaining",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "subtotal",
"type": "string",
"display": true,
"required": false,
"displayName": "subtotal",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total",
"type": "string",
"display": true,
"required": false,
"displayName": "total",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "hosted_invoice_url",
"type": "string",
"display": true,
"required": false,
"displayName": "hosted_invoice_url",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "invoice_pdf",
"type": "string",
"display": true,
"required": false,
"displayName": "invoice_pdf",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "created_date",
"type": "string",
"display": true,
"required": false,
"displayName": "created_date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "due_date",
"type": "string",
"display": true,
"required": false,
"displayName": "due_date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "finalized_at",
"type": "string",
"display": true,
"required": false,
"displayName": "finalized_at",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "paid_at",
"type": "string",
"display": true,
"required": false,
"displayName": "paid_at",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "collection_method",
"type": "string",
"display": true,
"required": false,
"displayName": "collection_method",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "billing_reason",
"type": "string",
"display": true,
"required": false,
"displayName": "billing_reason",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "line_items_count",
"type": "string",
"display": true,
"required": false,
"displayName": "line_items_count",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "processed_timestamp",
"type": "string",
"display": true,
"required": false,
"displayName": "processed_timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "shopify_order_id",
"type": "string",
"display": true,
"required": false,
"displayName": "shopify_order_id",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uc9oXoocsPsu5X0oDx6Q552389F28K8X_Yp75tAvhaU/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1Uc9oXoocsPsu5X0oDx6Q552389F28K8X_Yp75tAvhaU",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uc9oXoocsPsu5X0oDx6Q552389F28K8X_Yp75tAvhaU/edit?usp=drivesdk",
"cachedResultName": "Orders Invoices Data"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "9cba8243-00ab-42f0-a229-fcf7e559a4b3",
"connections": {
"Hourly Trigger": {
"main": [
[
{
"node": "Fetch B2B Order",
"type": "main",
"index": 0
}
]
]
},
"Fetch B2B Order": {
"main": [
[
{
"node": "Filter B2B Paid Orders",
"type": "main",
"index": 0
}
]
]
},
"Finalize Invoice": {
"main": [
[
{
"node": "Format Data for Sheets",
"type": "main",
"index": 0
}
]
]
},
"Process Line Items": {
"main": [
[
{
"node": "Create Stripe Invoice",
"type": "main",
"index": 0
}
]
]
},
"Create Stripe Invoice": {
"main": [
[
{
"node": "Finalize Invoice",
"type": "main",
"index": 0
}
]
]
},
"Create Stripe Customer": {
"main": [
[
{
"node": "Process Line Items",
"type": "main",
"index": 0
}
]
]
},
"Filter B2B Paid Orders": {
"main": [
[
{
"node": "Create Stripe Customer",
"type": "main",
"index": 0
}
]
]
},
"Format Data for Sheets": {
"main": [
[
{
"node": "Log to Google Sheets",
"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.
airtableTokenApigoogleSheetsOAuth2ApistripeApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Automate B2B order invoicing by fetching orders from Airtable, validating paid B2B entries, creating Stripe customers and invoices, finalizing invoices, and logging structured invoice data into Google Sheets. This workflow ensures seamless B2B billing, centralized…
Source: https://n8n.io/workflows/8950/ — 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.
This workflow automates tax compliance by aggregating multi-channel revenue data, calculating jurisdiction-specific tax obligations, detecting anomalies, and generating submission-ready reports for ta
This n8n workflow automates the end-to-end client onboarding process: capturing client details, validating emails, assigning tiers, generating welcome packs, creating tasks, notifying teams, archiving
This workflow automatically collects a list of companies from Google Sheets, searches for their competitors using SerpAPI, extracts up to 10 relevant competitor names with source links, and logs the r
This workflow runs daily to collect the latest funding round data from Crunchbase. It retrieves up to 100 recent funding events, including company, investors, funding amount, and industry details. The
Automates Singapore COE price tracking with AI forecasts and buy/wait recommendations. Weekly scraping collects LTA data, enriches with economic indicators, predicts 6-month trends, and alerts users v