n8n - AI Agents, AI Automations & AI Voice Agents (1)

install n8n

Hostinger n8n VPS hosting

1
2
3
4
5
6
install as guide flow

# udgrade n8n for the Ubuntu
docker compose pull
docker compose down
docker compose up -d

install in windows(Node.js)

1
2
3
4
5
6
7
8
9
1. search n8n github
2. Node.js install or nvm install(for Node.js version control)
3. npm install -g n8n
4. re-open cmd : run "n8n"
5. browser run : http://local:5678

# udgrade n8n
1. npm update -g n8n
2. n8n

install in Mac(Node.js)

1
2
3
4
5
6
7
8
1. node.js install
2. oper terminal : sudo npm install -g n8n
3. n8n
4. browser run: http://local:5678

# udgrade n8n
1. npm update -g n8n
2. n8n

coding

JSON or “Set Field”

Genernal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Selectors (`.first()`, `.last()`, `.all()`)
{{ $('Source Data').last().json.name }}

# JS function
{{ $('Source Data').last().json.name.toUpperCase() }}
{{ Math.round($('Source Data').last().json.age / 7) }}
{{ typeof $('Source Data').last().json.age }}

# get key
{{ Object.keys($('Source Data').last().json.contact) }}

# convert a structured JSON object into a single string
{{ JSON.stringify($('Source Data').last().json.contact, null, 2) }}

# join all list to a string
{{ $('Split Out Skills').all().map(item => item.json.skills).join(', ') }}

# String to object **** format must seet to object ****
{{ JSON.parse($('8. Utility Functions').item.json.contact_as_string) }}

# load file name - cv0 為傳入之檔名
{{ $node['Text Classifier'].binary["cv0"].fileName }}
# 以下也可以 - attachment_0 為傳入之檔名
{{$('Gmail Trigger').first().binary.attachment_0.fileName}}

# get file keys(input names)
{{$('Gmail Trigger').first().binary.keys()}}
❇️ Translate JSON string to json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 基本轉換:JSON.stringify({ name: "John", age: 30 }) 會得到 {"name":"John","age":30} 字串
# 美化輸出:JSON.stringify({ x: 5, y: 6 }, null, 2) 會以兩格縮排排版,便於閱讀
# 過濾屬性:JSON.stringify(obj, ["id", "name"]) 僅輸出 id 與 name 資料
# 使用函式轉換:JSON.stringify(obj, (key, value) => typeof value === "string" ? value.toUpperCase() : value) 可在序列化時調整值.

