API Reference
Webhook Reference
Receive real-time HTTP POST events when GitLeads captures a new lead. Webhooks let you build custom integrations with any tool or internal system.
Setting Up a Webhook
- 1Go to Dashboard → Integrations → Webhooks
- 2Enter your endpoint URL (must be publicly accessible HTTPS)
- 3Optionally enter a signing secret — we will HMAC-SHA256 sign every payload with it
- 4Use the Test Webhook button to send a sample payload and confirm your endpoint responds with 200
Testing locally? Use webhook.site or smee.io to inspect incoming payloads in a browser.
Event Types
| Event | Trigger |
|---|---|
| lead.created | A new GitHub signal is captured and a lead record is created |
| lead.enriched | Additional enrichment data is added to an existing lead (e.g. email found via secondary source) |
| lead.updated | A lead record is manually updated in the GitLeads dashboard |
Payload Format
Every webhook is an HTTP POST with Content-Type: application/json. The body follows this structure:
{
"event": "lead.created",
"timestamp": "2026-04-24T10:31:00Z",
"data": {
"id": "lead_abc123",
"signal_type": "stargazer", // stargazer | keyword_issue | keyword_pr | keyword_code
"signal_source": "competitor/their-sdk",
"signal_context": "Starred on 2026-04-24",
"github_username": "jdoe",
"github_profile_url": "https://github.com/jdoe",
"name": "Jane Doe",
"email": "jane@example.com", // null if not publicly available
"bio": "Staff engineer @ Vercel. Loves Rust.",
"company": "Vercel",
"location": "San Francisco, CA",
"followers": 1842,
"public_repos": 47,
"account_created_at": "2019-03-12",
"top_languages": ["TypeScript", "Rust", "Go"],
"captured_at": "2026-04-24T10:31:00Z"
}
}Signature Verification
If you set a signing secret, every webhook includes a X-GitLeads-Signature header. The value is sha256=<hex_digest>, where the digest is HMAC-SHA256 of the raw request body using your signing secret as the key.
Always verify the signature before processing the payload. Here is an example in Node.js:
const crypto = require('crypto');
function verifySignature(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-gitleads-signature'];
if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// handle event.event === 'lead.created' etc.
res.sendStatus(200);
});import hmac, hashlib
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = os.environ['WEBHOOK_SECRET'].encode()
@app.route('/webhook', methods=['POST'])
def webhook():
sig = request.headers.get('X-GitLeads-Signature', '')
expected = 'sha256=' + hmac.new(
WEBHOOK_SECRET, request.data, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(sig, expected):
abort(401)
event = request.get_json()
# handle event['event'] == 'lead.created' etc.
return '', 200Retry Behavior
GitLeads considers a webhook delivery successful if your endpoint returns any 2xx HTTP status within 10 seconds. If the delivery fails (non-2xx, timeout, or connection error), GitLeads retries with exponential backoff:
| Attempt | Delay after failure |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry (final) | 6 hours |
After 5 failed attempts the delivery is marked as failed and not retried. Failed deliveries are logged in Dashboard → Integrations → Webhooks → Delivery Log.
Best Practices
- →Respond with 200 immediately, then process the event asynchronously. Do not do heavy work in the request handler.
- →Make your handler idempotent — the same event may be delivered more than once due to retries. Use the
data.idfield to deduplicate. - →Always verify the signature in production. Skip verification only in local development.
- →Store the raw body before parsing JSON so signature verification works correctly. Parsing first can alter whitespace.