Webhook IntegrationPREMIUM

Receive real-time query results via HTTP webhooks

Overview

Webhooks allow you to receive query results directly to your application or service via HTTP POST requests. When your query completes, CronQuery will send the results to your specified webhook URL in real-time.

Premium Feature: Webhooks are available exclusively to Premium subscribers ($5/month). Upgrade your account to get started.

Setting Up Webhooks

1. Configure Your Webhook URL

When creating or editing a query, you can configure webhook notifications:

  1. Navigate to your query settings
  2. Enable webhook notifications
  3. Enter your webhook URL (must use HTTPS)
  4. Optionally, provide an authorization key for security
HTTPS Required: For security, all webhook URLs must use HTTPS. HTTP URLs will be rejected.

2. Webhook Payload

When your query completes successfully, CronQuery sends a POST request with the following JSON payload:

Successful Query Payload
{
  "type": "query_completed",
  "user_id": "user_123",
  "query_id": 456,
  "query_text": "Tell me when Bitcoin drops below $50,000",
  "result": "Bitcoin is currently at $48,500 - below your threshold!",
  "timestamp": "2026-02-16T10:30:00Z"
}

If your query encounters an error, you'll receive:

Failed Query Payload
{
  "type": "query_failed",
  "user_id": "user_123",
  "query_id": 456,
  "query_text": "Tell me when Bitcoin drops below $50,000",
  "error": "Unable to fetch current Bitcoin price - API timeout",
  "timestamp": "2026-02-16T10:30:00Z"
}

Webhook Security

Protect your webhook endpoints from unauthorized requests by using authorization keys. CronQuery supports Bearer token authentication.

How It Works

  1. Generate a secure random key (minimum 16 characters, maximum 512 characters)
  2. Enter the key in your query's webhook settings
  3. CronQuery encrypts and stores your key securely using AES-256-GCM
  4. With each webhook request, CronQuery sends: Authorization: Bearer YOUR_KEY
  5. Your webhook endpoint validates the Authorization header
Encryption: All authorization keys are encrypted before storage using industry-standard AES-256-GCM encryption. Your keys are never stored in plain text.

Generating a Secure Key

Use these commands to generate a cryptographically secure key:

Bash / Linux / macOS
openssl rand -hex 32
PowerShell
[Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Minimum 0 -Maximum 256 }))
Node.js
require('crypto').randomBytes(32).toString('hex')

Implementation Examples

Node.js (Express)

JavaScript
const express = require('express');
const app = express();

app.use(express.json());

// Your secret authorization key
const WEBHOOK_SECRET = 'your-secret-key-here';

app.post('/webhook/cronquery', (req, res) => {
  // Verify authorization header
  const authHeader = req.headers.authorization;
  
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing authorization' });
  }
  
  const token = authHeader.substring(7);
  
  if (token !== WEBHOOK_SECRET) {
    return res.status(403).json({ error: 'Invalid authorization' });
  }
  
  // Process the webhook payload
  const { type, query_id, query_text, result } = req.body;
  
  console.log(`Query ${query_id} completed: ${result}`);
  
  // Send success response
  res.status(200).json({ received: true });
});

app.listen(3000, () => console.log('Webhook server running on port 3000'));

Python (Flask)

Python
from flask import Flask, request, jsonify

app = Flask(__name__)

# Your secret authorization key
WEBHOOK_SECRET = 'your-secret-key-here'

@app.route('/webhook/cronquery', methods=['POST'])
def webhook():
    # Verify authorization header
    auth_header = request.headers.get('Authorization', '')
    
    if not auth_header.startswith('Bearer '):
        return jsonify({'error': 'Missing authorization'}), 401
    
    token = auth_header[7:]
    
    if token != WEBHOOK_SECRET:
        return jsonify({'error': 'Invalid authorization'}), 403
    
    # Process the webhook payload
    data = request.json
    query_id = data.get('query_id')
    result = data.get('result')
    
    print(f'Query {query_id} completed: {result}')
    
    return jsonify({'received': True}), 200

if __name__ == '__main__':
    app.run(port=3000)

Go (net/http)

Go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strings"
)

const webhookSecret = "your-secret-key-here"

type WebhookPayload struct {
    Type      string `json:"type"`
    UserID    string `json:"user_id"`
    QueryID   int64  `json:"query_id"`
    QueryText string `json:"query_text"`
    Result    string `json:"result,omitempty"`
    Error     string `json:"error,omitempty"`
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    // Verify authorization header
    authHeader := r.Header.Get("Authorization")
    if !strings.HasPrefix(authHeader, "Bearer ") {
        http.Error(w, "Missing authorization", http.StatusUnauthorized)
        return
    }
    
    token := strings.TrimPrefix(authHeader, "Bearer ")
    if token != webhookSecret {
        http.Error(w, "Invalid authorization", http.StatusForbidden)
        return
    }
    
    // Parse webhook payload
    var payload WebhookPayload
    if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
        http.Error(w, "Invalid payload", http.StatusBadRequest)
        return
    }
    
    // Process the webhook
    fmt.Printf("Query %d completed: %s\n", payload.QueryID, payload.Result)
    
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(map[string]bool{"received": true})
}

func main() {
    http.HandleFunc("/webhook/cronquery", webhookHandler)
    http.ListenAndServe(":3000", nil)
}

PHP

PHP
<?php
// Your secret authorization key
define('WEBHOOK_SECRET', 'your-secret-key-here');

// Get authorization header
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';

// Verify authorization
if (!str_starts_with($authHeader, 'Bearer ')) {
    http_response_code(401);
    echo json_encode(['error' => 'Missing authorization']);
    exit;
}

$token = substr($authHeader, 7);

if ($token !== WEBHOOK_SECRET) {
    http_response_code(403);
    echo json_encode(['error' => 'Invalid authorization']);
    exit;
}

// Parse webhook payload
$payload = json_decode(file_get_contents('php://input'), true);

$queryId = $payload['query_id'];
$result = $payload['result'] ?? '';

// Process the webhook
error_log("Query $queryId completed: $result");

// Send success response
http_response_code(200);
echo json_encode(['received' => true]);
?>

Best Practices

Security

Reliability

Testing

Troubleshooting

Webhooks Not Received

Authorization Failures

HTTPS Errors

Support

Need help setting up webhooks? We're here to help!