コンテンツにスキップ

Google Forms と Apps Script

Sheets アドオンは、生成 をクリックしたときにドキュメントを作ります。誰もクリックしなくてもドキュメントを作るには(たとえばフォームの回答ごとに証明書を作る、行にチェックが入ったら見積書を作るなど)、短い Apps Script でデータを AutomaDoc のドキュメント API に送り、できたファイルを好きなように扱います。Drive に保存する、メールで送る、シートにリンクを書き込むといった使い方ができます。

スクリプトはあなたの Google アカウントで、Google の通常の Apps Script のルールに従って実行されます。AutomaDoc が受け取るのは、スクリプトが送る JSON だけです。

  1. テンプレート。 AutomaDoc のテンプレートならどれでも使えます。スクリプトが送る JSON がレコードになるため、テンプレート自体のデータソースは使われません。プレースホルダーは、スクリプトが送るキーから名付けます(名前を参照)。
  2. テンプレート ID。 会社ポータルで テンプレート を開き、テンプレートのメニューを開いて テンプレート ID をコピー を選びます。
  3. API キー。 管理者が会社ポータルの API キー で作成する会社 API キー(amk_ で始まり、一度だけ表示されます)、または Lark Base のテンプレートなら、そのテンプレートだけを開けるテンプレート自身のキー。キーの範囲は狭いほど安全です。スクリプトを編集できる人なら誰でもキーを読めるためです。

レシピ:フォームの回答ごとにドキュメントを作る

Section titled “レシピ:フォームの回答ごとにドキュメントを作る”

このスクリプトはフォームにバインドされます。回答ごとに PDF を作り、Drive のフォルダーに保存して、回答した人にメールで送ります。

  1. Google フォームでフォームを開き、⋮ メニューを開いて Apps Script を選びます。
  2. Apps Script エディタで プロジェクトの設定 を開き、AUTOMADOC_API_KEY という名前のスクリプト プロパティを追加して、値にキーを入れます。こうすればキーがコードに含まれません。
  3. Code.gs の内容を下のスクリプトに置き換え、TEMPLATE_ID と FOLDER_ID(Drive フォルダーのアドレスの /folders/ より後の部分)を入力します。
  4. 関数の一覧で setUp を選び、実行 を一度クリックします。Google から、外部サービスへの接続、Drive の使用、あなたとしてのメール送信をスクリプトに許可するよう求められます。

以後、送信のたびに handleSubmit が実行されます。実行結果とエラーは、エディタの 実行数 に一覧表示されます。

