Google Sheets onEdit trigger: call an API when a row changes
A simple onEdit trigger can't call UrlFetchApp, which needs authorization. Use an installable edit trigger to call an API from a Sheets edit.

A simple onEdit(e) trigger in Google Sheets runs when a user changes a cell, but it can't use services that require authorization, and UrlFetchApp requires the script.external_request scope, so an API call from it fails. Use an installable edit trigger instead: it can call services that require authorization and runs under the account of the person who created it.
Google's rules below are quoted from its simple triggers and installable triggers guides, and the API side from Sume's Create a run docs, all read on 2026-09-28. Sume has no Google Sheets add-on; the handler makes a plain HTTPS call.
Why doesn't my onEdit trigger call the API?
Simple triggers fire without asking the user for authorization, so Google restricts what they can do. The installable version lifts the authorization limit but keeps the rule that matters most for a row-driven workflow: only a person's edit fires it.
e.valueis set only when a single cell was edited, so a paste over several cells won't match the check in the code below.- Because script writes don't fire triggers, the handler can write the returned run id back into the row without firing itself again.
- Rows written by another script or through an API request won't fire either trigger. For those, poll on a time-driven trigger as in Google Sheets to video automation with Apps Script.
- For structure changes such as a new sheet or a removed column, Google offers an installable change trigger; its event carries a
changeTypesuch asINSERT_ROW.
| Rule | Simple `onEdit(e)` | Installable edit trigger |
|---|---|---|
| Services that require authorization | Can't access them | Can call them |
| Whose authorization | None: it fires without asking the user | The account of the person who created it |
| Runtime | 30 seconds at most | Script runtime quota: 6 min / execution |
| Fired by script executions and API requests | No | No |
| File opened read-only (view or comment) | Doesn't run | Doesn't run |
How do I set up an installable edit trigger?
In the Apps Script editor, click Triggers, then Add Trigger, pick your function and the spreadsheet's on-edit event, and save. Or run ScriptApp.newTrigger("onRowEdit").forSpreadsheet(SpreadsheetApp.getActive()).onEdit().create() once, as in Google's SpreadsheetTriggerBuilder reference. Store the API key as a script property, not in a cell; script properties are shared by all users of the script, and Sume's Authentication docs say keys stay out of frontend JavaScript and screenshots.
The handler below runs when someone types go in column D of a Videos tab. Column A holds a stable row id, B the topic, C a version number you bump to make a new video on purpose.
const RUNS_URL = "https://api.sume.com/v1/formats/acme/daily-short/runs";
function onRowEdit(e) {
const sheet = e.range.getSheet();
if (sheet.getName() !== "Videos" || e.range.getColumn() !== 4 || e.value !== "go") return;
const row = e.range.getRow();
const [id, topic, version] = sheet.getRange(row, 1, 1, 3).getValues()[0];
const res = UrlFetchApp.fetch(RUNS_URL, {
method: "post",
contentType: "application/json",
headers: {
Authorization: "Bearer " + PropertiesService.getScriptProperties().getProperty("SUME_API_KEY"),
"Idempotency-Key": "videos-" + id + "-v" + version,
},
payload: JSON.stringify({ input: { topic: topic }, generation_spend_cap_usd: 5 }),
muteHttpExceptions: true,
});
const body = JSON.parse(res.getContentText());
sheet.getRange(row, 5).setValue(body.data ? body.data.id : body.error.code);
}What should the handler send to the API?
One POST /v1/formats/{handle}/{slug}/runs, with the row's cells as data. Sume answers 202 with the run's receipt while the run is still queued, so the handler stores data.id and stops.
- Put the cells in
input, a JSON object of caller data up to 64 top-level keys. You choose the shape and the Format reads the keys it knows.acme/daily-shortstands in for your own Format. - Build the
Idempotency-Keyfrom the row, not the moment. Sume's docs say to derive it from the thing being made plus a version you bump. Retypinggoon an unchanged row sends the same key and body and gets200with the original run: no second run, no second charge. - If someone edits the topic but not the version, the same key arrives with a different body and Sume answers
409 idempotency_conflict, and nothing runs. The code writes that code into column E, the cue to bump the version. - Set
contentTypetoapplication/json: UrlFetchApp defaults toapplication/x-www-form-urlencoded. WithmuteHttpExceptionson, a failure returns the response instead of throwing. - Send
Authorization: Bearerorx-api-key, never both: a request with two credentials is401 unauthorized.
How do I get the finished video back into the sheet?
Not from the edit trigger, which gets at most 6 minutes per execution; Sume's docs put long-form video at 15 to 30 minutes of work. Poll GET /v1/format-runs/{run_id} for the stored ids from a time-driven trigger and write primary_output_url when the run is completed (Runs and results). That URL is a durable media.sume.com file, public to anyone holding it, so anyone who can read the sheet can open the video. The published bulk Sheets post has a polling function you can reuse, and explains why an Apps Script web app is a poor webhook target.
Sources
- Google Apps Script: Simple triggers (read 2026-09-28)
- Google Apps Script: Installable triggers (read 2026-09-28)
- Google Apps Script: Event objects (read 2026-09-28)
- Google Apps Script: Class SpreadsheetTriggerBuilder (read 2026-09-28)
- Google Apps Script: Class UrlFetchApp (read 2026-09-28)
- Google Apps Script: Properties Service (read 2026-09-28)
- Google Apps Script: Quotas for Google Services (read 2026-09-28)
- Create a run
- Runs and results
- Authentication
Related posts
More in Integrations
- Hermes Agent MCP server: add Sume in config.yaml
Add Sume's hosted MCP server to Hermes Agent under mcp_servers in config.yaml, with an API-key header or OAuth, and paid tools behind approval.
- HeyGen MCP server: URL, setup for Claude, and credits
HeyGen MCP runs at mcp.heygen.com/mcp/v1/. Add it to Claude Code or Claude, sign in with OAuth, and videos use your HeyGen plan's credits.
- Higgsfield MCP: server URL, setup, and what it costs
Higgsfield MCP runs at mcp.higgsfield.ai/mcp. Sign in with a Higgsfield account, no API key; each generation spends your plan's credits.
- Hono webhook: verify the signature on the raw body
Read the raw body with c.req.text(), verify the HMAC signature, then JSON.parse that string and answer 204. Works on Workers, Bun, Deno and Node.
Written by Sume