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.
| Provider | Endpoint |
|---|---|
| GitHub | POST /api/v1/webhooks/github |
| GitLab | POST /api/v1/webhooks/gitlab |
| Azure DevOps | POST /api/v1/webhooks/azure-devops |
| Bitbucket | POST /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-EventRequiredGitHub 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-EventRequiredGitLab 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-KeyRequiredBitbucket 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 DevOpsAuthorizationRequiredAzure 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.
{
"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:
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
401on every delivery — the secret configured at the provider does not match the one stored on the OSPulse connection. Reset both to the same value.400with 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:
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
2xxas 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.
