OSPulse

Webhooks

OSPulse uses webhooks in both directions: your source-control provider notifies OSPulse when code changes so a rescan can run, and OSPulse can notify your systems through outbound webhook integrations.

Inbound: source-control ingest

These endpoints receive deliveries from GitHub, GitLab, Azure DevOps, and Bitbucket. You do not call them yourself — you register the URL with your provider and it posts to them on every push.

ProviderEndpoint
GitHubPOST /api/v1/webhooks/github
GitLabPOST /api/v1/webhooks/gitlab
Azure DevOpsPOST /api/v1/webhooks/azure-devops
BitbucketPOST /api/v1/webhooks/bitbucket

Push-triggered ingest is what keeps results current between scheduled scans: a merge that adds a dependency is reflected without waiting for the next cycle.

Ingest endpoints are never rate limited

The /api/v1/webhooks/… paths are exempt from rate limiting, so a burst of deliveries during a busy merge window can never be rejected with a 429. Providers apply their own delivery limits instead.

Authenticating deliveries

Every provider must prove the delivery genuinely came from it. Requests that fail this check are rejected before anything is persisted, so a forged payload cannot inject data.

GitHubX-Hub-Signature-256 + X-GitHub-EventRequired

GitHub signs the raw request body with the shared secret using HMAC-SHA256 and sends the result as X-Hub-Signature-256. The event type travels in X-GitHub-Event. A delivery missing either header is rejected.

GitLabX-Gitlab-Token + X-Gitlab-EventRequired

GitLab sends the per-connection secret verbatim as X-Gitlab-Token, which is compared against the stored value. The event type is in X-Gitlab-Event.

BitbucketX-Hub-Signature or Authorization, plus X-Event-KeyRequired

Bitbucket Cloud signs with X-Hub-Signature; Bitbucket Data Center authenticates with a bearer credential in the Authorization header. The event type is in X-Event-Key.

Azure DevOpsAuthorizationRequired

Azure DevOps service hooks authenticate with the credential configured on the subscription.

A delivery that arrives without its identity header is rejected with 401; one that arrives authenticated but without an event-type header is rejected with 400.

json
{
  "title": "Unauthorized",
  "status": 401,
  "detail": "GitLab webhook deliveries must carry an X-Gitlab-Token header matching the configured per-connection secret.",
  "correlationId": "0HN7GKQ2M3P4A:00000009"
}

Setting up an inbound webhook

Connect the provider

Create a source-control connection first — see import a repository. The connection holds the shared secret that deliveries are verified against.

Register the URL with your provider

Point the provider at the matching ingest endpoint, for example https://api.ospulse.app/api/v1/webhooks/github, and configure it to send push events with a JSON payload. Set the secret to the value stored on your OSPulse connection.

Verify the delivery

Push a commit, then confirm a scan was queued:

bash
curl "https://api.ospulse.app/api/v1/scans?take=5" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"

Providers also show delivery history with the response status, which is the fastest place to diagnose a 401.

Troubleshooting

  • 401 on every delivery — the secret configured at the provider does not match the one stored on the OSPulse connection. Reset both to the same value.
  • 400 with a missing-header message — the provider is not sending its event-type header. Check the webhook is configured to send push events, not a bare ping.
  • Deliveries succeed but nothing scans — the repository behind the push may not be imported. Confirm with GET /api/v1/repositories.
  • Retries — providers retry failed deliveries on their own schedule. Because ingest is effectively a request to rescan current state, a duplicate delivery is harmless.

Outbound: webhook integrations

OSPulse can also call your endpoint. Outbound webhooks are configured as an integration:

bash
curl -X POST https://api.ospulse.app/api/v1/integrations/webhooks \
  -H "Authorization: Bearer $OSPULSE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "security-events",
        "url": "https://hooks.example.com/ospulse"
      }'

A successful call returns 201 Created with the integration record. The exact request fields and the full integration surface — listing, updating, and removing integrations — are documented in the interactive API reference under Integrations.

Your receiving endpoint must be publicly reachable

Outbound delivery targets are validated before use. Internal, loopback, and private-network addresses are refused, which prevents a webhook configuration from being used to reach services inside our infrastructure. Expose a public HTTPS endpoint.

Receiving outbound webhooks safely

Whatever the payload, the same handling rules apply:

  • Respond quickly. Acknowledge with 2xx as soon as you have durably queued the event, then process asynchronously. Slow handlers cause retries.
  • Expect duplicates. Delivery is at-least-once. Make your handler idempotent by keying on the event identifier — see retries and idempotency.
  • Do not assume ordering. Events may arrive out of order; reconcile against the API when order matters.
  • Verify before trusting. Treat the request as untrusted input until you have authenticated it, and never act on a payload that fails verification.

Choosing webhooks or polling

Webhooks avoid the cost and latency of polling, but they add an endpoint you must operate. A reasonable default: use inbound source-control webhooks so scans stay current automatically, and poll the API on a modest schedule for alerts unless you already run event-driven infrastructure. See consume and triage alerts for the polling pattern.

Next steps