Log GitHub Events to Google Sheets
A deploy and PR history you can filter, without burning Actions minutes to produce it.
Quick Answer
Get a free webhook URL at requex.me, then in your repo go to Settings → Webhooks → Add webhook, paste the URL, and pick the events to track. Connect Google in Settings → Connectors and map columns to fields like {{body.pull_request.title}}. Every event becomes a row — no Actions minutes, no marketplace app.
“When did this start breaking?” and “how many PRs did we ship last sprint?” are both easy questions with annoying answers. GitHub's UI paginates, its search is scoped oddly, and the API needs a script every time. A running sheet answers both with a filter.
You could build that with a scheduled Action that queries the API — and pay for the minutes, and maintain the script. A webhook does it as events happen, for nothing.
Step 1: Create the webhook URL
Grab a URL from requex.me and sign in so it is saved to your account.
Step 2: Add the webhook in GitHub
In the repo, go to Settings → Webhooks → Add webhook:
- Payload URL: your Requex endpoint
- Content type:
application/json— not the form-encoded default, which wraps the whole payload in a singlepayloadfield and makes every template path one level deeper - Secret: generate one; you will use it in step 4
- Events: “Let me select individual events” — pushes, pull requests, workflow runs, releases, whatever you actually want logged
For an organisation-wide view, add the same webhook at the org level instead and every repo reports into one sheet.
Step 3: Map the columns
Which paths you use depends on the event. GitHub sends the event name in a header rather than the body, so start there:
Received at → {{timestamp}}
Event → {{headers.x-github-event}}
Repo → {{body.repository.full_name}}
Actor → {{body.sender.login}}
Action → {{body.action}}
Title → {{body.pull_request.title}}
Branch → {{body.pull_request.head.ref}}
Merged → {{body.pull_request.merged}}
URL → {{body.pull_request.html_url}}A path that does not exist for a given event renders as an empty cell rather than an error, so one field map can serve several event types — a push simply leaves the pull-request columns blank. If you would rather keep them apart, create two webhooks pointing at two tabs.
For workflow runs, the useful paths are {{body.workflow_run.name}}, {{body.workflow_run.conclusion}}, and {{body.workflow_run.head_branch}}. Logging conclusion gives you a failure rate per branch with one pivot table.
Step 4: Verify the signature
GitHub signs every delivery with X-Hub-Signature-256, an HMAC-SHA256 of the raw body using the secret from step 2. In Settings → Auth, choose HMAC, set the header name, pick SHA-256, set the prefix to sha256=, and paste the secret. Anything unsigned is turned away before it reaches the sheet.
More on this: GitHub webhook testing and webhook security best practices.
What the sheet is good for
- Deploy history — a timestamped row per release, so bisecting an incident starts with a filter instead of a scroll
- Review throughput — PRs opened versus merged per person per week, without a paid analytics tool
- Flaky build tracking — group workflow runs by conclusion and branch, and the pattern shows up in one column
- Audit trail — who changed repo settings or pushed to protected branches, in a place auditors can read without GitHub access
Things that bite
The ping event. GitHub sends one ping the moment you save the webhook. It has no action and none of your mapped fields, so it appears as a nearly empty first row. Delete it and carry on.
Payloads are large. A pull-request event is tens of kilobytes. Map only the fields you need — dumping {{body}} into a cell will hit the 50,000-character limit on a busy repo.
Push events are chatty. Every commit to every branch fires one. On an active repo that is thousands of rows a week; select events deliberately rather than ticking “send me everything”.
Related
Start Testing Webhooks Now
Generate your unique URL and test webhooks instantly. Free, no signup.
Open Webhook Tester →