
Google 表單對個人與企業而言,都是一項相當便利的工具,常被用於活動報名、問卷調查、意見回饋等各式情境。
若能在使用者填寫表單後,自動將資料同步至 kintone 應用程式中集中管理,即可大幅減少人工整理與轉錄的成本。
本篇文章將介紹如何透過 Google Apps Script(GAS),在 Google 表單送出時,自動呼叫 kintone REST API,將填答內容新增為一筆 kintone 記錄,並提供一個可直接實作的完整範例。
💡本範例需準備可使用的 kintone 帳號,以及一組 Google 帳號。
整體實作流程可分為以下三個步驟:
本次範例將示範製作一份「活動報名表」,並將其填答內容自動登錄至 kintone。
以下為本範例中,Google 表單題目與 kintone 欄位之對應關係:
| 項目 | Google 表單問題類型 | kintone 欄位類型 | kintone 欄位代碼 |
|---|---|---|---|
| 姓名 | 簡答 | 單行文字方塊 | name |
| 手機號碼 | 簡答 | 單行文字方塊 | phone |
| 電子信箱 | 簡答 | 單行文字方塊 | |
| 報名場次 | 選擇題 | 選項按鈕 | session |
| 如何得知 | 核取方塊 | 複選 | source |
| 如何得知(其他) | 核取方塊 | 單行文字方塊 | source_other |
💡「如何得知(其他)」為 Google 表單中「如何得知」題目勾選「其他」並填寫自訂內容時的答案,會另存至 kintone 的單行文字欄位。
登入 Google 帳號後,進入 Google 表單,依照上述欄位設計建立一份活動報名表。


回到 kintone,建立一個新的應用程式,並新增與 Google 表單對應的欄位。

💡 特別注意「報名場次」與「如何得知」等選項型欄位,其選項值需與 Google 表單中的選項文字完全一致,否則在新增記錄時將發生錯誤。
接著,至 設定 → 自訂/服務整合 → API 權杖 中新增一個 API 權杖,並且賦予其「新增記錄」的存取權限。
此 API 權杖將用於 GAS 呼叫 kintone REST API 時進行身分驗證。

回到 Google 表單,點擊右上角的「更多選項」,選擇 指令碼編輯器(Apps Script)。

進入 Apps Script 專案後,可先調整專案名稱,接著點擊側邊欄的「專案設定」。

在專案設定中,勾選 「在編輯器中顯示 appsscript.json 資訊清單檔案」。

回到編輯器後,打開 appsscript.json,並加入以下 OAuth scopes 設定:
"oauthScopes": [
"https://www.googleapis.com/auth/forms",
"https://www.googleapis.com/auth/forms.currentonly",
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/drive"
],
💡 若追加在 JSON 中的最後一項,請去除結尾的逗號
,。
此設定用於授權 GAS 存取表單內容,以及透過 UrlFetchApp 呼叫外部 API(kintone)。

回到 程式碼.gs 檔案中,貼上並完成以下程式碼,完成後記得儲存。

