Webhooks
Receive events from your tools via webhook endpoints.
Table of contents
Overview
Webhooks allow external tools to send events to ButterStack. Each integration has its own webhook endpoint with a unique token for authentication.
Authentication
All webhook endpoints use token-based authentication. The webhook_token is generated when you create an integration and is shown on the integration’s page (Project → Integrations → your integration).
The token can be delivered three ways; prefer a header, since a query string lands in proxy and access logs:
| Delivery | Accepted by |
|---|---|
X-ButterStack-Token: <token> header | The platform endpoints (Perforce, Jira, Trello, Monday, Asana, Linear, GitHub, Diversion, Lore) |
X-Webhook-Token: <token> header, or HTTP Basic with the token as the password | The CI intake (/webhooks/jenkins, /webhooks/ci) |
?token=<token> query string | All of the above |
POST /webhooks/{platform}?token={webhook_token}
Webhook Endpoints
Perforce
Endpoint: POST /webhooks/perforce
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Integration webhook token |
changelist | integer | Yes | Perforce changelist number |
timestamp | string | No | Submission timestamp (ISO 8601) |
Jenkins
Endpoint: POST /webhooks/jenkins
Body (JSON):
{
"job_name": "MyGame-Build",
"build_number": 456,
"status": "SUCCESS",
"build_url": "https://jenkins.example.com/job/MyGame-Build/456"
}
Game Engine
| Endpoint | Purpose |
|---|---|
POST /webhooks/game_engine/cooking_started | Build cooking phase started |
POST /webhooks/game_engine/asset_cooked | Individual asset cooked |
POST /webhooks/game_engine/shaders_started | Shader compilation started |
POST /webhooks/game_engine/shaders_completed | Shader compilation finished |
POST /webhooks/game_engine/packaging_started | Packaging phase started |
POST /webhooks/game_engine/build_completed | Build finished successfully |
POST /webhooks/game_engine/build_failed | Build failed |
Deployment
Endpoint: POST /webhooks/deployment
Body:
{
"platform": "steam",
"event": "uploaded",
"build_number": 456,
"steam_app_id": "480"
}
Platform values: steam (alias steamworks), reddit_devvit (aliases reddit, devvit), youtube_playables (aliases youtube, playables), epic_games_store (aliases epic, epic_games), google_play (aliases google, android), apple_app_store (aliases apple, ios, app_store), unity_cloud (alias unity). Any other value is recorded as custom.
CI Systems
| Endpoint | Notes |
|---|---|
POST /webhooks/jenkins | Jenkins and any other CI system that posts a flat JSON build payload |
POST /webhooks/jenkins/build_notification | HMAC-signed build-status callback |
POST /webhooks/jenkins/commit_trigger | Commit-driven build trigger |
POST /webhooks/ci | Neutral alias for the three above. Same controller, same payloads - use it when the poster is GitHub Actions, TeamCity, or another non-Jenkins CI |
POST /webhooks/buildkite | Native Buildkite intake. Verifies Buildkite’s X-Buildkite-Signature HMAC and rejects anything unsigned or stale |
Non-Jenkins CI must push its log
ButterStack polls the Jenkins API for console output, artifacts, and build info. No other CI provider is polled - a CI server inside a studio’s own network is not reachable from ButterStack, and a build_url pointing at an internal host (http://teamcity-server:8111/...) or a private-tailnet address resolves to nothing from the outside.
So for GitHub Actions, TeamCity, or any other non-Jenkins poster, the log has to ride on the webhook payload itself:
{
"job_name": "PilotLight_Verify",
"build_number": "298",
"status": "FAILURE",
"build_url": "https://teamcity.example.com/buildConfiguration/PilotLight_Verify/298",
"changelist": "207",
"logs_tail": "<the last few hundred KB of the build log>"
}
logs_tail (also accepted as log or console) populates output_log, capped at 256,000 characters with the oldest content dropped first. A report that omits it leaves ButterStack with no log for that build.
This is not cosmetic: output_log is the only build output the AI failure investigator can read. Without logs_tail the investigator has no error text to work from and will return a low-confidence infrastructure finding saying so rather than a real diagnosis (#1940).
Other Integrations
| Integration | Endpoint |
|---|---|
| Jira | POST /webhooks/jira |
| Linear | POST /webhooks/linear |
| Trello | POST /webhooks/trello |
| Monday.com | POST /webhooks/monday |
| Asana | POST /webhooks/asana |
| GitHub | POST /webhooks/github (requires X-GitHub-Event; X-Hub-Signature-256 when a secret is configured) |
| Diversion | POST /webhooks/diversion |
| Lore | POST /webhooks/lore - see the Lore integration guide |
Each of these except the CI intake also answers GET /webhooks/{platform}/verify, which returns {"status":"verified","service":"..."} for provider handshakes.
Response Codes
| Code | Description |
|---|---|
200 | Event processed, or recognized as a duplicate delivery and skipped |
202 | Event accepted and queued for asynchronous processing (Perforce and Lore return this) |
401 | Missing or invalid token, or a failed signature check |
403 | The integration’s project is paused or archived and cannot receive webhooks |
404 | No active integration matches the token |
422 | Malformed or unparseable body |
429 | Rate limit exceeded |
500 | Internal server error |
There is no {project_id} path segment on any webhook endpoint: the token identifies the integration, and therefore the project.
Testing Webhooks
curl -X POST \
"https://www.butterstack.com/webhooks/perforce" \
-H "X-ButterStack-Token: YOUR_TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "changelist=12345"