GitHub Actions Workflow Logs (Webhook)
Receives GitHub Actions workflow_run webhook events and, on each completed run, downloads its logs and streams them chunked into ~1 MB pieces split at newline boundaries.
Sync Type: Push (Webhook)
Overview
This input ingests GitHub Actions workflow run logs in response to webhook events rather than by polling the GitHub API. When a workflow run completes, GitHub delivers a workflow_run event with action: completed; the input reacts to that event, downloads the run's logs, and emits them as records.
It produces the same record shape as the polling GitHub Actions Workflow Logs input — so the two are interchangeable downstream — but differs in how and when logs are pulled.
Why use the webhook input
- Near real-time. Logs are fetched the moment a run finishes, instead of waiting for the next polling cycle (the polling input syncs on a fixed interval with a small propagation buffer). Expect log records within seconds of a run completing rather than minutes.
- Optimizes GitHub API usage. The polling input repeatedly lists workflow runs across a lookback window on every sync, regardless of whether anything changed. The webhook input makes no list calls — it only calls the API to download logs for runs that actually completed, so it uses far fewer requests against your GitHub REST API rate limit.
- More reliable. Reacting to a delivered event removes the entire class of polling edge cases (cursor windows, sync-boundary gaps, the maximum-job-execution-time bound). Delivery is at-least-once and de-duplicated downstream by record ID.
This is the preferred input when you want to minimize GitHub API calls and maximize reliability. Use the polling input instead if you need to backfill historical runs (see the limitation below) or cannot expose a webhook endpoint.
No backfill
The webhook input only ingests runs that complete after the webhook is configured — webhooks are forward-only and GitHub does not replay past events. It will not backfill logs for runs that already finished. To capture historical runs, use the polling GitHub Actions Workflow Logs input with a Backfill Start Time.
How It Works
- GitHub delivers a
workflow_runwebhook to the input's ingest URL for every run that is requested, in progress, or completed. - The input verifies the delivery's
X-Hub-Signature-256HMAC against the configured Webhook Secret and ignores any event whoseactionis notcompleted. - For a completed run that is in scope (see Scope below), the input downloads the run's log archive from GitHub's CDN, extracts the root-level per-job log files, and emits them chunked into ~1 MB records. Events from out-of-scope repositories are dropped.
Log archives are downloaded from GitHub's signed CDN URLs and do not count against REST API rate limits.
Prerequisites
Before configuring this input, you need:
- A Personal Access Token (PAT) or GitHub App with permission to read Actions logs for the repository — see GitHub authentication docs.
- Classic PAT:
repo(orpublic_repofor public repositories), optionallyactions:read. - Fine-grained PAT:
Actions: ReadandMetadata: Readon the target repository. - GitHub App:
Actions: ReadandMetadata: Readpermissions, installed on the target repository. Note the Client ID, Installation ID, and PEM private key.
- Classic PAT:
- A webhook secret — a strong random string you will configure both on the input (as Webhook Secret) and on the GitHub webhook (as Secret). GitHub uses it to sign each delivery.
- The ability to add a webhook to the repository (or organization) — requires admin access to the repository/org.
Setup
Step 1: Create the input in Monad
- Create a GitHub Actions Workflow Logs (Webhook) input and add it to a pipeline.
- Configure the Scope (the repository whose events you'll accept), Authentication Method, and a Webhook Secret.
- Copy the Pipeline ID from the pipeline details page.
Step 2: Add the webhook in GitHub
- In the target repository, go to Settings > Webhooks > Add webhook (for an org-wide webhook, use the organization's Settings > Webhooks).
- Fill in:
- Payload URL:
https://app.monad.com/api/v2/http/send/{pipeline_id}(replace{pipeline_id}with your Pipeline ID) - Content type:
application/json - Secret: the same value you set as the input's Webhook Secret
- Which events: select Let me select individual events, then check Workflow runs (uncheck everything else)
- Payload URL:
- Click Add webhook.
GitHub will send a ping event on creation; that (and any non-workflow_run event) is acknowledged and ignored. The input begins emitting logs the next time a workflow run completes.
Configuration
Settings
| Setting | Type | Required | Default | Description |
|---|---|---|---|---|
| Scope | oneOf | Yes | Single Repository | Which repositories' workflow_run events this input accepts. Events from out-of-scope repositories are dropped. See sub-fields below. |
| Authentication Method | oneOf | Yes | Personal Access Token | Authentication method used to download logs: Personal Access Token or GitHub App. Needs actions:read on the repositories in scope. See sub-fields below. |
| Webhook Secret | secret | Yes | - | Shared secret configured on the GitHub webhook. Used to verify the X-Hub-Signature-256 signature of every delivery before it is accepted. |
Scope: Single Repository
| Field | Type | Required | Description |
|---|---|---|---|
| Owner | string | Yes | Repository owner (user or organization name) |
| Repository | string | Yes | Repository name (without the owner prefix, e.g., api-service not owner/api-service) |
Only events from this owner/repository are processed; deliveries from any other repository (for example, on an org-wide webhook) are dropped.
Authentication Method: Personal Access Token
| Field | Type | Required | Description |
|---|---|---|---|
| Personal Access Token | secret | Yes | Personal access token with repo and/or actions:read scopes. |
Authentication Method: GitHub App
| Field | Type | Required | Description |
|---|---|---|---|
| Client ID | string | Yes | The GitHub App's client ID |
| Installation ID | string | Yes | The installation ID for accessing the repository |
| Private Key | secret | Yes | The GitHub App's private key in PEM format |
Record Fields
Each emitted record represents a single chunk of a job's log file. The record shape is identical to the polling input.
| Field | Description |
|---|---|
workflow_meta | Object containing identifying metadata about the run, workflow, and job that produced this log file. The same workflow_meta is repeated on every chunk of the same file. |
workflow_meta.run_id | Unique identifier for the workflow run within the repository (GitHub's run ID). |
workflow_meta.run_attempt | The attempt number for re-runs; starts at 1. |
workflow_meta.workflow_id | Unique identifier for the workflow definition itself. |
workflow_meta.workflow_name | Human-readable name of the workflow (e.g., "CI/Build"). |
workflow_meta.run_number | Sequential run number within the workflow. |
workflow_meta.head_branch | Git ref the run was associated with — branch name, tag, or PR ref. |
workflow_meta.head_sha | Git commit SHA for the code that ran. |
workflow_meta.event | The trigger event (e.g., push, pull_request, schedule). |
workflow_meta.status | Run status. Always completed — this input only ingests completed runs. |
workflow_meta.conclusion | Final result: success, failure, cancelled, etc. |
workflow_meta.created_at | RFC3339 timestamp when the run was created. |
workflow_meta.updated_at | RFC3339 timestamp of the most recent update to the run. |
workflow_meta.run_started_at | RFC3339 timestamp when the run began executing. |
workflow_meta.html_url | Link to the run in the GitHub UI. |
workflow_meta.repository | Repository identifier in owner/repo format. |
workflow_meta.actor | GitHub user or app that triggered the run. |
workflow_meta.triggering_actor | The actor that directly triggered this particular run. |
workflow_meta.job_name | Human-readable name of the job within the workflow. |
workflow_meta.log_file | Source filename within GitHub's log archive (e.g., 0_build.txt). |
file_id | Stable, deterministic identifier for the source log file: {owner}/{repo}/{run_id}-{run_attempt}-{log_file}. Group records by file_id to reassemble a full file. |
chunk_meta.chunk_index | Zero-based position of this chunk within the source file. |
chunk_meta.last_chunk | true on the final chunk of a file; false on every earlier chunk. |
chunk_meta.chunk_size_bytes | Byte length of the content for this chunk. |
chunk_meta.truncated_lines | Count of log lines in this chunk that exceeded 1 MB and were truncated. 0 for nearly all real-world logs. |
content | The raw log text for this chunk, as UTF-8. |
Reconstructing full log files
To rebuild a complete log file from its chunks:
- Group by
file_id: Collect all records that share the samefile_id. - Sort by chunk index: Within each group, sort records ascending by
chunk_meta.chunk_index. - Verify completeness: Confirm the highest-index record has
chunk_meta.last_chunk == trueand that chunk indexes form a contiguous sequence from 0 to N. - Concatenate: Append the
contentstrings in index order.
Content format
GitHub's job log files have a consistent structure preserved verbatim in content:
- Leading byte-order mark. The first chunk of every log file begins with
U+FEFF(the UTF-8 BOM). - Per-line timestamp prefix. Each line is
<RFC3339Nano timestamp><space><message>, e.g.2026-04-28T00:31:56.3946773Z Current runner version: '2.334.0'. - Inline GitHub annotation markers. Tags like
##[group]...##[endgroup],##[error],##[warning],##[command], and##[debug]appear as literal text incontent.
Non-UTF-8 bytes (rare) are replaced with the Unicode replacement character U+FFFD when JSON-encoded.
Limitations
- No backfill. Only runs completing after the webhook is configured are ingested. See the warning at the top; use the polling input for historical data.
- Single repository per input (today). The Scope setting currently supports one repository; events from other repositories are dropped. An organization-wide scope may be added in the future.
- Attempt labeling on rapid re-runs. Each record's
run_attemptcomes from the event, while the log content is the run's latest attempt at fetch time. In the normal timeline these match (the just-completed attempt is the latest when its event fires). If a re-run starts before a prior attempt's event is processed, the content may be the newer attempt's while the metadata names the older — the same at-least-once labeling caveat the polling input carries. - Delivery reliability. Ingestion is at-least-once: GitHub retries failed deliveries, and a transient log-download failure is retried. Duplicate deliveries collapse downstream because record IDs (
file_id+chunk_meta.chunk_index) are deterministic — upsert-by-id sinks absorb them transparently; append-only sinks may see bounded duplicates. - Expired logs. GitHub retains workflow logs for ~90 days. A completed-run event whose logs have already expired (404 / 410) is skipped without failing.
- Payload Limit. Github enforces a 25 MB limit on payloads sent from their webhook, thus any payloads sent to the configured url will be dropped as they could not have come from github.
Troubleshooting
1. Deliveries rejected (401)
- Invalid signature: Confirm the Secret on the GitHub webhook exactly matches the input's Webhook Secret. GitHub's webhook Recent Deliveries tab shows the response; a
401indicates a signature mismatch or missingX-Hub-Signature-256. - Content type: Ensure the webhook Content type is
application/json.
2. No data
- Webhook not firing: In the repository's Settings > Webhooks > Recent Deliveries, confirm
workflow_runevents are being delivered with2xresponses. - Wrong events selected: Ensure Workflow runs is checked. Other events (e.g.
push) are acknowledged but produce no logs. - No completed runs yet: The input emits only on
action: completed. A run that is still queued or in progress produces nothing until it finishes.
3. Missing logs for some repositories
- Events from repositories outside the configured Scope are intentionally dropped. Confirm the Owner/Repository match the repo whose webhook is firing. (Org-wide webhooks deliver events for every repo; only the in-scope one is processed.)
4. Authentication / download errors
- "invalid credentials" / 403 on download: Verify the PAT or GitHub App can read Actions logs for the in-scope repository (
actions:read/Actions: Read). - Expired logs: Logs older than ~90 days are no longer downloadable and are skipped.
Sample Record
Code
Related Articles
- GitHub Actions Workflow Logs (polling input)
- workflow_run webhook event
- Securing your webhooks (X-Hub-Signature-256)
- Download workflow run logs
- GitHub Authentication