# null 代表不使用 replacer,因此不過濾、不改寫屬性
# space 2 縮排(美化)
# (?:json)? 表含或不含 json 用 (json)? 存起資料較占空間 故用 (?:json)
# \s* : 表刪除接著所有空白
# g 表 所有符合條件
# /.../ 歸哲表達是除裡範圍
{{
JSON.stringify(
JSON.parse(
$json.content.parts.first().text
.replace(/```(?:json)?\s*/g, '')
.trim()
),
null,
2
)
}}
❇️ Edit field - 2 condition(僅對單一 輸入 node 有用)
1
2
{{ $json.message.text != null ? $json.message.text : 
$json.content.parts[0].text != null ? $json.content.parts[0].text : ''}}

Google Drive

1
2
3
4
# Drive --> Search files and folders
Search Method: Advanced Search
# 查詢會列出指定父資料夾底下尚未被刪除的所有檔案與子資料夾
Query String: '{{ $json.id }}' in parents and trashed = false

Regular Expression

1
2
# 把所有雙引號 " 取代為已轉義的 ",確保字串在 JSON、JS 字面量或序列化時不會因未轉義引號而破壞語法
replace(/"/g, '\\"')

❇️ Code

array to json output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const items = [
{
name: 'John',
email: 'john@example.com',
age: 25,
status: 'active'
},
{
name: 'Sarah',
email: 'sarah@example.com',
age: 30,
status: 'inactive'
},
{
name: 'Mike',
email: 'mike@example.com',
age: 28,
status: 'active'
}
];

return items.map(item => ({ json: item }));
convert data to a file mp3
1
2
3
4
5
6
7
8
9
10
11
12
return [
{
json: {},
binary: {
data: {
data: Buffer.from($json["audio_data"], "base64"),
mimeType: "audio/mpeg",
fileName: "output.mp3"
}
}
}
];
remove special character
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// n8n code node 轉換前面資料
return items.map(item => {
let text = item.json.output; // 取得前面節點傳來的 output 文字

// 範例:把所有文字轉成大寫
// text = text.toUpperCase();

// 範例:跳脫 Telegram MarkdownV2 的特殊字元(部分)
text = text.replace(/([_*[\]()~`>#+-=|{}.!])/g, '\\$1');

// 將處理後的文字存回新的欄位或覆蓋原本欄位
item.json.processedText = text;

return item;
});
string to json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const responseText = $input.first().json.content.parts[0].text

let jsonString = responseText;

// 移除 ```
jsonString = jsonString.replace(/```json\n?/g, '');

// 移除 ```
jsonString = jsonString.replace(/```\s*$/g, '');

// 移除前後空白
jsonString = jsonString.trim();

try {
// 解析 JSON 字符串
const output = JSON.parse(jsonString);

// 回傳解析後的結構化資料
return {
output
};

} catch (error) {
// 解析失敗時的錯誤處理
return {
error: 'JSON format error',
originalResponse: responseText,
parseSuccess: false,
};
}
10_95Customer+Data+Processor+Complex+(Data+Cleaning+Example)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
const items = $input.all();
const customers = items[0].json.customers;

const processedCustomers = customers.map(customer => {
// Convert revenue string to actual number
const revenueNumber = parseFloat(customer.revenue.replace(/[$,]/g, ''))

let tier ;
if (revenueNumber >= 40000) {
tier = 'Premium';
}
else if (revenueNumber >= 20000) {
tier = 'Standard';
}
else {
tier = 'Basic';
}

const domain = customer.email.split('@')[1];
const isUK = domain.includes('.uk');
const country = isUK ? 'United Kingdom' : 'United States';

const formattedPhone = `(${customer.phone.slice(0,3)}) -${customer.phone.slice(4,6)}-${customer.phone.slice(6,7)}-${customer.phone.slice(8)}`;

return {
// Original data (cleaned)
name: customer.name,
email: customer.email,
phone: formattedPhone,
company: customer.company,

// NEW calculated fields
revenueAmount : revenueNumber,
customerTier : tier,
country: country,

// Business logic fields
isHighValue : revenueNumber >= 25000,
needsAccountManager : tier === 'Premium',
emailDomain : domain
}
})

// sum 初值為 0,傳回加總值
const totalRevenue = processedCustomers.reduce((sum, customer) => sum + customer.revenueAmount, 0);
const averageRevenue = totalRevenue / processedCustomers.length;
// 傳回 c.isHighValue's length
const highValueCount = processedCustomers.filter(c => c.isHighValue).length;

return {
processedCustomers : processedCustomers,
summary :{
totalCustomers : processedCustomers.length,
totalRevenue : totalRevenue,
averageRevenue : Math.round(averageRevenue),
highValueCustomers: highValueCount,
premiumCustomers: processedCustomers.filter(c => c.customerTier === 'Premium').length
},

message: `Processed ${processedCustomers.length} customers with advanced business logic`
};
10_95Weather+Message+Generator+Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const items = $input.all();
const locations = items[0].json.locations;
const weatherReports = locations.map( location => {
let message = '';
let recommendation = '';

if (location.temperature < 10) {
message = `It's chilly in ${location.city} at ${location.temperature}°C`;
recommendation = 'Dress warm and bring a jacket!';
} else if (location.temperature > 25) {
message = `It's hot in ${location.city} at ${location.temperature}°C`;
recommendation = 'Perfect t-shirt weather!';
}
else {
message = `Bice weather in ${location.city} at ${location.temperature}°C`;
recommendation = 'Comfortable temperature today!';
}

if (location.condition.includes('rain')){
recommendation += ' Don\'t forget your umbrella!';
} else if (location.condition.includes('snow')){
recommendation += ' Watch out for slippery roads!';
} else if (location.condition.includes('sunny')){
recommendation += ' great day to be outside!';
}

if (location.humidity > 80 ){
recommendation += ' High humidity - stay hydrated!'
}

return {
city: location.city,
originalDate: location,
weatherMessage: message,
recommendation: recommendation,
alertLevel: location.temperature < 0 ? 'extreme' :
location.temperature < 10 ? 'low' :
location.temperature > 30 ? 'high' : 'normal'
};
})

return {
totalLocations: locations.length,
weatherReports : weatherReports,
generatedAt: new Date().toISOString()
}
10_95Sales+Calculator+Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
const items = $input.all();
const sales = items[0].json.salesData;

const saleWithTotals = sales.map(sale => ({
...sale,
totalPrice: sale.quantity * sale.unitPrice
}));

const totalRevenue = saleWithTotals.reduce((sum, sale) => sum + sale.totalPrice, 0);
const totalQuantitly = saleWithTotals.reduce((sum, sale) => sum + sale.quantity, 0);
const averageOrderValue = totalRevenue / sales.length;

// const productSummary = {};
// saleWithTotals.forEach(sale => {
// if (!productSummary[sale.product]) {
// productSummary[sale.product] = {
// product: sale.product,
// totalQuantity: 0,
// totalRevenue: 0,
// orderCount: 0
// };
// }
// productSummary[sale.product].totalQuantity += sale.quantity;
// productSummary[sale.product].totalRevenue += sale.totalPrice;
// productSummary[sale.product].orderCount += 1
// });


// 使用註解標注屬性
/**
* @typedef {Object} ProductSummaryItem
* @property {string} product
* @property {number} totalQuantity
* @property {number} totalRevenue
* @property {number} orderCount
*/

/** @type {Object.<string, ProductSummaryItem>} */
// JSDoc 的型別註解語法,用來告訴編輯器或其他工具,這個變數是一個物件(Object),並且:
// 物件的鍵(key)是字串(string)型態
// 物件的值(value)是你自己定義的 ProductSummaryItem 結構物件
// 這裡的 Object.<string, ProductSummaryItem> 表示「一個以字串為鍵,存放 ProductSummaryItem 型態物件的字典」。
const productSummary = {};

saleWithTotals.forEach(sale => {
if (!productSummary[sale.product]) {
productSummary[sale.product] = {
product: sale.product,
totalQuantity: 0,
totalRevenue: 0,
orderCount: 0
};
}
productSummary[sale.product].totalQuantity += sale.quantity;
productSummary[sale.product].totalRevenue += sale.totalPrice;
productSummary[sale.product].orderCount += 1;
});

const productAnalysis = Object.values(productSummary).map(product => ({
...product,
averageOrderValue: product.totalRevenue / product.orderCount,
revenuePercentage: (product.totalRevenue/ totalRevenue * 100).toFixed(2)
}))


const bestSellingProduct = productAnalysis.reduce((best, current) =>
current.totalQuantity > best.totalQuantity ? current : best
);

const highestRevenueProduct = productAnalysis.reduce((best, current) =>
current.totalRevenue > best.totalRevenue ? current : best
);


return {
summary: {
totalOrders: sales.length,
totalRevenue: totalRevenue,
totalQuantity: totalQuantitly,
averageOrderValue : parseFloat(averageOrderValue.toFixed(2))
},
bestPerformers : {
bestSelling: bestSellingProduct.product,
highestRevenue: highestRevenueProduct.product
},
productAnalysis: productAnalysis,
salesWithTotals: saleWithTotals
}
10_95Data+Format+Converter+Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const items = $input.all()
const apiData = items[0].json.apiResponse

const flattenedUsers = apiData.users.map(user => ({
// Basic info
userId: user.id,
fullName: `${user.profile.firstName} ${user.profile.lastName}` ,
firstName: user.profile.firstName,
lastName: user.profile.lastName,

// Contact info
email: user.profile.contact.email,
phone: user.profile.contact.phone,

// Preferences
notificationsEnabled: user.preferences.notifications,
theme: user.preferences.theme
}));

// Create different output formats
// 1. CSV-ready format (array of arrays)
const csvData = [
['User ID', 'Full Name', 'Email', 'Phone', 'Notifications', 'Theme'], //Head
...flattenedUsers.map(user => [
user.userId,
user.fullName,
user.email,
user.phone,
user.notificationsEnabled ? 'Yes' : 'No',
user.theme
])
];


// 2. Email list format
const emailList = flattenedUsers.map(user => user.email);

// 3. Contact cards format
const contactCards = flattenedUsers.map(user => ({
name: user.fullName,
contactInfo: `${user.email} | ${user.phone}`,
settings: `Notifications: ${user.notificationsEnabled ? 'On' : 'Off'}, Theme: ${user.theme}`
}));

// 4. Summary statistics
const stats = {
totalUsers: flattenedUsers.length,
notificationUsers: flattenedUsers.filter(u => u.notificationsEnabled).length,
themeBreakdown: {
dark: flattenedUsers.filter(u => u.theme === 'dark').length,
light: flattenedUsers.filter(u => u.theme === 'light').length,
},
// ...new Set(flattenedUsers.map(u => u.email.split('@')[1])) 去除重複值, 展開為 array
domains: [...new Set(flattenedUsers.map(u => u.email.split('@')[1]))]
};

// 5. Grouped by preferences
// 初始值 {}:reduce 的累加器從空物件開始,作為最後要回傳的分組結果容器
const groupedByTheme = flattenedUsers.reduce((groups, user) => {
// const theme = user.theme;
// if(!groups[theme]) {
// groups[theme] = [];
// }
// groups[theme].push(user);

const key = user.theme;
// groups[key] 為 null 或 undefined,則設為空陣列;否則保留原值
groups[key] = groups[key] ?? [];
groups[key].push(user);
return groups;
}, {});


return {
originalData: apiData,
transformedDtat :{
flattenedUsers: flattenedUsers,
csvFormat: csvData,
emailList: emailList,
contactCards: contactCards,
groupedByTheme: groupedByTheme
},
statistics: stats,
metadata: {
processedAt: new Date().toISOString(),
originalTotoa: apiData.metadata.total,
transformedTotal: flattenedUsers.length
}
}
10_96 class example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// get data
const students = $input.all()[0].json.students

const processStudents = students.map(student => {
const totalPoints = student.mathScore + student.englishScore + student.scienceScore;
const averageScore = totalPoints / 3;

let letterGrand;
if (averageScore >=90) {
letterGrand = 'A';
}else if (averageScore >=80) {
letterGrand = 'B';
}else if (averageScore >=70) {
letterGrand = 'C';
}else if (averageScore >=60) {
letterGrand = 'D';
}else {
letterGrand = 'F';
}

const isHonorRoll = averageScore >= 85 && student.attentionPresent >= 90;
const scholarshipEligible = (averageScore >= 88 && student.isFullTime) ? 'Yes' : 'No';
const congratsMessage = `Great job, ${student.studentName}! Your average is ${averageScore.toFixed(1)}`;
return {
json: {
...student,
averageScore: Math.round(averageScore),
letterGrade: letterGrand,
isHonorRoll: isHonorRoll,
scholashipEligible: scholarshipEligible,
personMessage: congratsMessage
}
}
});

let classTotal = 0;
let honorRollCount = 0;
for(let i = 0 ; i < processStudents.length ; i++ ){
classTotal += processStudents[i].json.averageScore;

if (processStudents[i].json.isFullTime) {
honorRollCount += 1;
}
}

const classAverage = classTotal / processStudents.length;

processStudents.push({
json: {
reportType: 'CLASS_SUMMARY',
totalStudents: processStudents.length,
classAverage: Math.round(classAverage),
honorRollStudents: honorRollCount,
hornorPercentage: Math.round((honorRollCount / processStudents.length) * 100 )
}
});

return processStudents;
10_106 add additional field
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const user = $input.item.json;
const fullName = `${user.firstName} ${user.lastName}`;

const birthDate = new Date(user.birthDate)
const ageDiffMs = Date.now() - birthDate.getTime();
const ageDate = new Date(ageDiffMs);
const age = Math.abs(ageDate.getFullYear()-1970);
console.log(`name:${fullName} age:${age}`)

return {
...user,
fullName,
age
}
10_106 await and call API
1
2
3
4
5
6
7
8
9
10
11
12
13
const user = $input.item.json;

const url = `https://api.genderize.io?name=${user.firstName}`

// wait http response
const response = await this.helpers.httpRequest({url: url, json: true});
// const response = await this.helpers.httpRequest({ url: url, json: true });

return{
...user,
gender: response.gender ,
genderProbability: response.probability
}
10_106 reduce process data
1
2
3
4
5
6
7
8
9
10
const users = $items();

const totalAge = users.reduce((sum,user) => {
return sum + user.json.age
}, 0);

return {
totalUsers: users.length,
averageAge: parseFloat(totalAge / users.length)
}
10_106 create csv binary file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const allUsers = $("4. Fetch External Data (Advanced)").all()

let csvContent = "FullName, Age, GenderGuss, ProcessedBy\n";

for (const item of allUsers) {
const user = item.json;
const row = `"${user.fullName}",${user.age},${user.gender},n8n`;
csvContent += row + '\n';
}

// string to bin
const binaryData = await this.helpers.prepareBinaryData(Buffer.from(csvContent), 'user_report2.csv');

return {
json :{
reportGenerated : new Date().toISOString(),
userCount : allUsers.length
},
binary: {
report: binaryData
}
}
❇️ 11_119 AI Agent output string,convert to json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Get the full response string from the previous step (usually "AI Agent" or similar)
// 第0筆, json output 欄位
// const output = items[0].json.output;
const output = $('AI Agent').first().json.output;

// show type
// ? 是「可選鏈結運算子」(optional chaining);它會在左邊為 null 或 undefined 時直接回傳 undefined,而不是丟錯
// check array and array
console.log('typeof output:', typeof items?.[0]?.json?.output);
console.log('isArray:', Array.isArray(items?.[0]?.json?.output));

// Try to extract the JSON array from the output string
// 過濾前後 ```json\n , \n``` 字串
const jsonStart = output.indexOf('[');
const jsonEnd = output.lastIndexOf(']') + 1;
const jsonString = output.slice(jsonStart, jsonEnd);
// Parse the JSON array
const contacts = JSON.parse(jsonString);

// compare contacts vs contacts.map(contact => ({ json: contact }))
// console.log(contacts);
// console.log(contacts.map(contact => ({ json: contact })));

// console.log('contacts:', typeof contacts?.[0]?.json?.Name);
// 奇怪在 AI Agent 後, 程式中執行 contacts.map(contact => ({ json: contact })) 就會出問題
//console.log('contacts map:', typeof contacts.map(contact => ({ json: contact })).[0]?.json?.Name);

// Optionally, return each contact as a separate item for downstream nodes (like Sheets)
// 因 n8n 是透過 json 溝通, 其實沒有差別
//return contacts.map(contact => ({ json: contact }));
return contacts;
❇️ code check from 2 source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let text = null;
let voice = null;

try {
text = $input.all(0)[0].json.message.text;
} catch(e) {
console.log("01:", e);
// ..
}

try {
voice = $input.all(1)[0].json.content.parts[0].text;
} catch(e) {
// ..
}

const output = {
text : text ? text : voice ? voice : '',
}

return output ;
❇️ data string 分段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const inputData = $input.first();
const longText = inputData.json.output;

// 分割長訊息
function splitMessage(message, limit = 4096) {
let messages = [];
let currentChunk = '';
const words = message.split(' ');
for (const word of words) {
if ((currentChunk + ' ' + word).length > limit) {
messages.push(currentChunk);
currentChunk = word;
} else {
currentChunk += (currentChunk ? ' ' : '') + word;
}
}
if (currentChunk) messages.push(currentChunk);
return messages;
}

// 分段後,轉成 n8n 的 items 格式
const chunks = splitMessage(longText, 3600);
const items = chunks.map(chunk => ({
json: {
...inputData.json, // 保留原始 json 內容
output: chunk // 把 output 改成當前這一段
}
}));

return items;

JavaScrip Function

function string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.length
.base64Encode()
.base64Decode()
.concat()
.extractDomain()
.extractUrl()
.extractUrlPath()
.hash() - default MD5 hash
.quote()
.removeMarkdown()
.removeTags() - HTML
.replaceSpecialChars()
.slice(1,5)
.substring(8,10)

JSON.stringify( )
.trim()
i.ndexOf('We')
.lastIndexOf('e')
.match(/\s.*/) : regular expression
.search('AI')
.isDomain()
.isEmail()
toLowerCase()
.toUpperCase()
.toSnakeCase()
.toDateTime()
.toNumber()
function number
1
2
3
4
5
6
7
8
9
10
11
(4.135678).round(3)
(4.9135678).floor()
(4.135678).ceil()
(4092111).format('en-US')
(4092111).isEven()
(4092111).isOdd()
(4092111.32).isInteger()
(4092111).toBoolean()
(34-1-33).toDateTime()
(1342).toLocaleString():numbet to locale using format
(1342).toString()
function array
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$json.array.length
$json.array.last()
$json.array.first()
$json.array.includes('n8n')
$json.array.append('google', 'Opera')
$json.array.append({"name":"Robert"}): append object
$json.array.chunk(3) : every array 3 item(start 1st)
["Bill", "", "Jonn", "Ton", ""].compact(3): remove blank item
["Bill", "", "Jonn", "Ton", ""].concat("jay", "chen"): same as append
[1,2,3,4,5].difference([2,4]): retuen no select array items
[1,2,3,4,5].find(number => number > 3) : get 1st condtion item
$json.array.indexOf('n8n') : return item index
[1,2,3,4,5].intersection([2,10,3,5]: item in 2 array
[1,2,3,4,5].isEmpty()
[1,2,3,4,5].isNotEmpty()
[1,2,3,4,5].join(' + ') : join to a string
[{nama:'Robert'},{agg:42}].merge([{city:'Taipei'},{country:"Taiwan"}]): merge 2 object array to one object
[{name:'Robert',age:42},{name:'Taipei',country:"Taiwan"}].pluck('name','age'): get the data have same key
$json.array.randomItem()
[{name:'Robert',age:42},{name:'Taipei',country:"Taiwan"}].renameKeys('name','new') : change key
$json.array.reverse(): reverse orginal array
$json.array.slice(1,3): slice array
[{course:'n8n', students:1000},{course:'Midjourney', students: '2231'}].smartJoin('course', 'students'): output object "course value" is key and "students value" is value
$json.array.sort() : sort original array
$json.array.toJsonString(): array to string
$json.array.toSpliced(1,2) : remove array item, from index 1 remove 2
$json.array.toSpliced(1,0, 'google'): remove 0 and add item 'google'
[1,2,3].union([2,4,6]): merage 2 array, but remove same item(reserve just 1)
[1,2,3,4,2,6].unique(): 1 array remove duplicate item(reserve just 1)
$json.array.map(item => item.toUpperCase()):run for all item
[1,2,4,5,3].filter(item => item < 4)
{{[1,2,4,5,3].reduce((last, current) => last + current)}}
function object && bollen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$json.object.keys()
$json.object.values()
$json.object.isEmpty()
$json.object.isNotEmpty()
$json.object.hasField('courseName')
$json.object.compact() : remove item null, undefined, blank..
$json.object.keepFieldsContaining('a'): value include something
$json.object.removeField('instructor')
$json.object.removeFieldsContaining('o')
$json.object.toJsonString()
http://example.com/?{{ $json.object.urlEncode() }} : http://example.com/?courseName=n8n+course&studentsEnrolled=10000&instructor=Krystian+Wojtarowicz

$json.bool.toNumber()
$json.bool.toString()
function Other
1
2
3
4
5
6
7
8
9
10
11
12
{{$now}} : [DateTime: 2025-09-27T23:26:01.837+08:00]
{{$today}}:[DateTime: 2025-09-27T00:00:00.000+08:00]
{{$now.format('yyyy-MM-dd HH:mm')}}: 2025-09-27 23:30
{{$now.plus(7, 'days').format('yyyy-MM-dd HH:mm')}}: 2025-10-04 23:32
{{$now.minus(7, 'days').format('yyyy-MM-dd HH:mm')}}: 2025-09-20 23:33
{{"9/26/2025".toDateTime().diffTo($now.format('yyyy-MM-dd'), 'days')}}:-1
5:30/10:49
{{$if( $now.day > 29 ,'hello' , 'goodbye')}} : goodbye
{{JSON.stringify($json.object)}} : "{\"courseName\":\"n8n course\",\"studentsEnrolled\":10000,\"instructor\":\"Krystian Wojtarowicz\"}"
{{[1,2,3,4,5].max()}}: 5
{{[1,2,3,4,5].min()}}: 1
{{$workflow.id}}: qdMW0He05AtJPV0m(also support name, active)
function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# check if something is an array  
Empty arrays ≠ null ≠ undefined - use Array.isArray() to check first

# tells you what data type something is
{{ typeof $json.fieldName }}

# converts to true/false
Boolean()

# convers to whole number only
ParseInt()

# converts to decimal numbers
ParseFloat()

# Safe number conversion
`{{ Number($json.value) || 0 }}` - This converts any value to a number, but gives you zero if the conversion fails instead of breaking your workflow.

# String to boolean (safe)
`{{ $json.value === 'true' }}` - This is much safer than just using Boolean() because it explicitly checks for the string "true" rather than treating any non-empty string as true.

# Extract from object
`{{ $json.user.department }}` - Use dot notation to pull specific values out of nested objects. This is super common when working with API responses.

# Array to string
`{{ $json.technologies.join(', ') }}` - Perfect for creating readable lists or sending array data to systems that only accept text.

# String to array
`{{ $json.techString.split(', ') }}` - The reverse operation, turning comma-separated text back into arrays you can loop through or manipulate.

# Object to string
`{{ JSON.stringify($json.user) }}` - Essential for storing complex objects in databases or sending them to APIs that expect text.

# String to object **** format must seet to object ****
`{{ JSON.parse($json.userDataString) }}` - Converting JSON strings back to objects so you can access their properties.

# Conditional conversion
`{{ Number($json.score) >= 80 ? 'Senior' : 'Junior' }}` - Combining conversion with logic to create derived fields based on your business rules.

# Handle null values
`{{ $json.value || 'default' }}` - Prevents null or undefined values from breaking your workflow by providing fallbacks.

# Trim whitespace:
`{{ $json.text.trim() }}` - Always use this on user input because people love to add extra spaces.

Example
1
2
3
4
5
6
7
8
9
10
11
{{ JSON.stringify($json.user) }}
{{ JSON.parse($json.userDataString) }}
{{ $json.isVerified ? "Verified" : "Pending" }}
{{ Number($json.scoreText) + Number($json.priceText) }}
{{ JSON.parse($json.userDataString).level }}
{{ Number($json.scoreText) >=80 ? "Senior": "Junior" }}
profile(Object) : {{ {usname:$json.user.username, level: Number($json.scoreText) > 80 ? 'Senior' : 'Junior', verified: $json.isVerified, score: Number($json.scoreText) } }}
{{ $json.technologies.join(' | ') }}
{{ $json.techString.split(',') }}
{{ $json.technologies[0] }}
{{ $json.technologies.length }}

Tool

Slack

Create : Slack API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 1. Slack 建立 channel : 要先建立工作空間(一個組織或團隊的工作空間(Workspace)) 
# n8n_discuss2

# 2. https://api.slack.com/apps
Create New App
--> From Scratch
--> App Name: Testing2, Pick a workspace to develop your app in: AI LAB(my work space)

# 3. n8n Credeintal OAuth2
select Slack OAuth2 API
--> connect my account
--> select Workspace (若已有選 Workspace, 要先選其他在選回來,中間可能會先跳到 Slack 畫面)
--> 安裝 n8n.cloud
--> Access connected

# 4. to new app(Testing2)
--> OAuth & Permissions
--> Bot Token Scopes
--> Add an OAuth Scope
app_mentions:read
channels:read
chat:write
channels:join
groups:read
--> OAuth Tokens
--> Install to AI LAB
--> 安裝 Testing2
--> copy "Bot User OAuth Token"

# 5. n8n Credeintal Access
select Access token
--> Access Token *: 填入 "Bot User OAuth Token"
--> Signature Secret: 填入 new app --> Basic Information --> Signing Secret
--> Eescute step --> some error(app 尚未受邀)

# 6. 回到 Slack , channel n8n_discuss2
下 @Testing2 hi
--> 選新增提及使用者
--> 再回去執行即 ok

# 7. 至 Slack Trigger node
選 Webhook URLs(注意有 test URL and Production URL)
copy URL to new App(Testing2)

# 8.Slack App testing2
OAuth & Permissions
--> Event Subscripts
--> Enable Events: Enable
--> Request URL: 填入 "Slack Trigger node" 之 Webhook URLs

# 9. 至 Slack Trigger node
執行

# 10.Slack App testing2
Request URL --> Retry (會show Verified)
--> Subscribe to bot events
--> Add Bot User Event
--> app_mention
--> Save change

# 11. n8n_discuss2
@Testing2 hello --> Slack trigger node 即可收到

# other record
Slack token 申請 : [Slack API]
--> Creat an App
--> From scratch
--> App Name : n8n work, Pick a workspace to develop your app in: AI LAB
--> Create App
--> Generate token and Scopes : 含 Client ID,Client Secret
--> OAuth & Permissions
Add New Redirect URL from n8n "OAuth Redirect URL"
Bot Token Scopes : chat:write , users:read, files:write
User Token Scopes : chat:write , users:read, files:write

Google

google standard node
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# get API
google cloud --> console(控制台)
--> API 服務
--> OAuth 同意畫面
--> 用戶端(若需要 建立用戶端)
已授權的重新導向 URI : 填入 n8n node OAuth Redirect URL
用戶端 ID: 填至 n8n node Client ID *
用戶端密碼: 填至 n8n node Client Secret *(產生後就不能讀取)

# google API 啟用
Google Sheets API --> 啟用
search google drive API --> 啟用
YouTube Data API v3 --> 啟用
google People API(for google Contracts) --> 啟用(似乎用不到,先關掉)
google HTTP Request(YouTube) - Youtube PlaylistItems Doc or Youtube PlaylistItems Doc-TW
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# channel 
Method: GET
URL: https://www.googleapis.com/youtube/v3/channels
Authentication: Predefined Credential Type
Credential Type: Google OAuth2 API
Google OAuth2 API:
OAuth Redirect URL: copy to google cloud 用戶端 已授權的重新導向 URI
Client ID *: copy from google cloud 用戶端 用戶端 ID
Client Secret *: copy from google cloud 用戶端 用戶端密碼
Scope: https://www.googleapis.com/auth/youtube.readonly
Send Query Parameters: Enable
Query Parameters
part: contentDetails
mine: true

# playlist
Method: GET
URL: https://www.googleapis.com/youtube/v3/playlistItems
Authentication: Predefined Credential Type
Credential Type: Google OAuth2 API
Google OAuth2 API:
same previous
Send Query Parameters: Enable
Query Parameters
part: snippet,status
playlistId: {{ $json.items[0].contentDetails.relatedPlaylists.uploads }}
maxResults: 20
order: date

Telegram token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
login 
--> search "botfather"
--> pick @BotFather
--> Start
--> press "/newbot "
--> enter name "hot5656firstresponder_bot"
--> copy token to n8n telegram node credential

# create 2nd Bot
Telegram
--> BotFather
--> press "/newbot "
--> press "Nutritionist_Agent"
--> press "hot5656_Nutritionist_bot"
--> copy token to n8n telegram node's credential
--> search "Nutritionist_Agent"
--> pick from BotFather

Airtable token

1
2
3
4
5
6
7
8
9
10
11
# add a base
Create
--> Create a app
-> Untitled Base
--> Field: File ID,Creation Time,Recipient Email

# API KEY
Account
--> go to developer hub
--> create token
--> scopr:data.records:read,data.records:write,schema.bases:read,access:all

❇️google Gemini API key - Google AI Studio - ❇️Usage, ❇️Condition

1
2
3
4
5
6
7
8
9
10
login 
--> get API key
--> Create API key

# 頻率限制:
每分鐘要求數 (RPM)
每分鐘權杖數 (輸入) (TPM)
每日要求數 (RPD)

# 免費方案有流量和次數限制(如每日或每分鐘請求數、Token數),但一般個人用途足夠,超額才需付費

google API key for Youtube

1
2
3
4
5
6
7
google cloud console 
--> 憑證(Credintials)
-->建立憑證(Create credentials)
--> API key

# enable API
YouTube Data API v3

Reddit App

1
2
3
4
5
6
7
https://www.reddit.com/prefs/apps 
--> create pp
name: n8n automation
web app(select)
redirect uri :copy from reddit credential's OAuth Redirect URL
--> creat app
--> copy secret and web app(id)

Notion

Notion Integrations 建立 內部整合密鑰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 建立 內部整合密鑰
--> 新整合
整合名稱:Demo
關聯工作空間: Robert Kao Notion
類型:Internal
--> 儲存
功能: 閱讀評論,插入評論 eneable , copy 內部整合密鑰
--> 儲存
你的整合尚無任何頁面的存取權限
如欲開始使用整合,請從以下位置新增頁面和資料庫:Access Tab
--> 選取頁面
--> 私人
--> select all
--> 更新選取權限

# 建立新 page
# 另 許權限也要加入
+開始協作
-->建立新page : n8n transcribe

# 建立新 page #2
+ 開始協作
--> 輸入 page name
--> 點3點處
--> 連接
--> Demo (my 整合名稱)
輸入 /table 選 table view
input database name --> Youtube post #1
name change to : Video Name(example)
+ Add property : Facebook Post(text) ...
1
2
3
4
# 建立 File Uploaded - Google Drive(表格)
notion home - https://www.notion.so/
--> +建立協作 set name: File Uploaded - Google Drive
--> 設定為表格, 欄位 Name(文字), File URL(link)
1
2
3
4
** duplicate other database
--> 點3點處
--> 連接
--> Demo (my 整合名稱)

peplexity API key

1
2
3
4
User
--> Account
--> API Key
--> create

Apify token

1
2
3
4
Apify login
--> Actor
--> API
--> copy token

Supabase

Supabase use for RAG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
New Project --> 
name :RAG Restaurant Demo
generate passowrd (must copy)
--> create New Porject
--> connect
--> Transaction pooler --> view parameter see all for n8n
host: aws-1-us-east-1...
port: 6543
database: postgres
user: postgres...
pool_mode: transaction

# see information
table can see :
n8n_chat_histories
n8n_vectors

# send "Test step" for check database is ok(it also available for Simple Vector Store)
send "Test step" to Postgres PGvector Store , then give me the response for test it
Supabase use for whatsapp
1
2
3
4
5
6
7
8
9
10
11
12
13
New Project --> 
name :whatsapp longterm memory
generate passowrd (must copy)
database --> teable
--> create a table
-->name:ChatMemory
--> add column
message - text
recipient text
sender text
Project Settings:
--> Data API's URL (Credential's URL)
--> Data API's service_rolesecret(Credential's Service Role Secret)
Supabase use for recorded communication and RAG : Supabase Vector Store node –> quickstart for setting up your vector store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
New Project --> 
name :Remeber AI Agent
generate passowrd (must copy, ✅ use for Postgres Chat Memory)
--> create New Porject
--> connect
select Method: Transaction pooler
--> view parameters (✅ use for Postgres Chat Memory)
host: aws-1-ap-southeast-2.pooler.supabase.com
port: 6543
database: postgres
user: ...
pool_mode: transaction

Project Setting
--> Data API
URL: ... (✅ use for Supabase Vector Storey)
--> API key --> Legacy API Keys
service_rolesecret: ... (✅ use for Supabase Vector Store)

# create Table for Supabase Vector Store
--> SQL Edit
--> copy SQL form [quickstart for setting up your vector store] to SQL Edit
--> 🔷 change vector(1536) to vector(768) for Gemini
--> 🔷 need Enable RLS (Authentication --> Policies)
--> Run
--> 🔷 change "create extension vector" to "create extension if not exists vector"
--> 🔷 change "embedding extensions.vector(768)" to "embedding vector(768)"
--> 🔷 if found 'Error: Failed to run sql query: ERROR: 42P07: relation "documents" already exists', delete Table "documents"
--> 🔷 if found 'Error: Failed to run sql query: ERROR: 42723: function "match_documents" already exists with same argument types', change "create function match_documents" to "create or replace function match_documents"
--> check Table have "documents"

# Enable RLS
Authentication
--> Policies
--> enable "documents" and "n8n_chat_histories" RLS

# n8n have 2 Credential
🟢Postgres Chat Memory: directly use the Database
🟢Supabase Vector Store: use the Supabase REST API

facebook developer WhatsApp

建立應用程式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
facebook developer
--> 我的應用程式
--> 建立應用程式

create APP
--> name + mail
--> case:other
--> type:business
--> correct App

WhatsApp
--> Setup
--> App Setting Setting
--> Basic
--> get id and pass

whatApp --> API Setup
加入號碼(保留 test phone number)
--> generate access token
--> 選擇可以存取所有目前和日後的WhatsApp 帳號
Client ID & ient Secret(receive from WhatsApp)
1
2
3
4
--> 我的應用程式
--> n8n Agent(目前我用的)
--> 應用程式設定
--> 基本資料:應用程式編號,應用程式密鑰
權杖 及 Account ID(send to WhatsApp)
1
2
3
4
5
6
7
8
9
10
11
--> 我的應用程式
--> n8n Agent(目前我用的)
--> WhatsApp
--> API 設定
--> 產生存取權杖(僅能用數小時), WhatsApp Business 帳號編號
--> 工具(延伸至90天)
--> 存取權杖偵錯工具
--> 存取權杖
--> 填入權杖
--> 偵錯
--> 延伸存取權杖

Rapid - zillow(By Sabri)

1
2
3
4
# get Aillow key
zillow --> subscribe to test --> test endpoint(30 per month)
url: https://zillow56.p.rapidapi.com/search?
x-rapidapi-key: fb...

pinecorn - for RAG

1
2
3
4
5
6
7
8
9
Pinecorn --> Create index
--> n8nragagent
--> Configuration
--> text-embedding-3-small
--> Custom setting:
--> Dimension :768 (for Gemini need set to 768, OpenAI set to 1536)
--> API keys
--> Create API key
--> n8nragagent (recorded APi key)

groq - for mamny LLM, free limit, support Model

1
2
3
4
5
6
# API keys
Default Project --> API Keys

# Use infomation
Dashboard --> Usage --> Activity
Dashboard --> logs

SerpAPI - free 250 searches / month

1
2
3
4
5
# API Key : get API

# YOur Searches: Search data

# Your API Mertrics: userage information

Setting

API return code

1
2
3
2xx(success): 200
4xx(client Error) : request error
5xx(Server error)

prompt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
## Role & Purpose
The role tells the AI who it is supposed to be. The purpose tells the AI why it exists and what its main job is. This is the foundation of your prompt
because it sets the agent’s identity.
- ..
- ..

## Capabilities
This section describes what the agent is able to do - its core skills and strengths.
- ..
- ..

## Tools (Optional)
This section explains which external tools, data sources, or APIs the agent can use and how they fit into its workflow.

## Interaction Style
This section defines how the agent should communicate - its tone, level of detail, and overall personality when responding.

## Example Output (Optional)
This section shows the agent what a good response looks like. By providing examples, you give the AI a pattern to follow, which makes its answers
more consistent and accurate.

## Notes (Optional)
This section includes any extra guidance or reminders that help the agent work better. Notes are optional but useful for edge cases, exceptions, or
small rules that don’t belong in the main prompt.

❇️ custom key in header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Authentication:GenericnCredential Type
Generic Auth Type: Custom Auth
Custom Auth: Custom Auth account(PollO)-set as suitable

JSON exmple:
{
"headers": {
"x-api-key": "your_key..",
"Content-Type": "application/json"
}
}

{
"headers": {
"Authorization": "your_key..",
"Content-Type": "application/json"
}
}

# API Market free image & video upload
{
"headers": {
"x-magicapi-key": "your_key..",
"accept": "application/json",
"Content-Type": "multipart/form-data"
}
}

# Credintial for Tavily
{
"headers": {
"Authorization": "your_key.."
}
}

# Credintial for Serper
{
"headers": {
"Content-Type": "application/json",
"X-API-KEY": "your_key.."
}
}

❇️ twelvedata - set key at Query Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. set at Query Parameters
Name: apikey
Value: your_api_key

# 2. set at Authentication
Authentication: Generic Credential Type
Generic Auth Type: Query Auth
Query Auth: Query Auth (twelvedata) - add it
Name: apikey
Value: your_api_key

# Setting
Method: GET
URL: https://api.twelvedata.com/time_series
Send Query Parameters: Enable
symbol: {{ $json.message.text }}
interval: 1min
outputsize: 100

GNews - set key at Query Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
# set at Authentication
Authentication: Generic Credential Type
Generic Auth Type: Query Auth
Query Auth:Query Auth (Gnews)
Name: apikey
Value: your_api_key

# setting
Method: GET
URL: https://gnews.io/api/v4/search
Send Query Parameters: Enable
q: {{ $json.topic }}
lang: en

NewsAPI - set key at Query Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# set at Authentication
Authentication: Generic Credential Type
Generic Auth Type: Query Auth
Query Auth: Query Auth (twelvedata) - add it
Name: apikey
Value: your_api_key

# setting
Method: GET
URL: https://newsapi.org/v2/everything
Send Query Parameters: Enable
q: {{ $json.message.text }}
from: {{ $today.minus({ days: 2 }).toFormat('yyyy-MM-dd') }}

# setting #2
Query Parameters: Enable
q: {{ $json.topic }}
language: en
sortBy: publishedAt
pageSize: 20

Twitter API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# set at Authentication
Authentication: Generic Credential Type
Generic Auth Type: Header Auth
Header Auth: Header Auth (twitterapi) - add it
Name: X-API-Key
Value: your_api_key

# setting
Method: GET
URL: https://api.twitterapi.io/twitter/tweet/advanced_search
Send Query Parameters: Enable
Query Parameters
query: openai -- search data
queryType: Top
Pagination
Pagination Mode: Response Contains Next URL
Next URL: https://api.twitterapi.io/twitter/tweet/advanced_search?cursor={{ $response.body.next_cursor }}
Pagination Complete When: Other
Complete Expression: {{ $response.body.has_next_page == false }}
Limit Pages Fetched: Enable
Max Pages: 5
Interval Between Requests (ms): 3000 -- 若有問題增加 6000

Perplexity - ask Perplexity API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# set at Authentication
Authentication: Generic Credential Type
Generic Auth Type: Bearer Auth
Bearer Auth: Bearer Auth (Perplxity) - add it
Bearer Token: <api_key>

# set API key - Send Headers
Send Headers: Enable
Specify Headers: Using Fields Below
Header Parameters
Name: Authorization
Value: Bearer <api_key>

# Setting
Method: POST
URL: https://api.perplexity.ai/chat/completions
Send Body: Enable
Body Content Type:JSON
Specify Body:Using JSON
JSON:
{
"model": "sonar",
"messages": [
{
"role": "system",
"content": "Be precise and concise."
},
{
"role": "user",
"content": "{{ $json.chatInput }}"
}
]
}

Rapid - zillow

1
2
3
4
5
URL: x-rapidapi-host's value
Authentication:GenericnCredential Type
Generic Auth Type: Header Auth
Name: x-rapidapi-key
Value: x-rapidapi-key's value

This message was sent automatically with n8n

1
Add Field --> Append n8n Attribution : disable

n8n Community

enable
1
2
3
4
5
6
7
8
9
10
11
12
13
# enable Community node support
n8n home
--> Admin Panel (see version : Running version n8n@1.104.2)
--> setup
Verified community nodes: on (can install community nodes)
also can change version, Zone
--> save change
--> Dashboard
--> Open Instance (return n8n home)

# see install community node
--> User --> setting (can see Cmmunity Notes)
--> can see installed community nodes
install by n8n cloud
1
2
3
4
5
6
7
8
9
# Conver text to speetch(11 labs)
+ search "elevenlabs"
--> install
--> Conver text to speetch(11 labs)

# web search, can use for I Agent's tool
# tavily Tool
+ search "tavily"
--> install (有時失敗,再試一次就好了)
install by n8n hosting
1
User --> setting -->  Cmmunity Nodes --> install searching 

node

n8n form : on form submission

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Form Title : Contact Form
Form Description : Contact us when you have any problem to solver
Add form Elemant:
Field:Name
Placholder: Enter your name(required)

Field:Last Name
Placholder: Enter your last name(required)

Field:Email
Element type : Email
Placholder: Enter your email(required)

Field:Description
Placholder: Provide information about the problem(required)

Field:Course Type
Element type : Dropdown List:n8n course, Zapier course, Midjourney course

send a message to Slack : Slack –> send a message

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Slack OAuth2 API
OAuth Redirect URL --> copy to Slach
Client ID : put Slack Id
Client Secret : put Slack Secret

Send Message To : channel
Channel: n8n-work
message type : simple Text Message
Message Text :
Client as request
Name:{{ $json.Name }}
Email:{{ $json.Email }}
Phone number:{{ $json['Phone Number'] }}
Description:{{ $json.Description }}

add user to google contract : google contracts –> create a contract

1
2
3
4
5
operation : Create 
Give Name : {{ $('Google Sheets Trigger').item.json.Name }}
Add field:
Email: Type:work, value:{{ $('Google Sheets Trigger').item.json.Email }}
Phone: Type:mobil(有些值會 check number format), value:{{ $('Google Sheets Trigger').item.json['Phone Number'] }}

wait message from Telegram’s bot “ on app event -> telegram –> on message

1
-> set credential (hugginFace cannot support telegram, change to n8n cloud) 

AI Agent framework :AI agent node

1
2
3
4
5
6
7
8
9
# n8n 針對 AI Agent 節點(AI Agent / OpenAI Chat Model 等)設定了每日免費請求次數限制。當你觸發超過這個免費次數,就會看到這類 rate limit exceeded 或 too many requests 的訊息。
# 若需額外使用量,系統會提示你加購點數(credits):根據錯誤訊息和官方社群解釋,購買 10 credits 可解鎖每天 1,000 次免費模型請求額度。
# 這個限制是 n8n 針對其自家/託管的 AI 服務設下,無論你是在 n8n Cloud 還是自架 n8n,只要用 n8n 內建(非自己 API Key)的 AI Agent 模組,都會受這個免費額度天花板約束。
# 如果你想要完全自訂額度或無限制,需在節點改成自行填入自己的 LLM API Key(如 OpenAI API Key)來直連,這樣限制就來自你購買的 API 配額,與 n8n 無關
# 額度提升方式
# 依照系統錯誤訊息,進入 n8n 的 credit 購買流程,加入 10 credits 可每天解鎖 1,000 次免費模型呼叫(不單次,而是每日)。一般會要求你綁信用卡或付費帳戶。
# 若你不加購,隔天會自動恢復基礎的免費額度(約 50 次左右,會依官方政策調整)。
Source for Prompt : Define below
Prompt : {{ $json.message.text }}, output not over 1200 letters, and not use markdown format
1
2
--> set API key
Model : gemini-2,5-flash (也可選其他看看)

Sen to Telgram : Telegram –> send a text message

1
2
3
4
# Telegram 文字訊息的長度限制是 最多 4,096 個字元(字符)。這個限制適用於單條文字訊息,不論是純文字還是帶格式的內容17。
# 此外,附件描述文字的長度上限是 1,024 到 2,048 字元,檔案大小限制普通用戶是 2 GB,Telegram Premium 用戶可達 4 GB1。
Chat ID :{{ $('Telegram Trigger').item.json.message.chat.id }}
Text: {{ $json.output }}

Google Driver –> On change involving a specific Folder

1
2
Folder : Demo
Watch For : File Created

Google Driver–> Share File

1
2
3
4
5
# 若對方非 gmail email, access 時會回送 mail 要求設定權限

File : By Id-{{ $json.id }}
--> add permission
--> Role: Writer, Type:User, Email address: ...

Airtable –>Create a record

1
2
3
4
5
Base:Untitled Base, 
Table:Table 1
File ID:{{ $('Google Drive Trigger').item.json.id }}
Creation Time:{{ $('Google Drive Trigger').item.json.createdTime }}
Recipient Email:..

download file from google drive : google drive–> download file

1
File : {{ $json.id }}

Gemini Transcribe : Gogle Gemini –> Transcribe a recoding

1
2
Model: gemini-2.5.flash or Flash Live
Input Type: Binary File

Gemini Summarize : Gogle Gemini –> Message a model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Model: gemini-2.5.flash or Pro
Prompt: Transcript: {{ $json.content.parts[0].text }}

--> Add Message
Prompt:
Instruction:
Summarize the audio content into plain text, following the structure below.
Rules:
The entire summary must not exceed 1500 characters.
Include and label all the following sections: title, summary, main_ points, action_items, follow_up, stories, references, arguments, related_topics.
Keep the style clear, concise, and organized.
Use short, natural-sounding bullet points for lists.
Example formtating:
title: Notion Button
Summary: A collection of buttons for Notion that streamline workflow and save time.
main_points:
- Overview of Notion buttons
- Example of button use cases
- Best practices for setup
action_items:
- Test different button templates\n
- Implement buttons in daily workflows
- Share templates with team
follow_up:
- Explore advanced button integrations
- Gather team feedback on usage
stories:
- A team improved productivity by 30% using custom buttons
references:
- Official Notion documentation
- Community tutorials on Notion workflows
arguments:
- Buttons reduce repetitive tasks
- Custom buttons enhance flexibility
related_topics:
- Notion automations
- Workflow optimization
-Team collaboration tools

save to Notion : Notion –> Append a block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Credential : Internal Integration Secret: 
parent Page : Notion's create page...
Title:{{ $('Google Drive Trigger').item.json.originalFilename }}

--> add Block
Type name: Heading1
Text:Summary

--> add Block
Type name: Paragraph
Rich Text: off
Text:{{ $json.content.parts[0].text }}

# error -> when data generate from Ai change --> The entire summary must not exceed 1200 characters.
Bad request - please check your parameters
body failed validation: body.children[1].paragraph.rich_text[0].text.content.length should be ≤ 2000, instead was 2406.

update to Notion database : Notion –> Create a database page

1
2
3
4
5
6
7
8
# from node : Google Driver –> On change involving a specific Folder(Demo)
DataBase:From List: File Uploaded - Google Drive
--> Add Property
key name or ID : Name
Title : {{ $json.originalFilename }}
--> Add Property
key name or ID : File URL
URL: {{ $json.webViewLink }}

get a google doc : Google Docs –> Get document

1
2
# from node : Google Driver –> On change involving a specific Folder(n8n testing)
Doc ID or URL : {{ $json.id }}

precess by Gemini : Gemini –> Meessage a modem

1
2
3
4
Model : gemini-2.5-flash 
Prompt: Summarise this content into 3-5 sentences: {{ $json.content }}

Tools : add Wikipedia

http request(freeimage host)

1
2
3
4
5
6
7
8
Method: POST
URL: https://freeimage.host/api/1/upload
Send Body: enable
Body Content Type: Form-Data
key(From data): <your key>
action(From data): upload
source(n8n Binnary File): data
format(From data): json

http request(imgbb)

1
2
3
4
5
6
Method: POST
URL: https://api.imgbb.com/1/upload
Send Body: enable
Body Content Type: Form-Data
key(From data): <your key>
image(n8n Binnary File): data

http request(API Market free image & video upload)

1
2
3
4
5
Method: POST
URL: https://prod.api.market/api/v1/magicapi/image-upload/upload
Send Body: enable
Body Content Type: Form-Data
filename(n8n Binnary File): data

basc LLM Chain + Model : Use for simple prompt-and-response tasks with no advanced logic

Information Extractor + Model : extractor data

Sentimat Anaysys + Model : Use to detect tone (positive, negative, neutral) in text.

Text Classifier + Model : Use to sort text into categories

Summarization chain : Use to condense long content into a short summary

Split : Turn a list item(s) into separate items

Splitter

  • Character Text Splitter
    • 依「字元數」分段,可指定分隔符(如換行、句號或自訂符號),再按固定長度切塊。​
    • 優點是邏輯簡單、效能高,但容易在句子或段落中間硬切,語義連貫性較差,適合只需要粗略切片的情境。​
  • Recursive Character Text Splitter
    • 同樣以「字元」為限制,但會「遞歸」按層級嘗試:先用段落、再句子、再單字等分割,盡量讓每個 chunk 在不超過長度限制下保持自然語段。​
    • 一般 RAG / 向量搜尋工作流比較推薦用這個,因為能在控制長度的同時保留較完整的上下文結構。​
  • Token Splitter
    • 不是看字元,而是先把文字轉成模型用的 BPE「token」,再按 token 數量(例如 500 tokens 一塊)切分,最後再轉回文字。​
    • 最大優點是能精準對齊 LLM 的上下文長度限制,輸入長度控制更準確,適合直接拿去給 OpenAI 等模型做 embedding 或對話。​

如果只想選一個「通用、安全的」做法,平常在 n8n 裡處理長文、做向量資料庫檢索,通常優先選 Recursive Character Text Splitter;需要精確控制 token 上限(例如怕超過 4k / 8k token)時再改用 Token Splitter。

  • Recursive Character Text Splitter 裡,Chunk Size 決定「每塊文字的最大字元數」,Chunk Overlap 則是「相鄰兩塊之間重疊的字元數」
    • 參數概念說明
      1.Chunk Size:每個 chunk 最長可以包含多少個字元,超過時就會依分隔符(段落、句子、空白等)往下切。​
      2.Chunk Overlap:相鄰 chunk 之間要重複多少字元,用來保留上下文,避免一句話剛好被切斷時語意斷裂。​
    • 實務上的建議數值
      1. 一般 RAG 文本(技術文件、網頁、PDF 段落等),常見設定為
      • Chunk Size 約 400–1,000 字元
      • Chunk Overlap 約 10–25% 的 Chunk Size(例如 400 / 100、800 / 160)。​
      1. 有人示範在 n8n 中用 Recursive Character Text Splitter 時,採用 Chunk Size 400、Overlap 100 作為範例設定,兼顧語意與長度控制。​​
    • 依你的情境調整
      1. 長、結構較完整的文章(技術白皮書、部落格):可選擇 Chunk Size 600–1,000,Overlap 100–200,讓每塊有更多上下文,適合「摘要、解釋」類問題。​
      2. 短內容、問答精準檢索(FAQ、聊天記錄切段):可用 Chunk Size 300–600,Overlap 50–120,讓召回更細粒度,避免一次抓到太大段文。

workflow

Perplexity

Smart Perlexity AI Agent
1
2
3
4
5
6
7
8
9
10
11
12
13
On chat message :

Ai Agent
chat model: Google Gemini Chat model(gemini-2.5.flash)
Tools: Perplexity Tool
Credential to connect with: Perplexity account
API Key: <api_key>
text : Defined automatically by the model
Model: Sonar or Sonar Pro more good
System Message: Tool "Message a model in Perplexity" supports searching internal information

# input
chat input : "What are the 5 best AI tools for video editing until now (2025/08/08)
Perlexity Node
1
2
3
4
5
6
7
8
On chat message : 

Perplexity --> Message a model
Model : Sonar or Sonar Pro more good
text : {{ $json.action }}

# input
chat input : give me 5 marketing ai tools in 2025
Perplexity Node(HTTP Request)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
On chat message : 

HTTP request
URL: https://api.perplexity.ai/chat/completions
Send Headers: Enabled
Name: Authorization
Value: Bearer YOUR_API_KEY
Send Body: Enabled
Body Content Type: JSON
Specify Body: Using JSON
JSON:
{
"model": "sonar",
"messages": [
{
"role": "system",
"content": "Be precise and concise."
},
{
"role": "user",
"content": ""{{ $json.chatInput }}"
}
]
}

# input
chat input : give me 5 marketing ai tools in 2005

example AI automation from Templeate

1
2
3
4
5
6
n8n home 
--> template (see workflow template)
--> serch "email summary agent"
--> select templeat
--> Use for free
--> Import template to ... or Copy template to clipboard (JSON)

AI Automation Feedback Collect form

on form submission
1
2
3
4
5
6
Form Title :Feedback Form
--> Add form Element
Field name :Wtat you name?
Field name :What's your email?
Field name :Please give us your feedback, Element type:Dropdown List --> Great,Good,Neutral,Bad
Field name :Please lease a comment
google sheets –> append row in sheet
1
2
docuemnt : Feedback Collection
value : set from form

AI Agent from scratch

chat Trigger
1
send :  Can you please send an email to Boss that I need to reschedule our upcoming meeting?
AI Agent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# add option --> system message
#Role
You are a personal assistant AI Agent. Your primary role is to send emails. You have access to two tools: Contacts and Gmail_Tool.

##Tools
1.Contacts
Use this tool when you need to look up contact information like an email address or phone number. You need to use this tool BEFORE sending an email or BEFORE getting emails.

2. Gmail_Tool:
Use this for handling all email
-related actions, like sending emails, reading emails, or forwarding emails.
- Keep emails professional.
- Format nicely the Body of the email with line breaks and clear structure.
- Always sing off as
Best regards,
Robert
Here is the current time/date: {{ $now.format('DD')}}
AI Agent - Chat Model : Gemini chat model
1
model : gemini-2.5-flash 
AI Agent - Memory : simple memory
1
2
# 保留 message
Context Window Length : 10
AI Agent - Tool : Google Sheets Tool
1
2
3
set name : Contacts
To/Subject/Message : Dedined automatically by the model
--> add option : Append n8n Attribution - disable
AI Agent - Tool : Gmail Tool
1
set name : Gmail_Tool

Get Daily News

Schedule Trigger
Edit Field : set topic value : “World News” , “AI” …
HTTP request#1-GNews, HTTP request#2-news API : for 2 new source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Get GNews Articles
Method: GET
URL: https://gnews.io/api/v4/search
Send Query Parameters: Enabled
Parameter 1
Name: q
Value: AI

Parameter 2
Name: lang
Value: en
Parameter 3
Name: apikey
Value: Your_API_Key

# Get NewsAPI Articles
Method: GET
URL: https://newsapi.org/v2/everything
Send Query Parameters: Enabled
Parameter 1
Name: q
Value: AI
Parameter 2
Name: language
Value: en
Parameter 3
Name: sortBy
Value: publishedAt
Parameter 4
Name: pageSize
Value: 20
Send Headers: Enabled
Name: X-Api-Key
Value: Your_API_Key
Merge
AI AGent(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Prompt:
** Select Top Articles **
From {{$json.articles}}, choose the 15 most relevant articles related to AI technology advancements,
tools, research, and applications.
** Summarize Clearly **
Write in clear, professional, and readable language,
summarizing the main point of each article in 1-2 short sentences.
** Include Links **
After each summary, add the article's URL for readers to explore more.
** Add the Date **
Begin the summary with today’s date:
{{ $now.format('yyyy/MM/dd') }}

# System Message:
** Output Format **
Only return the summary in plain text – no explanations

# chat model : google gemini Model
model : gemini-2.5-flash
gooogle Gemini –> message a model(2):use prompt as AI AGent
Telegram –> send a text message

Generate Video prompt

Schedule Trigger
AI Agent - Create a Idea
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# Prompt:
Give me an idea to create the content about biblical characters speaking to the camera and making a vlog
System Message:
** Purpose **
You generate viral short-form video ideas inspired by biblical characters or scenes, styled like modern TikTok vlogs.
These should feel personal, emotional, and often humorous.
** Think Tool Prompt (used before generation) **
“How can I take a biblical moment or character and turn it into a relatable, viral,
or funny TikTok-style video idea that feels natural, emotional, or modern?”
** Output Requirements **
Your response must include:
- caption – Short, poetic or funny, TikTok-style. Include emojis and 3–5 hashtags
(e.g., #biblevlog #foryou #holycomedy #ancientvibes).
- idea – 1–2 sentence summary of the action, story, or twist in the video.
- environment – Describe what the set looks like: light, textures, mood, setting. Think “GoPro meets ancient Jerusalem.”
- status – Must be "to create", without quotation marks
** JSON Output Parser **
Every output must strictly follow this format:
{
"caption": "",
"idea": "",
"environment": "",
"status": ""
}
** Sample Outputs **
Example 1
{
"caption": "When Jesus gives you the silent treatment ️😶 #jesusvlog #holycomedy",
"idea": "Jesus calmly stares at the camera while the disciple keeps asking, 'Are we lost?' – awkward silence builds up.",
"environment": "Desert trail, golden hour light, dry bushes around, warm rocky textures and wind softly blowing.",
"status": "ready for production"
}
Example 2
{
"caption": "POV: You just met Mary Magdalene and she’s not what you expected 😳✨ #biblevlog #foryou",
"idea": "A modern-style introduction of Mary Magdalene where she breaks the fourth wall with sarcasm and confidence.",
"environment": "Dusty stone home with narrow light beams, clay walls, fabric drapes blowing slightly in the breeze.",
"status": "ready for production"
}
Example 3
{
"caption": "Me trying to explain the flood before it happened ️🐘 #noahvibes #bibletok",
"idea": "Noah explains why he’s building the ark, while others laugh in the background — like a vlog rant.",
"environment": "Half-built wooden structure, cloudy sky, goats wandering around, mud everywhere, distant laughter.",
"status": "concept only"
}

# chat model : google gemini Model
model : gemini-2.5-flash

# Require Specific Output Format : enable
# Output Parser : Structure Output Parser
{
"caption": "",
"idea": "",
"environment": "",
"status": ""
}

# Tool : Think
AI Agent - Create a Video prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# Prompt:
Create a prompt for the Veo3 video based on the following information:
Idea: {{ $json.output.idea }}
Environment: {{ $json.output.environment }}
# System Message:
** System Message **
You are a professional cinematic prompt generator for AI video tools.
Your task is to convert a short-form video idea and its environment description
(inspired by biblical or ancient-world themes)
into a highly detailed, camera-aware,
emotionally rich prompt suitable for generating a cinematic AI video using Veo3.
You must understand the emotional tone, visual aesthetics,
and storytelling style of short-form TikTok videos that imitate biblical scenes with modern pacing and framing.
** Veo3 Prompt Structure Guidelines **
Each prompt should be natural, cinematic, and visually immersive, including:
- Scene description – Describe the overall environment clearly (terrain, buildings, light, movement).
- Character focus – If the idea involves a person,
include their position, outfit style (biblical era), mood/expression, and framing (e.g., close-up, wide shot).
- Camera movement – Always include one cinematic camera motion
(e.g., slow push-in, handheld tracking, drone pan, orbit, dolly backward).
- Time of day & lighting – Set the mood with light (e.g., golden hour, harsh daylight, candlelit interior).
- Atmosphere & texture – Mention dust, wind, fabric movement, shadows,
reflections, or imperfections in the scene to increase realism.
- Style & quality – Include cinematic tone, analog warmth,
slightly soft vintage film look, GoPro-like closeness if appropriate.
** Input **
- You will receive two fields: idea, environment
- Return only the complete and detailed Veo3 prompt, no additional text.
** Example Output **
A biblical man stands in front of a half-built wooden ark,
gesturing passionately as he explains something to the camera.
The camera follows him handheld-style, moving slightly as he paces.
In the background, groups of villagers are laughing and pointing.
The sky is overcast with low clouds, and goats wander through muddy terrain.
The man wears worn, ancient robes,
his feet muddy, his tone frustrated but hopeful.
The scene has a documentary-style realism, with soft golden highlights breaking through cloud gaps.
Light wind moves fabric and rustles dry straw around the ark.
Shot in cinematic 24fps with natural tones and slight vintage softness.

# chat model : google gemini Model
model : gemini-2.5-flash

# Tool : Think
google sheets –> append row in sheet

Gemini - Create a Video prompt

Schedule Trigger
Edit field (Schedule Trigger cannot trigger Gemini) : Gemini = “run”
Gemini –> message a model - Create a Idea : set follow AI Agent
Code (string to json)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const responseText = $input.first().json.content.parts[0].text

let jsonString = responseText;

// 移除 ```
jsonString = jsonString.replace(/```json\n?/g, '');

// 移除 ```
jsonString = jsonString.replace(/```\s*$/g, '');

// 移除前後空白
jsonString = jsonString.trim();

try {
// 解析 JSON 字符串
const output = JSON.parse(jsonString);

// 回傳解析後的結構化資料
return {
output
};

} catch (error) {
// 解析失敗時的錯誤處理
return {
error: 'JSON format error',
originalResponse: responseText,
parseSuccess: false,
};
}
Gemini –> message a model - Create a Video prompt : set follow AI Agent
google sheets –> append row in sheet

LinkedinPost Generator

n8n form : on form submission
1
2
3
4
5
6
7
8
# set 
Title : LinkedIn Post Generator
Description : Provide the details to create a captivating LinkedIn post

# input
Email: ...
Post Topic : 3 amazing facts about artificial intelligence
Target Audience: people who are beginners in AI
LinkedIn Post Destcription AI Agent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
tool : HTTP Request(name -Search Web)
Method: POST
URL: https://api.tavily.com/search
Authentication: Generic Credential Type
Generic Credential Type: Header Auth
Send Body: Enabled
Body Content Type : JSON
Specify Body: Using JSON
JSON:
{
"query": "{searchTerm}",
"topic": "general",
"search_depth": "advanced",
"chunks_per_source": 3,
"max_results": 1,
"time_range": null,
"days": 7,
"include_answer": true,
"include_raw_content": false,
"include_images": false,
"include_image_descriptions": false,
"include_domains": [],
"exclude_domains": []
}
Chat model : Google Gemini Chat model
1
2
3
# 2nd (prompt put to this)
Gemini --> message a model
tool : HTTP Request(name -Search Web)
Create Image Prompt
1
Chat model : Google Gemini Chat model
1
2
# 2nd (prompt put to this)
Gemini --> message a model
Generate Title
1
Chat model : Google Gemini Chat model
1
2
# 2nd (prompt put to this)
Gemini --> message a model
OpenAI –> Generate an image
1
2
3
Model : DALL-E3 (more cheap)
Quality : Standard
Resolution : 1024*1024
goopgle Drive –> upload file
1
add File name, Folder 
Gmail –> send a message
1
2
3
4
5
6
7
8
9
10
11
subtitle : Your LinkedIn Post is published
message :
Hello!

Here are details for your linked post:

Title :{{ $('Generate Title').item.json.output }}

Description:{{ $('LinkedIn Post Destcription AI Agent').item.json.output }}

Image URL: {{ $json.webViewLink }}

Automation to Manage and Classify Gmail

Gmail Trigger
1
2
Mode :every minite
Simplify : disable(because we want to access all email data)
Information Extractor
1
2
3
4
5
6
7
tool: gemini chat model

Attributes: senderName
Extract the sender's name from this email, if no name, return an empty string.

add option --> System Prompt Template(use default)
You are an advanced extraction tool. Only pull out the relevant details from the text. If you're unsure about the value of any requested attribute, leave it as an empty string.
if
1
2
Condition : {{ $json.output.senderName }} (string) is not empty 
- Merge output type::Keep Non-Matches - 輸出的就是那一些「只出現在某一個來源,但在另一個來源沒有對應資料」的項目
Edit Field + Merge
1
2
3
4
5
# 1st 
intro (string) Dear {{ $json.output.senderName }},

# 2nd
intro (string) Hi'
Edit Field(Clean Data)
1
2
3
4
message_id:{{ $('Gmail Trigger').item.json.id }}
threadId:{{ $('Gmail Trigger').item.json.threadId }}
text:{{ $('Gmail Trigger').item.json.text }} - maill 內容
intro:{{ $json.intro }}
Text classifier
1
2
3
4
5
6
7
8
9
10
11
12
tool: gemini chat model

Text to Classify:{{ $json.text }}

Categories:
Services requests : Questions about consulting services offered, rates, or availability.
Consultation requests : Scheduling initial consultations or follow-up meetings.
Payments : Questions about invoices, payments, or refunds
Others : Emails that don’t clearly fit into the other categories.

System Prompt Template :
Please classify the text provided by the user into one of the following categories: {categories}, and use the provided formatting instructions below. Don't explain, and only output the json.
Gmail –> add labbel to message
1
2
3
# Gmail add Label 1st
Message ID : {{ $('Clean Data').item.json.message_id }}
Label Names or IDs : <select label>
Gmail –> Replay to a message
1
2
3
Message ID : {{ $('Clean Data').item.json.message_id }}
Email Type:text
Message: {{ $('Text Classifier').item.json.intro }}: ...

Make Better Think

chat trigger
Gemini –> Message a model
1
2
3
4
5
6
7
8
9
10
11
#tool:Think
Think tool Description: ### Overview This tool should...

Model: gemini-2.5-flash
Prompt:
# Overview
You are a helpful assistant
# Instructions
When you create any piece of written content you use a "think node" to make it human-like {{ $json.chatInput }}

Simplity enable
sheet –> append row in sheet
1
2
3
4
5
Docuemnt: Think tools Test
time: {{ $now.format('yyyy-MM-dd hh:mm') }}
workflow: No Agent
Input: {{ $('When chat message received').item.json.chatInput }}
output:{{ $json.content.parts[0].text }}

AI story

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# chat trigger(input example)
crowded city in the middle of the day, a lot of people, cozy atmosphere

# gemini --> Message a model
Prompt : ...
simpllify Output: enebale
Output Content as JSON

# OpenAI --> Generate an image
Model: GPT Image 1
Prompt: ..
Quality: Medium
Resolution: 1536*1024(寬畫面)

# google drive --> upload(image)
Filename: image_{{ $('When chat message received').item.json.sessionId }}
input Data Field Name: data

# google drive --> download (因要存起來upload then download)
File by URL : {{ $json.webViewLink }}

# save image to freeimage host
Method : POST
URL: https://freeimage.host/api/1/upload
Body :
key: <your key>
action : upload
source(n8n Binary File): data
format : json

# Pollo generate video by runway gen 4
Method:POST
URL: https://pollo.ai/api/platform/generation/runway/runway-gen-4-turbo
Body using JSON:
{
"input": {
"image": "{{ $json.image.url }}",
"prompt": {{ $('Generate Prompt').item.json.content.parts[0].text }},
"length": 5,
"aspectRatio": "16:9",
"seed": 123
}
}

# Merge

# Wait 5s

# get video status
Method: GET
URL: https://pollo.ai/api/platform/generation/{{ $json.data.taskId }}/status


#if
{{ $json.data.generations[0].status }} "is equal to" Success
false link to Merge

# get Video
Method: GET
URL: {{ $json.data.generations[0].url }}

# google drive --> upload(video)
Filename: video_{{ $('When chat message received').item.json.sessionId }}
input Data Field Name: data

# Generate Audio by Speechify
Method: POST
URL: https://api.sws.speechify.com/v1/audio/speech
Body :
{
"input": {{ $('Generate Prompt').item.json.content.parts[0].text }},
"voice_id": "george",
"audio_format": "mp3"
}

#Code(Convert to File)
return [
{
json: {},
binary: {
data: {
data: Buffer.from($json["audio_data"], "base64"),
mimeType: "audio/mpeg",
fileName: "output.mp3"
}
}
}
];

# google drive --> upload(audio)
Filename: audio_{{ $('When chat message received').item.json.sessionId }}
input Data Field Name: data

# save to sheet(append row in sheet)
chat: {{ $('When chat message received').item.json.chatInput }}
prompt: {{ $('Generate Prompt').item.json.content.parts[0].text }}
image: {{ $('Save Image').item.json.webViewLink }}
video: {{ $('Upload file').item.json.webViewLink }}
audio: {{ $json.webViewLink }}

Viral Youtube Posting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# Schedule trigger 
Trigger Interval: Custom(Crop) - 自訂
Exppreesio : 0 15 * * 2,5 - Tue,Fri 13:00

# http Request - Get The Latest Youtube Video
Methold : GET
URL: https://www.googleapis.com/youtube/v3/search
Send Query Parameters : enable
Specify Query Parameters : Using Fields Below
part:snippet
channelId: <your youtube channel id>
maxResults: 1
order: date
type: video
key : <your googleAPI key>

# http Request - Get Transcript(use Apify)
Methold : GET
URL: https://api.apify.com/v2/acts/pintostudio~youtube-transcript-scraper/run-sync-get-dataset-items
Send Query Parameters : enable
Specify Query Parameters : Using Fields Below
token: <your Apify token>
Send Body: enable
JSON --> Using JSON
{
"videoUrl": "https://www.youtube.com/watch?v={{ $json.items[0].id.videoId }}"
}

# AI Agent - Facebook Post
Prompt (User Message) : ..
System Message: ...
tool : gemini chat model(gemini-2.5-flash)

# AI Agent - Email Campaign
Prompt (User Message) : ..
System Message: ...
tool : gemini chat model(gemini-2.5-flash)

# AI Agent - Skool Annoncement
Prompt (User Message) : ..
System Message: ...
tool : gemini chat model(gemini-2.5-flash)

# Notion --> Create a database page
Database: sellect for you(eg. Youtube post #1)
Title: empty
Add property:
Video Name : {{ $('Get The Latest Youtube Video').item.json.items[0].snippet.title }}
Facebook Post: {{ $('Facebook Post').item.json.output }}
Email Campaign: {{ $('Email Campaign').item.json.output }}
Skool Announcement: {{ $json.output }}

Crecte Socia Media in Seconds

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# google sheet --> on row added
Document sheet --> on row added

#if
Conditions : {{ $json.Done }} is empty

# Set Field
query : {{ $json['Content Subject'] }}
tragetAudience : {{ $json['Target Audience'] }}

# http request
# replace(/"/g, '\\"') :把所有雙引號 " 取代為已轉義的 ",確保字串在 JSON、JS 字面量或序列化時不會因未轉義引號而破壞語法
Method : POST
URL: https://api.tavily.com/search
Body enable
# {
# "query": "{{ $json.query.replace(/"/g, '\\"') }}",
# "search_depth": "basic",
# "include_answer": true,
# "topic": "news",
# "include_raw_content": true,
# "max_results": 3
# }

# Split Out
Fields To Split Out:results

# Aggregate
include: Specified
Field:title, raw_content

# AI Agent(LikedIn)
Prompt:..
System Message:..
tool: gemini chat model

# AI Agent(Facebook)
Prompt:..
System Message:..
tool: gemini chat model

# AI Agent(Blog Post)
Prompt:..
System Message:..
tool: gemini chat model

# Google sheet --> update row in sheet
Column to match on : Campaign
Campaign (using to match) : {{ $('If').item.json.Campaign }}
Done : complete
Linkdein:{{ $('LinkedIn').item.json.output }}
Facebook:{{ $('Facebook').item.json.output }}
Blog:{{ $json.output }}

Building an AI Agent Instruction From Scratch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Chat Trigger
Meaages prompt: {{ $json.chatInput }}
System message :
## Purpose
You are a professional executive assistant. Your job is to complete the task you are requested to do from the user.

## Capibilities
- always when it's requested to perform some task that requested dates to use, you can use this time as the reference, what time is it {{ $now }}
- always usee tool/tools to perform a task, if it's need

## Tools
- Send a message in Gmail - use this tool to send emails
- Create an event in Google Calendar - use this tool to schedule the meetings that user requests.
- Get Lables from Gmail - get all labels from my gmail account. Make sure to record all the labels names properly so later you can use it for other steps
- Add label to message in Gmail - use this tool to add label to a proper message in gmail. Make sure you are using a proper label name. Every time use the id of a label to match it, instead of the label name.
- Append row in sheet in Google Sheets - use it to save email information to google sheets. whenever you use this took first, get emails and then then use this tools to save information

## Interation Style
- when you send emails, keep professional and funny tone, in the responses

## Notes
- every time make sure you use the tool to complete the task

# Gemini --> Message a model
# Tools - Google Calendar Tool(just login)
select Calendar
# - Gmail tool(send a message in Gmail)
Resource : message + Send
# - Gmail tool(Get many labels in Gmail)
Resource : label + Get many
# - Gmail tool(Get many messages in Gmail)
Resource : message + Get many
# - Gmail tool(Add label to message in Gmail)
Resource : message + Add Label
# - Google Sheets Tooll(Append row in sheet in Google Sheets)
Operation: Append Row
Document:Email information

# chat
Please schedule an event for me at 8 pm, it should last 1 hour
please get all my gmail's label
Take the latest receive email and save the information inside the Google Sheet

Create an AI Agent Instructions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# chat trigger

# gemini model
prompt: {{ $json.chatInput }}
system message :
## Purpose
You are a professional virtual assistant. Your job is to accomplish the task for the user.

## Capabilities
- If you need in some way to know what the current date is, you can use this date as a reference: {{ $now }}
- You use AI to structure the content in a nice format and provide comprehensive information about the query.

## Tools
- Get News - use this tool to fetch the latest news from the internet
- Create a Document in Google Docs - use this to create a black document docs, that contains only the title
- Update a Document in Google Docs - use this tool to update a blank document. Insert inside the content you create with AI
- Send an email in Gmail - use this tool to send an email to a user

## Interaction style
- keep the tone processional, and the format really format

## Notes
- When you create a blank document, always in the title include the followiing form: The News for [query] - [current date]
- When you get your news from "get news", use AI to format it this way, it can be nicely inserted inside Google Docs. so bascicaly choose the proper format, so we have heads, bolded parts and the necessary format.
- Every time before sending an email action, use AI to format an email in HTML format. Include headings, bolded parts, italicized, and generally the best HTML formatting

# Tools - Gmail Tool(Send a message in Gmail)

# Tools - Google Docs Tool(Create a document in Google Docs)
Folder Name: Demo

# Tools - Google Docs Tool(Update a document in Google Docs)

# Tools - HTTP Request Tool(Get News(Tavily))
Method: POST
URL: https://api.tavily.com/search
Send Mody Enable
{
"query": "{{ $fromAI('parameters0_Value') }}",
"search_depth": "basic",
"include_answer": true,
"topic": "news",
"include_raw_content": true,
"max_results": 3
}

# Tools - option HTTP Request Tool(Get News(Serper))
Method: POST
URL: https://google.serper.dev/search
Send Mody Enable
Specify Body: Using Fields Below
q: <set auto>

# chat
Please get me the news about the "top 5 world news". Use AI to format the information in a nice way, then create a blank document, and after update it with information from AI(and translate to Traditional Chinese). And at the end, send the email with information to me: kyp001@yahoo.com.tw

# another - prompt
Role & Purpose
You are a customizable news-gathering and reporting AI agent. Your purpose is to fetch world news based on a given topic or category, format them beautifully in Google Docs, translate the summary into Traditional Chinese, and send the final report directly in an HTML email to a user-specified address.

Capabilities
- Accept user input for news topic (e.g., "top 5 world news" or any keyword).
- Fetch related news using Tavily News API.
- Summarize and format the news in a clear, professional style.
- Translate all summaries into Traditional Chinese.
- Create a blank Google Document with a clean layout (titles, sections, spacing, and styled text).
- Update the document with first the full English version, followed by the full Traditional Chinese version.
- Send the completed report via Gmail in HTML format (email body content, not just a link).
- current time is {{ $now }}

** Tools **
- Tavily News – Get the latest news by topic or category.
- Google Docs (Create) – Start a new blank document.
- Google Docs (Update) – Insert formatted content with styled sections.
- Gmail – Send the final news report as HTML email directly to recipient.

Interaction Style
- Write concise, professional summaries with structured sections.
- Use headings, bold text, spacing, and lists to ensure readability.
- Present all English summaries first, then all Traditional Chinese translations.
- Maintain a consistent, formal, and polished style across both outputs.

Notes
- Default to top 5 items unless user specifies otherwise.
- Ensure Google Docs formatting uses native styling (no markdown).
- Email body must be HTML with proper headings, lists, and spacing.
- Always send to the user-specified email; confirm if not provided.

# chat
give me the "Ukraine war" news and send to kyp001@yahoo.com.tw

wait workflow

  • if length limite add flow for it : Telegrame, Notion
  • Line Bot
  • Audio Transcribe : NotebookLM
  • scan youtube some channge upgrade, down mp3 file
  • google API how to set project, cost or limit ?
  • gemini limite?

Ref