Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

doGet en doPost voor webhooks

Maak een HTTP-eindpunt van je Apps Script met doGet() en doPost() voor het verwerken van webhooks, formulieren en API-verzoeken.

Web app-deployment

Apps Script kun je als web app deployen, zodat het HTTP-verzoeken ontvangt. Daarmee wordt je script een webhook-ontvanger, API-endpoint of server-side verwerker. Twee gereserveerde functies doen het werk: doGet(e) vangt GET-verzoeken op en doPost(e) vangt POST-verzoeken op. Beide krijgen een event-object e mee met de request-gegevens.

Zo deploy je een script als web app

  1. Open je script en kies Deploy > New deployment.
  2. Kies bij Type de optie Web app.
  3. Stel Uitvoeren als in: jij (vaste rechten) of de aanvragende gebruiker.
  4. Stel Wie heeft toegang in. Voor publieke webhooks kies je Anyone.
  5. Klik op Deploy en noteer de deployment-URL.

Let op: elke keer dat je code wijzigt, moet je een nieuwe versie van de deployment publiceren via Manage deployments, anders draait de oude code op de bestaande URL.

doGet implementeren

doGet is handig voor statuschecks, het uitleveren van publieke data of het tonen van een HTML-pagina. Lees de querystring uit via e.parameter.

function doGet(e) {
  const actie = e.parameter.actie || 'status';

  switch (actie) {
    case 'status':
      return antwoordJson({ status: 'ok', tijdstip: new Date().toISOString() });

    case 'data':
      const data = haalPubliekeDataOp();
      return antwoordJson({ succes: true, data });

    case 'formulier':
      return toonFormulier();

    default:
      return antwoordJson({ fout: 'Onbekende actie' });
  }
}

function antwoordJson(data) {
  return ContentService
    .createTextOutput(JSON.stringify(data))
    .setMimeType(ContentService.MimeType.JSON);
}

function toonFormulier() {
  return HtmlService.createHtmlOutputFromFile('formulier')
    .setTitle('Aanvraagformulier');
}
warning

Web apps sturen geen HTTP-statuscodes

ContentService en HtmlService geven altijd HTTP 200 terug. Je kunt dus geen 400 of 401 als statuscode meesturen. Communiceer fouten in plaats daarvan via een veld in de JSON-body, bijvoorbeeld { "fout": "Onbekende actie" }, en laat de client daarop reageren.

doPost implementeren

doPost ontvangt de request-body via e.postData. Bij JSON gebruik je e.postData.contents, bij een gewone form-post lees je e.parameter uit.

function doPost(e) {
  try {
    const contentType = e.postData ? e.postData.type : '';
    let body;

    if (contentType === 'application/json') {
      body = JSON.parse(e.postData.contents);
    } else {
      body = e.parameter;
    }

    valideerWebhookBody(body);
    const resultaat = verwerkWebhookData(body);

    return antwoordJson({ succes: true, ...resultaat });

  } catch (err) {
    console.error('doPost fout: ' + err.message);
    return antwoordJson({ succes: false, fout: err.message });
  }
}

function valideerWebhookBody(body) {
  if (!body || typeof body !== 'object') {
    throw new Error('Ongeldige request body');
  }
}

function verwerkWebhookData(body) {
  console.log('Webhook ontvangen: ' + JSON.stringify(body));
  return { verwerkt: true };
}

Webhook-authenticatie

Een web app met toegang voor Anyone vraagt geen login. Iedereen die de URL kent, kan een verzoek sturen. Beveilig je endpoint daarom met een geheim token dat je in PropertiesService bewaart en bij elk verzoek controleert.

function doPost(e) {
  const geheimToken = PropertiesService.getScriptProperties().getProperty('WEBHOOK_SECRET');
  const ontvangenToken = e.parameter.token || '';

  if (ontvangenToken !== geheimToken) {
    return antwoordJson({ fout: 'Ongeldig token' });
  }

  return verwerkGeverifieerdVerzoek(e);
}
lightbulb

Vergelijk tokens veilig en bewaar geheimen apart

Zet het secret nooit hardcoded in je code, maar in Project Settings > Script Properties. Wil je timing-aanvallen tegengaan, gebruik dan een lengte- plus karaktervergelijking in plaats van een directe string-match, of werk met een HMAC-handtekening zoals hieronder bij GitHub.

Slack webhook-ontvanger

Slack stuurt eerst een url_verification-verzoek waarop je de challenge moet terugkaatsen. Daarna komen events binnen als event_callback.

function doPost(e) {
  try {
    const body = JSON.parse(e.postData.contents || '{}');

    if (body.type === 'url_verification') {
      return antwoordJson({ challenge: body.challenge });
    }

    if (body.type === 'event_callback') {
      verwerkSlackEvent(body.event);
    }

    return antwoordJson({ ok: true });

  } catch (err) {
    console.error(err.message);
    return antwoordJson({ ok: false });
  }
}

