Skip to main content
This endpoint is called by GitHub, not by your frontend or CLI. When you create a project in Shipyard, the server registers this URL as a webhook on your GitHub repository via the GitHub API. From that point on, every push to any branch fires a signed HTTP POST to this endpoint. Shipyard verifies the HMAC-SHA256 signature, checks whether the push targets the project’s configured branch, creates a build record, and starts the pipeline.
You do not call this endpoint directly. GitHub sends the request automatically on every push event. The information below is provided so you understand what Shipyard expects and how to troubleshoot delivery failures in GitHub’s webhook settings.

POST /api/webhook

Receives a GitHub push event, verifies the HMAC-SHA256 signature, matches the repository URL and branch to a project, inserts a build record, and starts the build pipeline asynchronously. No JWT auth — authenticated via HMAC-SHA256 signature.

Required headers

HeaderDescription
X-Hub-Signature-256HMAC-SHA256 signature computed by GitHub using your WEBHOOK_SECRET. Format: sha256=<hex>. Shipyard computes the same HMAC over the raw request body and compares using a timing-safe equality check.
X-GitHub-EventEvent type. Shipyard only processes push events. A ping event (sent when the webhook is first registered) returns 200 without triggering a build.
Content-TypeMust be application/json. Set automatically by GitHub when you configure the webhook’s content type as JSON.
Keep your WEBHOOK_SECRET confidential. Anyone who possesses this value can compute a valid signature and send a forged push event to your server, which would trigger arbitrary builds. Rotate the secret in your .env and update the webhook configuration in GitHub if you suspect it has been exposed.

Request body

The request body is the standard GitHub push event payload, sent automatically by GitHub. Shipyard reads the following fields:
repository.html_url
string
The HTTPS URL of the repository (e.g., https://github.com/owner/repo). Shipyard uses this to find the matching project in the database.
ref
string
The full Git ref of the pushed branch, in the format refs/heads/<branch>. Shipyard strips the refs/heads/ prefix and compares the result against the project’s configured branch. If they do not match, no build is triggered.
commits[0].message
string
The commit message of the most recent commit in the push. Stored as the build’s commit field.
commits[0].author.name
string
The name of the commit author. Stored as the build’s commitAuthor field.
after
string
The full SHA of the HEAD commit after the push. Stored as the build’s commitHash and used during the clone step to check out the exact revision. Also used as the commit hash if a rollback to this build is requested later.

What Shipyard does with the payload

  1. Verifies the X-Hub-Signature-256 header using HMAC-SHA256 with WEBHOOK_SECRET. Returns 401 if the signature is missing or does not match.
  2. Checks the X-GitHub-Event header. Returns 200 for ping, proceeds for push, and ignores other events.
  3. Strips refs/heads/ from ref to get the branch name.
  4. Queries the database for a project whose repoUrl matches repository.html_url and whose branch matches the extracted branch name. Returns 404 if no match is found.
  5. Inserts a new build record with status: "queued" and responds 201.
  6. Starts the build pipeline asynchronously (clone, Dockerfile generation, Docker build, test run, deploy).

Response

On success (matching project found and build queued):
{
  "message": "Build Queued",
  "build": {
    "id": 14,
    "status": "queued",
    "commit": "fix: update hero copy",
    "branch": "main",
    "commitAuthor": "Jane Doe",
    "commitHash": "a1b2c3d4e5f6",
    "exitCode": null,
    "projectId": 7,
    "startedAt": "2026-04-11T12:00:00.000Z",
    "finishedAt": null
  }
}

Error responses

StatusCondition
401X-Hub-Signature-256 header is missing
401Signature does not match the computed HMAC — payload may be tampered or WEBHOOK_SECRET is wrong
404No project found whose repoUrl and branch match the push event
500Failed to insert the build record

Troubleshooting webhook deliveries

GitHub logs every webhook delivery attempt, including the request headers, payload, and response code. To inspect failed deliveries:
  1. Go to your repository on GitHub
  2. Open Settings → Webhooks
  3. Click on the Shipyard webhook entry
  4. Scroll to Recent Deliveries to see the response code and body for each delivery
Common reasons for 401 responses:
  • WEBHOOK_SECRET in your .env does not match the secret stored in GitHub’s webhook configuration. Update one to match the other and redeliver.
  • The request body was modified in transit (e.g., by a reverse proxy that re-serializes JSON). Shipyard computes the HMAC over the raw body — any reformatting will break the signature.
Common reasons for 404 responses:
  • The repository URL stored on the project uses HTTPS (https://github.com/owner/repo) but the webhook payload’s repository.html_url differs (e.g., has a trailing .git). Check that the repoUrl recorded in Shipyard matches the format GitHub sends exactly.
  • The push targeted a branch that is not configured on the project. Only pushes to the branch specified at project creation (or last updated via PUT /api/project/projects/:projectId) trigger a build.

Build docs developers (and LLMs) love