Skip to content
← Back to Functions
Code

Webhook Delivery Queue Item

Creates a queued webhook delivery record with payload hash and retry metadata.

Function signature

ogBuildWebhookDeliveryItem(event = array(), options = array())

Categories

  • APIs and Webhooks

Parameters

eventEvent data with target_url, event_type, payload, and optional resource_id. Recognized keys: `event_type`, `payload`, `target_url`.optionsDelivery options including secret, max_attempts, and timeout_seconds. Recognized keys: `max_attempts`, `secret`.

Return value

Short public-safe status message.

  • delivery_id
  • target_url
  • event_type
  • payload_json
  • payload_hash
  • signature
  • attempt_count
  • max_attempts
  • created_at
  • next_attempt_at

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, policy arrays, URLs, signatures, and caller-owned allowlists before use; keep secrets and internal paths out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Creates a queued outbound webhook delivery record.
 *
 * The payload is JSON encoded, hashed, and optionally HMAC signed. Secrets
 * are never stored in the returned queue data.
 *
 * @param array $event Event data with target_url, event_type, payload, and optional resource_id.
 * @param array $options Delivery options including secret, max_attempts, and timeout_seconds.
 * @return array Queue-ready webhook delivery data.
 */
function ogBuildWebhookDeliveryItem($event = array(), $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($event)) {
		$result['message'] = 'Event data must be an array.';
		return $result;
	}

	if (!is_array($options)) {
		$options = array();
	}

	$target_url = '';
	if (!empty($event['target_url'])) {
		$target_url = trim((string)$event['target_url']);
	}
	if (filter_var($target_url, FILTER_VALIDATE_URL) === false) {
		$result['message'] = 'Webhook target URL is invalid.';
		return $result;
	}
	if (stripos($target_url, 'https://') !== 0) {
		$result['message'] = 'Webhook target URL must use HTTPS.';
		return $result;
	}

	$event_type = '';
	if (!empty($event['event_type'])) {
		$event_type = preg_replace('/[^a-zA-Z0-9_.-]/', '', (string)$event['event_type']);
	}
	if (empty($event_type)) {
		$result['message'] = 'Webhook event type is required.';
		return $result;
	}

	$payload = array();
	if (!empty($event['payload']) && is_array($event['payload'])) {
		$payload = $event['payload'];
	}

	$delivery_id = bin2hex(random_bytes(16));
	$created_at = time();
	$body = array(
		'id' => $delivery_id,
		'event_type' => $event_type,
		'created_at' => $created_at,
		'payload' => $payload
	);

	$payload_json = json_encode($body);
	if ($payload_json === false) {
		$result['message'] = 'Webhook payload could not be encoded.';
		return $result;
	}

	$payload_hash = hash('sha256', $payload_json);
	$signature = '';
	if (!empty($options['secret'])) {
		$signature = hash_hmac('sha256', $created_at . '.' . $payload_json, (string)$options['secret']);
	}

	$max_attempts = 5;
	if (!empty($options['max_attempts'])) {
		$max_attempts = (int)$options['max_attempts'];
	}
	if ($max_attempts < 1) {
		$max_attempts = 1;
	}

	$result['success'] = true;
	$result['message'] = 'Webhook delivery item built.';
	$result['data'] = array(
		'delivery_id' => $delivery_id,
		'target_url' => $target_url,
		'event_type' => $event_type,
		'payload_json' => $payload_json,
		'payload_hash' => $payload_hash,
		'signature' => $signature,
		'attempt_count' => 0,
		'max_attempts' => $max_attempts,
		'created_at' => $created_at,
		'next_attempt_at' => $created_at
	);

	return $result;
}