Skip to main content

Overview

Webhooks allow you to receive real-time HTTP notifications when events occur in LatentGEO. Configure your endpoint to receive POST requests with event payloads.

Webhook Configuration

Create Webhook Config

POST /api/v1/webhooks/config Creates a new webhook configuration.
curl -X POST https://api.latentgeo.com/api/v1/webhooks/config \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/latentgeo",
    "secret": "your-webhook-secret-min-16-chars",
    "events": ["audit.completed", "pdf.ready"],
    "active": true,
    "description": "Production webhook for audit notifications"
  }'
url
string
required
HTTPS URL to receive webhook POST requests
secret
string
Secret for signing payloads (16-256 characters). Used to generate HMAC-SHA256 signature.
events
array
default:"[\"audit.completed\", \"pdf.ready\"]"
List of events to subscribe to
active
boolean
default:"true"
Whether the webhook is active
description
string
Optional description (max 500 characters)
Webhook URLs must use HTTPS in production. HTTP is only allowed in debug mode.

Test Webhook

POST /api/v1/webhooks/test Sends a test event to verify your endpoint.
url
string
required
Webhook URL to test
secret
string
Webhook secret for signature verification
event_type
string
default:"audit.completed"
Event type to send
success
boolean
Whether test succeeded
status_code
integer
HTTP status code returned by your endpoint
response_time_ms
number
Response time in milliseconds
error
string
Error message (if test failed)

List Webhook Events

GET /api/v1/webhooks/events Returns all available webhook event types.
events
array
Array of event type objects with descriptions

Webhook Events

Available Events

EventDescription
audit.createdTriggered when a new audit is created
audit.startedTriggered when audit processing begins
audit.completedTriggered when audit finishes successfully
audit.failedTriggered when audit fails with an error
audit.progressTriggered at progress milestones (25%, 50%, 75%)
report.generatedTriggered when audit report is generated
pdf.readyTriggered when PDF report is ready
pagespeed.completedTriggered when PageSpeed analysis completes
geo.analysis.completedTriggered when GEO tools analysis completes
github.pr.createdTriggered when GitHub PR is created
github.sync.completedTriggered when GitHub sync completes
competitor.analysis.completedTriggered when competitor analysis completes

Receiving Webhooks

Payload Structure

All webhook POST requests include:
{
  "event": "audit.completed",
  "timestamp": "2026-03-03T10:30:00Z",
  "webhook_id": "wh_1234567890",
  "data": {
    "audit_id": 123,
    "url": "https://example.com",
    "status": "completed",
    "geo_score": 78.5,
    "total_pages": 25
  }
}
event
string
Event type
timestamp
string
ISO 8601 timestamp
webhook_id
string
Unique delivery ID for idempotency
data
object
Event-specific payload data

HTTP Headers

Webhook requests include the following headers:
HeaderDescription
X-Webhook-SignatureHMAC-SHA256 signature (if secret configured)
X-Webhook-EventEvent type
X-Webhook-TimestampDelivery timestamp
Content-Typeapplication/json

Signature Verification

If you configured a webhook secret, verify the signature:
Python
import hmac
import hashlib

def verify_webhook_signature(payload_body: bytes, signature: str, secret: str) -> bool:
    expected_signature = hmac.new(
        secret.encode(),
        payload_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected_signature)

# Usage
request_signature = request.headers.get('X-Webhook-Signature')
if not verify_webhook_signature(request.body, request_signature, webhook_secret):
    return Response('Invalid signature', status=401)
TypeScript
import crypto from 'crypto';

function verifyWebhookSignature(
  payloadBody: string,
  signature: string,
  secret: string
): boolean {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payloadBody)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Best Practices

Return 200 quickly: Respond with HTTP 200 within 5 seconds. Process the webhook asynchronously to avoid timeouts.
  1. Verify signatures: Always verify the X-Webhook-Signature header
  2. Idempotency: Use webhook_id to prevent duplicate processing
  3. Retry handling: Implement exponential backoff for failed deliveries
  4. Timeouts: Respond within 5 seconds to avoid retries
  5. Logging: Log all webhook deliveries for debugging

Incoming Webhooks

GitHub Webhook

POST /api/v1/webhooks/github/incoming Receives GitHub webhook events (push, pull_request, issues).
Requires X-Hub-Signature-256 header for signature verification.
X-Hub-Signature-256
string
required
GitHub webhook signature
X-GitHub-Event
string
required
GitHub event type

HubSpot Webhook

POST /api/v1/webhooks/hubspot/incoming Receives HubSpot CRM events (contact.creation, contact.propertyChange, deal.creation).

Health Check

GET /api/v1/webhooks/health Returns webhook service health status.
status
string
Service status (healthy)
service
string
Service name (webhook)
supported_events
integer
Number of supported event types
timestamp
string
Current server timestamp

Error Codes

400
error
Invalid event type or JSON payload
401
error
Invalid webhook signature
503
error
Webhook secret not configured on server

Build docs developers (and LLMs) love