Webhooks

A1Cron supports webhook callbacks to notify your application about cron job execution results in real-time. You can configure separate URLs for successful and failed executions.

Webhook Configuration

When creating or updating a cron job, you can specify webhook URLs in the callbacks object:

{
  "callbacks": {
    "success_url": "https://your-app.com/webhooks/cron-success",
    "failure_url": "https://your-app.com/webhooks/cron-failure"
  }
}

Webhook Payload

Both success and failure webhooks receive the same payload structure:

cron_job_id
string

The unique identifier of the cron job

cron_job_name
string

The name of the cron job

execution_id
string

Unique identifier for this specific execution

status
string

Execution status: success, failure, error, or timeout

executed_at
string

ISO 8601 timestamp of when the job was executed

response_code
integer

HTTP response code from the endpoint (null for timeouts)

response_time_ms
integer

Time taken for the request in milliseconds

response_body
string

Response body from the endpoint (truncated to 1KB)

error_message
string

Error message if the execution failed (null for success)

retry_attempt
integer

Which retry attempt this was (0 for first attempt)

next_retry_at
string

ISO 8601 timestamp of next retry (null if no more retries)

Webhook Examples

Success Webhook Payload

{
  "cron_job_id": "550e8400-e29b-41d4-a716-446655440000",
  "cron_job_name": "Daily Sales Report",
  "execution_id": "exe_123456789",
  "status": "success",
  "executed_at": "2024-01-25T14:00:00Z",
  "response_code": 200,
  "response_time_ms": 245,
  "response_body": "{\"message\": \"Report generated successfully\", \"report_id\": \"rpt_98765\"}",
  "error_message": null,
  "retry_attempt": 0,
  "next_retry_at": null
}

Failure Webhook Payload

{
  "cron_job_id": "550e8400-e29b-41d4-a716-446655440000",
  "cron_job_name": "Daily Sales Report",
  "execution_id": "exe_987654321",
  "status": "failure",
  "executed_at": "2024-01-25T14:00:00Z",
  "response_code": 500,
  "response_time_ms": 1245,
  "response_body": "{\"error\": \"Database connection failed\"}",
  "error_message": "Endpoint returned status code 500",
  "retry_attempt": 1,
  "next_retry_at": "2024-01-25T14:05:00Z"
}

Timeout Webhook Payload

{
  "cron_job_id": "550e8400-e29b-41d4-a716-446655440000",
  "cron_job_name": "Daily Sales Report",
  "execution_id": "exe_456789123",
  "status": "timeout",
  "executed_at": "2024-01-25T14:00:00Z",
  "response_code": null,
  "response_time_ms": 30000,
  "response_body": null,
  "error_message": "Request timed out after 30 seconds",
  "retry_attempt": 2,
  "next_retry_at": "2024-01-25T14:10:00Z"
}

Implementing Webhook Handlers

Node.js Express Example

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

app.use(express.json());

// Success webhook handler
app.post('/webhooks/cron-success', (req, res) => {
  const {
    cron_job_id,
    cron_job_name,
    execution_id,
    response_body,
    executed_at
  } = req.body;

  console.log(`✅ Cron job "${cron_job_name}" executed successfully`);
  console.log(`Execution ID: ${execution_id}`);
  console.log(`Response: ${response_body}`);

  // Process the successful execution
  // e.g., update database, send notifications, etc.

  res.status(200).json({ received: true });
});

// Failure webhook handler
app.post('/webhooks/cron-failure', (req, res) => {
  const {
    cron_job_id,
    cron_job_name,
    execution_id,
    status,
    error_message,
    retry_attempt,
    next_retry_at
  } = req.body;

  console.error(`❌ Cron job "${cron_job_name}" failed`);
  console.error(`Status: ${status}`);
  console.error(`Error: ${error_message}`);
  console.error(`Retry attempt: ${retry_attempt}`);
  
  if (next_retry_at) {
    console.log(`Next retry at: ${next_retry_at}`);
  } else {
    console.error('No more retries scheduled');
    // Send alert to team
  }

  res.status(200).json({ received: true });
});

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

Python Flask Example

from flask import Flask, request, jsonify
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

@app.route('/webhooks/cron-success', methods=['POST'])
def handle_cron_success():
    data = request.json
    
    logging.info(f"✅ Cron job '{data['cron_job_name']}' executed successfully")
    logging.info(f"Execution ID: {data['execution_id']}")
    logging.info(f"Response: {data['response_body']}")
    
    # Process the successful execution
    # e.g., update database, send notifications, etc.
    
    return jsonify({"received": True}), 200

@app.route('/webhooks/cron-failure', methods=['POST'])
def handle_cron_failure():
    data = request.json
    
    logging.error(f"❌ Cron job '{data['cron_job_name']}' failed")
    logging.error(f"Status: {data['status']}")
    logging.error(f"Error: {data['error_message']}")
    logging.error(f"Retry attempt: {data['retry_attempt']}")
    
    if data.get('next_retry_at'):
        logging.info(f"Next retry at: {data['next_retry_at']}")
    else:
        logging.error('No more retries scheduled')
        # Send alert to team
        send_alert_to_team(data)
    
    return jsonify({"received": True}), 200

def send_alert_to_team(data):
    # Implement your alerting logic here
    pass

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

Webhook Security

Verify Webhook Signatures

A1Cron signs webhook payloads using HMAC-SHA256. Verify the signature to ensure the webhook is from A1Cron:

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return signature === expectedSignature;
}

app.post('/webhooks/cron-success', (req, res) => {
  const signature = req.headers['x-a1cron-signature'];
  const webhookSecret = process.env.WEBHOOK_SECRET;
  
  if (!verifyWebhookSignature(req.body, signature, webhookSecret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Process the webhook...
});

IP Whitelisting

For additional security, whitelist A1Cron’s webhook IP addresses:

  • 52.89.214.238
  • 34.212.75.30
  • 54.218.53.128

Webhook Best Practices

Common Use Cases

Alert on Failures

app.post('/webhooks/cron-failure', async (req, res) => {
  const { cron_job_name, error_message, retry_attempt } = req.body;
  
  // Send immediate alert for critical jobs
  if (cron_job_name.includes('critical')) {
    await sendSlackAlert({
      text: `🚨 Critical cron job failed: ${cron_job_name}`,
      error: error_message,
      attempt: retry_attempt
    });
  }
  
  res.status(200).json({ received: true });
});

Track Execution Metrics

app.post('/webhooks/cron-success', async (req, res) => {
  const { cron_job_id, response_time_ms, executed_at } = req.body;
  
  // Store metrics in time-series database
  await metricsDB.insert({
    job_id: cron_job_id,
    timestamp: executed_at,
    duration_ms: response_time_ms,
    status: 'success'
  });
  
  res.status(200).json({ received: true });
});

Chain Dependent Jobs

app.post('/webhooks/cron-success', async (req, res) => {
  const { cron_job_name, response_body } = req.body;
  
  // Trigger dependent job after data sync completes
  if (cron_job_name === 'Data Sync') {
    const syncResult = JSON.parse(response_body);
    if (syncResult.records_synced > 0) {
      await triggerCronJob('Data Processing Job');
    }
  }
  
  res.status(200).json({ received: true });
});

Testing Webhooks

Use webhook.site to test webhook integration:

  1. Go to webhook.site to get a unique URL
  2. Use it as your webhook URL when creating a cron job
  3. Trigger the cron job manually
  4. View the webhook payload on webhook.site

Example:

{
  "callbacks": {
    "success_url": "https://webhook.site/your-unique-id",
    "failure_url": "https://webhook.site/your-unique-id"
  }
}