Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.roboticks.io/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks

Every action GitHub takes that matters to Roboticks — a PR opened, a push to main, an installation suspended — arrives as a webhook delivery to https://api.roboticks.io/api/v1/github/webhooks. This page documents what we listen to, how we verify it, and what happens next.

Endpoint

POST https://api.roboticks.io/api/v1/github/webhooks
Content-Type: application/json
X-GitHub-Event: <event>
X-GitHub-Delivery: <uuid>
X-Hub-Signature-256: sha256=<hmac>
The endpoint is public, idempotent on X-GitHub-Delivery, and returns within 1 second for every delivery (heavy work is queued).

Subscribed events

EventSub-actions handledWhat we do
installationcreated, deleted, suspend, unsuspendCreate / disable / re-enable the installation record. On deleted, retain history; on suspend, stop dispatching new jobs.
installation_repositoriesadded, removedSync the project ↔ repo mapping. Removed repos go to a disconnected state but historical data is retained.
pull_requestopened, synchronize, reopened, ready_for_review, closedEnqueue a test run for the head SHA. On closed we cancel any in-flight job for that PR.
pushcreated, deleted (branch)Trigger runs for branches that match the project’s branch policy — by default main, master, and release/*.
workflow_runcompletedIf a customer runs the Roboticks SDK inside their own GitHub Actions workflow, we pick up the JUnit + MCAP artifacts via this event.
check_suitererequested, requestedRe-dispatch the test run when a user clicks Re-run all jobs on the Check Suite.
Everything else is ignored at the load balancer — we don’t even parse the body.

What happens on a delivery

The HTTP response is always within 1 s because we acknowledge before processing. GitHub retries on 5xx; we do not want to be in the retry loop, so heavy work is queued behind a fast ack.

HMAC verification

Each installation has a webhook secret rotated every 90 days. GitHub signs every delivery with that secret using HMAC-SHA256 over the raw request body. The signature arrives in X-Hub-Signature-256.
import hmac, hashlib

def verify(raw_body: bytes, header_sig: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header_sig)
Three things to know:
  1. We verify on the raw body, before JSON parsing. Reformatting the body invalidates the signature.
  2. We use hmac.compare_digest for constant-time comparison — no timing side channels.
  3. Any failure short-circuits to HTTP 401 and the delivery is dropped. GitHub will retry; if your secret has actually rotated and our copy is stale, the retries will all fail and you’ll see the corresponding alert on the dashboard.
The webhook secret is the only thing that proves a delivery came from GitHub. Do not log it. Do not put it in error messages. If you suspect it’s leaked, rotate it from Settings → Integrations → GitHub App → Rotate webhook secret — GitHub re-signs subsequent deliveries automatically.

Replay protection

X-GitHub-Delivery is a UUID per delivery. We store every delivery ID for 24 hours in Redis and 401 on any duplicate. Why:
  • A leaked-but-valid signed payload can’t be replayed to re-trigger a job.
  • A malicious mirror that captures and re-posts cannot bypass the window.
24 hours is well above the GitHub retry window (8 attempts over ~8 hours). Outside the window, the SHA-bound idempotency keys on the job queue catch duplicates as well — replays cannot create duplicate jobs.

Payload examples

{
  "action": "opened",
  "number": 142,
  "pull_request": {
    "head": { "sha": "abcd1234...", "ref": "feature/estop-deadline" },
    "base": { "sha": "9876fedc...", "ref": "main" },
    "draft": false
  },
  "repository": {
    "full_name": "acme-robotics/amr-stack"
  },
  "installation": { "id": 12345678 }
}
{
  "action": "created",
  "installation": {
    "id": 12345678,
    "account": { "login": "acme-robotics", "type": "Organization" },
    "repository_selection": "selected"
  },
  "repositories": [
    { "full_name": "acme-robotics/amr-stack" }
  ]
}
{
  "action": "rerequested",
  "check_suite": {
    "head_sha": "abcd1234...",
    "head_branch": "feature/estop-deadline"
  },
  "repository": { "full_name": "acme-robotics/amr-stack" },
  "installation": { "id": 12345678 }
}

Delivery observability

Two surfaces:
  • GitHub sideSettings → Applications → Roboticks → Advanced → Recent Deliveries. Every delivery, its payload, our response code, and a redeliver button.
  • Roboticks sideSettings → Integrations → GitHub App → Webhook log. Same data, plus the resolved installation, project, and downstream job ID for each delivery.
Both surfaces are kept for 30 days.

Skipped deliveries

We accept the delivery (202) but skip downstream processing if:
  • The installation is suspended.
  • The repo has been removed from the installation since the event fired.
  • The repo is not linked to any Roboticks project (this is normal during onboarding).
  • The PR is a draft and the project’s policy excludes drafts (default: skip drafts).
Skipped deliveries are visible in the webhook log with reason codes.

Next

Permissions

The scopes that back the webhook handler.

Check Runs

The output produced by a webhook-triggered job.

Troubleshooting

Webhook 4xx errors and how to recover.