範例程式碼如下:
/*
* Google 表單與 kintone 連動 - 範例程式碼
* Copyright (c) 2025 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
*/
// ===== 表單題目文字與 kintone 欄位代碼的對應 =====
const FORM_TITLES = {
name: '姓名',
phone: '手機號碼',
email: '電子信箱',
session: '報名場次',
source: '如何得知',
source_other: '如何得知(其他)',
}
// ===== kintone 環境資訊 =====
const KINTONE_DOMAIN = 'EXAMPLE.cybozu.com' // 不要加 https://
const KINTONE_APP_ID = '123'
const KINTONE_API_TOKEN = 'YOUR_API_TOKEN'
// ===== 主程式:表單送出時自動執行 =====
function onFormSubmit(e) {
// 用來存放「題目文字 → 回答」的對照表
const formData = {}
// 取得「這一次送出」的所有題目與回答
const itemResponses = e.response.getItemResponses()
itemResponses.forEach(ir => {
// 表單題目文字
const title = ir.getItem().getTitle()
// 使用者的回答
const response = ir.getResponse()
// ---- 特別處理「如何得知(核取方塊 + 其他)」 ----
if (title === FORM_TITLES.source) {
// 取得此題在表單中設定的「固定選項」
const choiceValues = ir
.getItem()
.asCheckboxItem()
.getChoices()
.map(c => c.getValue())
let sourceValues = [] // 固定選項(給 kintone 多選)
let sourceOtherValue = '' // 其他自填(給單行文字)
response.forEach(value => {
if (choiceValues.includes(value)) {
// 在固定選項中 → 多選欄位
sourceValues.push(value)
} else {
// 不在固定選項中 → 視為「其他」
sourceOtherValue = value
}
})
// 存入 formData
formData[FORM_TITLES.source] = sourceValues
formData[FORM_TITLES.source_other] = sourceOtherValue
return
}
// ---- 其他題目直接存入 formData ----
formData[title] = response
})
// 檢查表單資料長什麼樣(除錯用)
console.log('formData:', formData)
// ===== 組成 kintone record 格式 =====
const record = {
name: { value: formData[FORM_TITLES.name] || '' },
phone: { value: formData[FORM_TITLES.phone] || '' },
email: { value: formData[FORM_TITLES.email] || '' },
session: { value: formData[FORM_TITLES.session] || '' },
source: { value: formData[FORM_TITLES.source] || [] },
source_other: { value: formData[FORM_TITLES.source_other] || '' }
}
console.log('kintone record:', record)
// ===== 呼叫 kintone API 新增記錄 =====
const url = `https://${KINTONE_DOMAIN}/k/v1/record.json`
const payload = {
app: KINTONE_APP_ID,
record
}
const res = UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/json',
headers: {
'X-Cybozu-API-Token': KINTONE_API_TOKEN
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
})
const code = res.getResponseCode()
const text = res.getContentText()
console.log('kintone response:', code, text)
if (code < 200 || code >= 300) {
throw new Error(`kintone API error (${code}): ${text}`)
}
}
以下將針對程式碼的整體結構與關鍵處理邏輯進行說明。
const FORM_TITLES = {
name: '姓名',
phone: '手機號碼',
email: '電子信箱',
session: '報名場次',
source: '如何得知',
source_other: '如何得知(其他)',
}
此物件用來定義 Google 表單題目文字 與 kintone 欄位代碼 的對應關係,後續在解析表單回傳資料時,能避免直接寫死題目文字,提升可讀性與維護性。
此區段用於設定呼叫 kintone REST API 所需的環境資訊,請依實際使用環境替換。
const KINTONE_DOMAIN = 'EXAMPLE.cybozu.com'
const KINTONE_APP_ID = '123'
const KINTONE_API_TOKEN = 'YOUR_API_TOKEN'
onFormSubmit(e)function onFormSubmit(e) {
此函式為 Google 表單送出觸發 的進入點。
當使用者送出表單時,GAS 會自動將事件資訊包裝成 e 物件傳入。
透過 e.response.getItemResponses(),即可取得「本次送出」的所有題目與對應回答。
const formData = {}
程式中會先建立一個 formData 物件,用來暫存「題目文字 → 回答內容」的對照結果,方便後續組合成 kintone 記錄格式。
在 onFormSubmit(e) 中,這段是整個資料取得的核心:
const itemResponses = e.response.getItemResponses()
itemResponses.forEach(ir => {
// 表單題目文字
const title = ir.getItem().getTitle()
// 使用者的回答
const response = ir.getResponse()
e.response.getItemResponses() 取得的是什麼?e 是 Google 表單「送出事件」的 event 物件。e.response 代表「這一次送出」的表單回覆(不是整份表單所有回覆)。getItemResponses() 會回傳一個陣列,每一個元素都是一題的回答物件(ItemResponse)。因此 itemResponses 可以理解為:
本次送出中,每一道題目(Item)及其回答(Response)的集合。
接著便可以透過 itemResponses.forEach(ir => ...) 來處理每一個回答物件(ir)。ir 是單一題目的回答物件(ItemResponse),你可以從中分別取得:
ir.getItem().getTitle()const title = ir.getItem().getTitle()
ir.getItem() 取得此回答對應的題目(Item)getTitle() 取得題目在表單畫面上顯示的「題目文字」ir.getResponse()const response = ir.getResponse()
getResponse() 會回傳「該題型對應的回答格式」,依題目類型不同而不同,常見情況如下:
string)string)string[])在本範例中:
姓名 / 手機號碼 / 電子信箱 / 報名場次 → 回傳字串如何得知 → 回傳包含字串的陣列(就算只勾一個,也仍是陣列)由於 Google 表單核取方塊的回答值是字串陣列(string[]),資料格式和 kintone 複選欄位相同,在所有選項皆一致,且沒有其他自填選項的情況下,其實可以直接使用 getResponse() 的回傳值。
不過,在本範例中有加入「其他」的自填選項,讓填表者可以自行填寫其他項目,因此必須要把自填的值從陣列中拆分出來,後續才能順利更新至 kintone 記錄中。
if (title === FORM_TITLES.source) {
// 取得此題在表單中設定的「固定選項」
const choiceValues = ir
.getItem()
.asCheckboxItem()
.getChoices()
.map(c => c.getValue())
let sourceValues = [] // 固定選項(給 kintone 多選)
let sourceOtherValue = '' // 其他自填(給單行文字)
response.forEach(value => {
if (choiceValues.includes(value)) {
// 在固定選項中 → 多選欄位
sourceValues.push(value)
} else {
// 不在固定選項中 → 視為「其他」
sourceOtherValue = value
}
})
// 存入 formData
formData[FORM_TITLES.source] = sourceValues
formData[FORM_TITLES.source_other] = sourceOtherValue
return
}
本次範例使用到 kintone REST API 的「新增記錄」端點:
POST /k/v1/record.json
程式碼對應段落如下:
const url = `https://${KINTONE_DOMAIN}/k/v1/record.json`
const payload = {
app: KINTONE_APP_ID,
record
}
const res = UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/json',
headers: {
'X-Cybozu-API-Token': KINTONE_API_TOKEN
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
})
const payload = {
app: KINTONE_APP_ID,
record
}
這裡的 payload 會被轉成 JSON 送出,必要參數如下:
app:目標應用程式 IDrecord:要新增的那一筆記錄內容(每個欄位都要符合 kintone 的資料格式)在前面的處理中,已經將 Google 表單整理成 formData,接著必須再轉成 kintone 需要的 record:
const record = {
name: { value: formData[FORM_TITLES.name] || '' },
phone: { value: formData[FORM_TITLES.phone] || '' },
email: { value: formData[FORM_TITLES.email] || '' },
session: { value: formData[FORM_TITLES.session] || '' },
source: { value: formData[FORM_TITLES.source] || [] },
source_other: { value: formData[FORM_TITLES.source_other] || '' }
}
X-Cybozu-API-Tokenheaders: {
'X-Cybozu-API-Token': KINTONE_API_TOKEN
}
存取 kintone 環境需要身份驗證,這裡採用應用程式 API 權杖,僅授權必要的權限(針對報名表應用程式的新增記錄權限)。
contentType 與 payloadcontentType: 'application/json',
payload: JSON.stringify(payload),
contentType 告訴伺服器這是一段 JSONUrlFetchApp.fetch 的 payload 需要以 JSON.stringify() 轉換成字串編輯完程式碼後,請先儲存變更。
接著進入側邊欄的「觸發條件」設定頁面,新增一個觸發條件,設定內容如下:
💡 若發現執行功能沒有
onFormSubmit可選,可能是因為沒有儲存到程式碼變更,請回到編輯器儲存後,再進行觸發條件設定。


點擊儲存後,如果是第一次進行設定,可能會跳出需要登入與授權的對話窗,請選擇專案所在的登入帳號,並信任此應用程式存取表單。




設定完成後就大功告成!可以實際發送表單,確認資料是否正確上傳至 kintone。
點進側邊欄的「執行項目」,可以檢視程式是否有被觸發,以及觸發的記錄內容。
如果有發生錯誤,通常可以在此查看到錯誤訊息,可以利用 console.log() 檢查各階段的資料格式來排除問題。

本篇範例示範了如何僅透過 Google Apps Script(GAS),在 Google 表單送出時直接呼叫 kintone REST API,將填答內容自動登錄至 kintone 應用程式中。
由於不需要另外架設中介伺服器,對開發者而言是一個導入門檻低、實作成本小的選項。
透過這樣的方式,能快速將表單蒐集到的資料整合至 kintone 進行後續管理與運用,期望本篇內容能作為實務上的參考,協助你更有效率地完成系統串接與資料自動化。
iThome鐵人賽