function verwerkSlackEvent(event) {
  if (event.type === 'message' && !event.bot_id) {
    const blad = SpreadsheetApp.openById('SHEET_ID').getActiveSheet();
    blad.appendRow([new Date(), event.user, event.text, event.channel]);
  }
}

Formulier-POST verwerken

Een HTML-formulier dat naar je web app post, lever je af in e.parameter. Valideer de velden en geef een nette HTML-bevestiging terug.

function doPost(e) {
  const naam = e.parameter.naam || '';
  const email = e.parameter.email || '';
  const bericht = e.parameter.bericht || '';

  if (!naam || !email || !bericht) {
    return antwoordHtml('<p>Alle velden zijn verplicht.</p>');
  }

  if (!email.includes('@')) {
    return antwoordHtml('<p>Ongeldig e-mailadres.</p>');
  }

  GmailApp.sendEmail('info@bedrijf.nl', 'Contactformulier: ' + naam,
    'Van: ' + naam + ' (' + email + ')

' + bericht);

  return antwoordHtml('<p>Bedankt! Je bericht is ontvangen.</p>');
}

function antwoordHtml(inhoud) {
  return HtmlService.createHtmlOutput('<!DOCTYPE html><html><body>' + inhoud + '</body></html>');
}

GitHub-webhook met handtekeningcontrole

GitHub ondertekent elk verzoek met een HMAC-SHA256-handtekening in de header. Apps Script geeft request-headers niet rechtstreeks door aan doPost, dus de gangbare aanpak is een gedeeld secret valideren op de payload zelf, of GitHub het secret laten meesturen waar je dat kunt.

Een GitHub-webhook opzetten

  1. Deploy je script als web app met Anyone toegang.
  2. Voeg de web app-URL toe als webhook in GitHub via Settings > Webhooks.
  3. Stel een secret in en bewaar dezelfde waarde in PropertiesService.
  4. Bereken in doPost() met Utilities.computeHmacSha256Signature() de handtekening over e.postData.contents en vergelijk die met de meegestuurde waarde.
  5. Parse pas daarna de body met JSON.parse(e.postData.contents).
  6. Verwerk het event op basis van body.action of body.ref.

Gelijktijdige verzoeken en uitvoertijd

Een web app mag per uitvoering maximaal 6 minuten draaien (de runtime-quota van Apps Script). Een GET- of POST-verzoek dat daar overheen gaat, wordt afgebroken. Houd er rekening mee dat de aanroepende kant (een browser of UrlFetchApp) vaak een veel kortere eigen time-out heeft, dus mik op verwerking binnen enkele seconden.

Komen er meerdere webhooks tegelijk binnen die hetzelfde Sheet of dezelfde Property bijwerken, gebruik dan de Lock Service om race-condities te voorkomen.

function veiligToevoegen(rij) {
  const lock = LockService.getScriptLock();
  lock.waitLock(10000);
  try {
    SpreadsheetApp.openById('SHEET_ID').getActiveSheet().appendRow(rij);
  } finally {
    lock.releaseLock();
  }
}
Hoe krijg ik de web app-URL na deployment?

Je ziet de URL in het deployment-scherm direct na publicatie. In een gebonden script kun je de actieve URL ook ophalen met ScriptApp.getService().getUrl().

Heeft doGet toegang tot Session.getActiveUser()?

Alleen als de web app is ingesteld op uitvoeren als de gebruiker die de app opent en die gebruiker is ingelogd met een Workspace-account. Bij toegang voor Anyone of bij anonieme bezoekers geeft getActiveUser() een lege string terug.

Hoe debug ik een web app?

Schrijf met console.log() naar de Cloud-logs, zichtbaar onder Executions in de editor. Test losse functies door ze handmatig aan te roepen met een nagebootst event-object, bijvoorbeeld doGet({ parameter: { actie: 'status' } }).

Hoelang mag een web app-verzoek draaien?

Apps Script staat maximaal 6 minuten per uitvoering toe. De client die het verzoek doet, heeft vaak een kortere eigen time-out, dus reken op snelle responses. Voor langlopend werk sla je de taak op en verwerk je die later via een time-driven trigger.

Waarom krijg ik geen 401 of 400 terug bij een fout?

ContentService en HtmlService antwoorden altijd met HTTP 200. Geef de foutstatus mee in de JSON-body, bijvoorbeeld een veld fout of een boolean succes, en laat de client daarop controleren.

Hoe lees ik request-headers in doPost?

Apps Script geeft custom HTTP-headers niet door aan doPost. Werk in plaats daarvan met een token in de querystring of valideer een HMAC-handtekening over de payload met Utilities.computeHmacSha256Signature().

Met doGet en doPost wordt Apps Script een volwaardig HTTP-endpoint voor webhooks, API's en formulierverwerking. Combineer het met de Lock Service voor thread-veilige verwerking en met PropertiesService voor veilige opslag van geheimen.