Wat is een incoming webhook?
Een incoming webhook is een URL waarnaar je een HTTP POST-verzoek kunt sturen om automatisch een bericht in een Google Chat Space te plaatsen. Elke applicatie of dienst die HTTP-verzoeken kan versturen, kan zo berichten sturen naar Chat. Je hoeft hiervoor geen volledige Chat-app te bouwen.
Veelvoorkomende toepassingen:
- Monitoring-alerts (server down, hoge CPU)
- CI/CD-notificaties (build geslaagd of mislukt)
- Sales-notificaties (nieuwe lead binnengekomen)
- Geautomatiseerde dagelijkse rapporten
- Integraties met externe tools (GitHub, Jira, PagerDuty)
Incoming webhooks zijn een eenvoudige one-way integratie: je kunt berichten sturen naar Chat, maar niet reageren op berichten van gebruikers. Voor tweerichtingscommunicatie heb je een volledige Chat-app (bot) met de Chat API nodig.
Webhook aanmaken
Webhook aanmaken in een Space
- Open de Google Chat Space waaraan je berichten wilt sturen.
- Klik bovenaan op de naam van de Space.
- Kies Apps en integraties.
- Klik op Webhooks (of Webhook toevoegen).
- Geef de webhook een herkenbare naam, bijvoorbeeld
GitHub CI/CD. - Upload optioneel een avatar via een afbeeldings-URL.
- Klik op Opslaan.
- Kopieer de webhook-URL via het menu (drie puntjes) en bewaar deze veilig.
Behandel de URL als een wachtwoord
De webhook-URL bevat een ingebouwde sleutel en token. Iedereen die de URL heeft, kan berichten plaatsen in je Space. Deel de URL nooit publiek en zet hem niet in een openbare repository.
Bericht sturen via de webhook
De payload is altijd JSON. De eenvoudigste vorm is een object met een text-veld.
Curl (command line)
curl -X POST \
'https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=API_KEY&token=TOKEN' \
-H 'Content-Type: application/json' \
-d '{"text": "Automatisch bericht vanuit mijn systeem!"}'
Let op de backslashes aan het einde van elke regel: die zijn nodig zodat het commando als een geheel wordt uitgevoerd.
Python
import requests
import json
from datetime import datetime
WEBHOOK_URL = 'https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=...'
def stuur_chat_bericht(tekst):
payload = {'text': tekst}
response = requests.post(
WEBHOOK_URL,
data=json.dumps(payload),
headers={'Content-Type': 'application/json'}
)
return response.status_code == 200
def stuur_monitoring_alert(service, status, details):
icoon = '\U0001F7E2' if status == 'ok' else '\U0001F534'
tijdstip = datetime.now().strftime('%H:%M:%S')
tekst = f'{icoon} *{service}* status: *{status}*
{details}
_Tijdstip: {tijdstip}_'
return stuur_chat_bericht(tekst)
stuur_monitoring_alert('Webserver', 'offline', 'Reactietijd: time-out na 30s')
Voor opmaak in text gebruikt Chat zijn eigen markup: *vet*, _cursief_ en `code`. Sinds september 2025 ondersteunen tekstwidgets in kaarten ook gewone Markdown.
Kaarten versturen (cardsV2)
Voor rijkere notificaties gebruik je kaarten via het cardsV2-veld:
def stuur_kaart(titel, tekst, knop_tekst=None, knop_url=None):
card = {
'cardsV2': [{
'cardId': 'notificatie',
'card': {
'header': {'title': titel},
'sections': [{
'widgets': [
{'textParagraph': {'text': tekst}}
]
}]
}
}]
}
if knop_tekst and knop_url:
card['cardsV2'][0]['card']['sections'][0]['widgets'].append({
'buttonList': {
'buttons': [{
'text': knop_tekst,
'onClick': {'openLink': {'url': knop_url}}
}]
}
})
response = requests.post(
WEBHOOK_URL,
data=json.dumps(card),
headers={'Content-Type': 'application/json'}
)
return response.status_code == 200
Cards v1 is verdwenen
Gebruik altijd cardsV2. De oude Cards v1-indeling is in 2025 uitgefaseerd. Nieuwe kaarten bouw je dus altijd met de v2-structuur (cardId plus card).
Rate-limiting en betrouwbaarheid
Google Chat hanteert een limiet van ongeveer 1 verzoek per seconde, gedeeld door alle webhooks in dezelfde Space. Het is dus geen aparte teller per webhook: meerdere webhooks in dezelfde Space delen samen die ruimte. Overschrijd je de limiet, dan krijg je een HTTP-status 429 Too Many Requests terug.
Praktische aanpak:
- Verwerk je batches, voeg dan een korte pauze tussen berichten toe (bijvoorbeeld iets meer dan een seconde).
- Vang status
429op en probeer het opnieuw met exponentiële backoff (steeds langer wachten). - Bundel meerdere meldingen liever in één kaart in plaats van veel losse berichten.
Webhook beveiligen
Webhook-URLs zijn gevoelig: iedereen met de URL kan berichten sturen. Beveiligingstips:
- Sla de webhook-URL op in een secrets manager (bijvoorbeeld Google Secret Manager of HashiCorp Vault).
- Gebruik omgevingsvariabelen in je applicatie, nooit hardcoded waarden.
- Roteer de webhook-URL regelmatig door een nieuwe aan te maken en de oude te verwijderen.
- Monitor op een ongewone berichtfrequentie, zodat je misbruik snel opmerkt.
Eén webhook per integratie
Maak per integratie een aparte webhook aan. Zo kun je een specifieke integratie uitschakelen door alleen die webhook te verwijderen, zonder andere koppelingen te verstoren.
Hoeveel berichten kan ik per minuut sturen?
Reken op ongeveer 1 verzoek per seconde, gedeeld door alle webhooks in dezelfde Space. Bij overschrijding krijg je status 429 terug. Voeg bij batches een korte pauze toe en gebruik exponentiële backoff.
Kan een webhook ook bijlagen of bestanden sturen?
Nee, incoming webhooks ondersteunen alleen tekst en kaarten (cardsV2). Voor het uploaden van bestanden heb je een volledige Chat-app met de Chat API nodig.
Wat gebeurt er als de Space verwijderd wordt?
De webhook-URL wordt dan ongeldig en je krijgt een foutmelding (bijvoorbeeld een 404) terug. Bouw daarom foutafhandeling in die mislukte verzoeken detecteert en logt.
Werkt een webhook ook in directe berichten?
In een gewone Space werkt het altijd. In directe berichten (DM) werkt een webhook alleen wanneer alle deelnemers Chat-apps mogen gebruiken. In veel organisaties zet je webhooks daarom in Spaces.
Hoe roteer ik een webhook-URL veilig?
Maak eerst een nieuwe webhook aan, zet de nieuwe URL in je secrets manager, schakel je applicatie om en verwijder daarna pas de oude webhook. Zo blijft de stroom van berichten zonder onderbreking doorlopen.
Hoe voorkom ik dat mijn URL uitlekt?
Bewaar de URL in een secrets manager of omgevingsvariabele, nooit in code of een publieke repository. Lekt een URL toch, verwijder de webhook dan direct en maak een nieuwe aan.