Platform
Orchestrate
Webhooks

Webhooks

Receive event-driven notifications when queries complete, are blocked, cached, or compressed. Xilos sends HMAC-SHA256-signed HTTP POST requests to your endpoint so you can integrate query events into Slack, Microsoft Teams, custom dashboards, or any HTTP-compatible system.

Event Types

Xilos emits webhooks for the following events:

EventTriggered When
query.completedA query finishes processing and a response is returned.
query.blockedA query is blocked by a restriction rule or guardrail.
query.cachedA query is served from the semantic cache without an LLM call.
query.compressedA query's prompt is compressed before being sent to the LLM.
cost.thresholdA cost threshold (daily, monthly, or per-query) is exceeded.
user.createdA new user is created in the organization.

Info: Subscribe to specific event types per webhook. A single endpoint can receive multiple event types, or you can create dedicated webhooks per event.

Creating a Webhook

From the Dashboard

Open Webhooks

Navigate to OrchestrateWebhooks.

Create new

Click New Webhook.

Enter endpoint URL

Provide the HTTPS URL that will receive webhook payloads.

Select events

Choose which event types this webhook should receive.

Save

Click Save. Xilos generates a signing secret. Copy and store it securely — you will need it to verify payloads.

From the API

cURL

curl -X POST https://api.xilos.ai/v1/webhooks \
  -H "Authorization: Bearer $XILOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.yourcompany.com/xilos",
    "events": ["query.completed", "query.blocked", "cost.threshold"]
  }'

Python

import xilos
 
client = xilos.Client(api_key="...")
 
webhook = client.webhooks.create(
    url="https://hooks.yourcompany.com/xilos",
    events=["query.completed", "query.blocked", "cost.threshold"],
)
 
print(webhook.id)
print(webhook.signing_secret)

Warning: The signing secret is only shown once at creation time. Store it immediately in a secrets manager. If lost, regenerate it from the webhook settings page (this invalidates the previous secret).

HMAC-SHA256 Signatures

Every webhook delivery includes an X-Xilos-Signature header containing an HMAC-SHA256 signature of the request body. Verify this signature on your server to confirm the payload originated from Xilos.

Verifying the Signature

Node.js

import crypto from 'crypto';
 
function verifySignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Python

import hmac
import hashlib
 
def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Warning: Always use a constant-time comparison function (e.g., hmac.compare_digest or crypto.timingSafeEqual) when verifying signatures. Naive string comparison is vulnerable to timing attacks.

Payload Format

All webhook payloads share a common envelope:

FieldTypeDescription
eventstringThe event type (e.g., query.completed).
timestampstringISO 8601 timestamp of when the event was generated.
webhook_idstringThe ID of the webhook that triggered this delivery.
dataobjectThe event-specific payload.

Payload Example

The following is an example of a query.completed webhook payload:

{
  "event": "query.completed",
  "timestamp": "2026-07-06T14:32:07.482Z",
  "webhook_id": "wh_8f3a2b1c",
  "data": {
    "query_id": "q_4a9c7e2d",
    "user_id": "usr_2b8f1a03",
    "model": "claude-sonnet-4",
    "routing_rule": "Technical_Support_Tier1",
    "input_tokens": 1240,
    "output_tokens": 856,
    "cost_usd": 0.0123,
    "latency_ms": 1843,
    "cache_hit": false,
    "compressed": true,
    "compression_ratio": 0.52,
    "faithfulness_score": 0.91,
    "answer_relevance_score": 0.88,
    "context_relevance_score": 0.85,
    "query": "How do I reset my API key?",
    "response_preview": "To reset your API key, navigate to Settings → API Keys..."
  }
}

Event-Specific Data Fields

query.blocked

{
  "data": {
    "query_id": "q_4a9c7e2d",
    "user_id": "usr_2b8f1a03",
    "rule": "PII_Restriction",
    "reason": "Query contains personally identifiable information.",
    "category": "pii",
    "severity": "high",
    "query": "What is John Smith's social security number?"
  }
}

query.cached

{
  "data": {
    "query_id": "q_4a9c7e2d",
    "user_id": "usr_2b8f1a03",
    "original_query": "What is the capital of France?",
    "matched_query": "What's the capital city of France?",
    "similarity_score": 0.94,
    "cost_saved_usd": 0.0089,
    "latency_saved_ms": 1620
  }
}

query.compressed

{
  "data": {
    "query_id": "q_4a9c7e2d",
    "user_id": "usr_2b8f1a03",
    "original_tokens": 4200,
    "compressed_tokens": 2016,
    "compression_ratio": 0.48,
    "strategies": ["prefix_stabilizer", "conversation_summarizer", "bm25_extractor"],
    "cost_saved_usd": 0.0312
  }
}

cost.threshold

{
  "data": {
    "scope": "daily",
    "period": "2026-07-06",
    "threshold_usd": 500.00,
    "actual_usd": 512.47,
    "top_model": "claude-opus-4",
    "top_model_spend_usd": 287.13
  }
}

user.created

{
  "data": {
    "user_id": "usr_2b8f1a03",
    "email": "jane.doe@company.com",
    "role": "member",
    "organization_id": "org_1a2b3c"
  }
}

Retry Logic

If your endpoint returns a non-2xx response or does not respond within 10 seconds, Xilos retries the delivery with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
6 (final)6 hours

After 6 failed attempts, the delivery is marked as failed and recorded in the webhook's delivery log. The webhook itself remains active for future events.

Info: Your endpoint should respond with a 200 or 201 status code as soon as the payload is received. Process the payload asynchronously if your logic is slow — timeouts count as failures and trigger retries.

Delivery Log

Each webhook maintains a delivery log accessible from the dashboard:

  • Timestamp — When the delivery was attempted.
  • Event — The event type.
  • Statusdelivered, failed, or retrying.
  • HTTP status code — The response code from your endpoint.
  • Response body — The first 500 characters of your endpoint's response.
  • Retry count — How many retry attempts have been made.

You can manually redeliver any event from the delivery log.

Best Practices

  • Verify signatures — Always validate the X-Xilos-Signature header. Reject payloads that fail verification.
  • Respond quickly — Return 200 immediately and process asynchronously. Timeouts trigger retries.
  • Make endpoints idempotent — Xilos may deliver the same event more than once during retries. Use the query_id or event timestamp to deduplicate.
  • Use HTTPS — Xilos only delivers to HTTPS endpoints. Never use plain HTTP.
  • Subscribe selectively — Only subscribe to events you act on. Receiving unnecessary events wastes bandwidth and processing.
  • Monitor the delivery log — Check for failed deliveries and redeliver important events manually if needed.
  • Rotate secrets — Regenerate the signing secret periodically from the webhook settings page. Update your verification code before regenerating.