const AUTOMADOC_API = 'https://api.automadoc.cc';
const TEMPLATE_ID = 'PASTE-THE-TEMPLATE-ID';
const FOLDER_ID = 'PASTE-THE-DRIVE-FOLDER-ID';
/** Run once: creates the trigger that calls handleSubmit on every response. */
function setUp() {
ScriptApp.newTrigger('handleSubmit')
.forForm(FormApp.getActiveForm())
.onFormSubmit()
.create();
}
function handleSubmit(e) {
const response = e.response;
// One key per question: its title is the placeholder's name.
const data = {};
response.getItemResponses().forEach(function (itemResponse) {
data[itemResponse.getItem().getTitle()] = answerOf(itemResponse.getResponse());
});
data['Submitted'] = Utilities.formatDate(
response.getTimestamp(), Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm');
const doc = createDocument(data);
const file = DriveApp.getFolderById(FOLDER_ID).createFile(downloadFile(doc));
console.log('Saved ' + file.getName() + ': ' + file.getUrl());
// Needs "Collect email addresses" in the form's settings.
const email = response.getRespondentEmail();
if (email) {
MailApp.sendEmail({
to: email,
subject: doc.filename,
body: 'Thank you. Your document is attached.',
attachments: [file.getBlob()],
});
}
}
/** Grid questions answer with a list of lists, which the API refuses: one text instead. */
function answerOf(value) {
if (Array.isArray(value) && value.some(Array.isArray)) {
return value.map(function (row) { return [].concat(row).join(', '); }).join('; ');
}
return value;
}
/** Fills the template and waits until the document is finished. */
function createDocument(data) {
let doc = callApi(AUTOMADOC_API + '/api/v1/templates/' + TEMPLATE_ID + '/documents?wait=20', {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify({ data: data, format: 'pdf' }),
});
// Not finished within the 20 seconds: read its status every 5 seconds, for up to 4 minutes.
for (let i = 0; doc.status === 'processing' && i < 48; i++) {
Utilities.sleep(5000);
doc = callApi(doc.statusUrl);
}
if (doc.status !== 'completed') {
throw new Error(doc.error || 'The document is still being made: ' + doc.statusUrl);
}
if (doc.warnings) console.log(doc.warnings.join('\n'));
return doc;
}
/** The finished file, as a blob named like the document. */
function downloadFile(doc) {
const response = UrlFetchApp.fetch(doc.statusUrl + '/file', {
headers: { 'X-Webhook-API-Key': apiKey() },
muteHttpExceptions: true,
});
if (response.getResponseCode() !== 200) {
throw new Error('Download failed: ' + response.getContentText());
}
return response.getBlob().setName(doc.filename);
}
function callApi(url, options) {
options = options || {};
options.headers = { 'X-Webhook-API-Key': apiKey() };
options.muteHttpExceptions = true;
const response = UrlFetchApp.fetch(url, options);
const body = JSON.parse(response.getContentText());
if (response.getResponseCode() >= 400) throw new Error(body.code + ': ' + body.error);
return body;
}
function apiKey() {
return PropertiesService.getScriptProperties().getProperty('AUTOMADOC_API_KEY');
}

各呼び出しで行われること:

  • POST /api/v1/templates/{templateId}/documents?wait=20 で { "data": …, "format": "pdf" } を送ります。wait を付けると、ドキュメントが完成したとき(200)か、20秒後(202、まだ processing)に応答が返ります。format には "native"(テンプレート自体の Word・Excel・PowerPoint 形式)や "png" も指定でき、filename でこの呼び出しのファイル名パターンを設定できます。
  • GET /api/v1/documents/{generationId}(応答の statusUrl)で status を読み取ります。値は processing、completed、failed のいずれかで、失敗の理由は error に入ります。完了したドキュメントには filename と downloadUrl があります。
  • GET /api/v1/documents/{generationId}/file は、同じキーでファイルそのものを返します。

リンクだけが必要なら、downloadFile は省略できます。doc.downloadUrl はキーなしでファイルを開け、会社が生成したドキュメントを保持する期間(無期限に保持する設定なら30日)有効です。ステータスを読み取るたびに新しいリンクが返ります。

レシピ:シートのチェックボックスにチェックが入ったらドキュメントを作る

Section titled “レシピ:シートのチェックボックスにチェックが入ったらドキュメントを作る”

同じヘルパーはスプレッドシートからも使えます。スプレッドシートを開いて 拡張機能 → Apps Script を選び、スクリプト プロパティを追加して、上のヘルパー(createDocument、downloadFile、callApi、apiKey と3つの定数)を貼り付け、setUp と handleSubmit の代わりに次のコードを入れます。

この例のタブ名は Orders です。1行目に列名があり、Generate という名前のチェックボックス列でドキュメントの作成を開始し、Document という名前の列にそのリンクが入ります。

const SHEET_NAME = 'Orders';
/** Run once: creates the trigger that calls handleEdit on every edit. */
function setUp() {
ScriptApp.newTrigger('handleEdit')
.forSpreadsheet(SpreadsheetApp.getActive())
.onEdit()
.create();
}
function handleEdit(e) {
const sheet = e.range.getSheet();
// Only a single checkbox ticked on a data row of the right tab
if (sheet.getName() !== SHEET_NAME || e.range.getRow() < 2 || e.value !== 'TRUE') return;
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getDisplayValues()[0];
if (headers[e.range.getColumn() - 1] !== 'Generate') return;
const row = e.range.getRow();
const values = sheet.getRange(row, 1, 1, headers.length).getDisplayValues()[0];
const data = {};
headers.forEach(function (header, i) {
if (header && header !== 'Generate' && header !== 'Document') data[header] = values[i];
});
const doc = createDocument(data);
const file = DriveApp.getFolderById(FOLDER_ID).createFile(downloadFile(doc));
const linkColumn = headers.indexOf('Document') + 1;
if (linkColumn > 0) sheet.getRange(row, linkColumn).setValue(file.getUrl());
}

トリガーは、setUp が作成するインストール型トリガーである必要があります。onEdit という名前の関数はシンプルトリガーとして実行され、Google はシンプルトリガーからの外部サービスの呼び出しを許可していません。

getDisplayValues() は各セルをシートに表示されているとおりに送るため、数値はシートで書式設定されたテキストとして届きます。ある列に number、currency、sum などのフィルターを使うには、その列の値を代わりに getValues() から送ってください。

各キーは、列名やフィールド名と同じようにプレースホルダー名になります。スペース、ドット、そのほかの記号は _ になり、あらゆる言語の文字と数字はそのまま残り、大文字と小文字は区別されます。

質問または列 プレースホルダー
Full name {Full_name}
Invoice No. {Invoice_No_}
Submitted(スクリプトが追加) {Submitted}

文字も数字も含まないタイトルは拒否され(DATA_INVALID_NAME)、同じ名前になる2つのタイトルも拒否されます(DATA_NAME_COLLISION)。チェックボックスの質問はリストで回答し、カンマ区切りで出力されます。

2026-10-17 09:30 のように、タイムゾーンを付けずにテキストとして送った日付は、テンプレートのタイムゾーンで読み取られます。そのため、date フィルターで書式を変えられます。

画像 は公開された Web アドレス(https:// または http://)で、テンプレートにプレースホルダー画像がある位置に送る必要があります。フォームの質問からアップロードされたファイルは、フォームの所有者の Drive にある非公開のファイルです。AutomaDoc はそれを取得できず、回答にはファイルの ID しか入りません。そうした質問はテンプレートに含めないでください。

テンプレートごとのリクエスト 1分間に60回
会社ごとのドキュメント すべての自動化の合計で1分間に600件
ドキュメントごとのステータスの読み取り 1分間に60回。ファイルのダウンロードは別に60回
wait 最大25秒
リクエストの本文 1 MB
1つの値の文字数 100,000

拒否されると code と error が返り、上のスクリプトはその両方を 実行数 のログに残して停止します。RATE_LIMITED(429)は、1分間の呼び出しが多すぎることを意味します。一度に多くの回答を受け取る可能性があるフォームでは、このエラーを捕捉し、少し後で再試行してください。すべてのコードは API リファレンスに記載しています。

Google はスクリプトの1回の実行を6分で停止します。スクリプトが4分でステータスの読み取りをやめるのはそのためです。その時点でまだ作成中のドキュメントが失われることはありません。エラーに含まれる statusUrl は引き続き応答します。

この方法で作ったドキュメントは自動生成ドキュメントです。まず Business シートの自動生成の枠から、次に月間の無料枠から、その次にドキュメントパックから差し引かれます。3つとも使い切ると、API は QUOTA_EXCEEDED を返します。Free プランでは、会社が購入したパックを持っていない限り、「Generated with AutomaDoc」のフッターが入ります。