{
  "feature": "webhook-ingestion",
  "version": "1.0.0",
  "description": "Receive and process incoming webhooks from external services with signature verification (HMAC/RSA), replay protection, idempotent deduplication, and async handler routing",
  "category": "integration",
  "tags": [
    "webhooks",
    "ingestion",
    "signature-verification",
    "replay-protection",
    "events"
  ],
  "fields": [
    {
      "name": "source",
      "type": "text",
      "required": true,
      "label": "Webhook source identifier (e.g., provider name or integration ID)"
    },
    {
      "name": "event_type",
      "type": "text",
      "required": true,
      "label": "Event type from the external service (e.g., payment.completed)"
    },
    {
      "name": "payload",
      "type": "json",
      "required": true,
      "label": "Raw webhook payload body"
    },
    {
      "name": "signature",
      "type": "text",
      "required": true,
      "label": "Cryptographic signature from the webhook header"
    },
    {
      "name": "signature_algorithm",
      "type": "select",
      "required": false,
      "label": "Signature verification algorithm",
      "options": [
        {
          "value": "hmac_sha256",
          "label": "HMAC-SHA256"
        },
        {
          "value": "hmac_sha512",
          "label": "HMAC-SHA512"
        },
        {
          "value": "rsa_sha256",
          "label": "RSA-SHA256"
        }
      ]
    },
    {
      "name": "timestamp",
      "type": "datetime",
      "required": true,
      "label": "Webhook timestamp from the provider header"
    },
    {
      "name": "event_id",
      "type": "text",
      "required": true,
      "label": "Unique event identifier for idempotency deduplication"
    },
    {
      "name": "processing_status",
      "type": "select",
      "required": false,
      "label": "Current processing status",
      "options": [
        {
          "value": "received",
          "label": "Received"
        },
        {
          "value": "verified",
          "label": "Verified"
        },
        {
          "value": "processing",
          "label": "Processing"
        },
        {
          "value": "completed",
          "label": "Completed"
        },
        {
          "value": "failed",
          "label": "Failed"
        }
      ]
    },
    {
      "name": "handler_id",
      "type": "text",
      "required": false,
      "label": "Registered handler that processes this event type"
    },
    {
      "name": "retry_count",
      "type": "number",
      "required": false,
      "label": "Number of handler retry attempts"
    },
    {
      "name": "webhook_secret",
      "type": "hidden",
      "required": true,
      "label": "Shared secret or public key for signature verification"
    }
  ],
  "rules": {
    "signature_verification": [
      "Every incoming webhook must include a cryptographic signature in the request header",
      "HMAC-SHA256: compute HMAC of raw body using shared secret; compare with provided signature",
      "RSA-SHA256: verify signature using provider's public key against raw body",
      "Reject webhook immediately if signature does not match (do not process payload)",
      "Use constant-time comparison to prevent timing attacks on signature verification"
    ],
    "replay_protection": [
      "Reject webhooks with timestamp older than 5 minutes from current server time",
      "Clock skew tolerance of 30 seconds to account for network delays",
      "Store processed event_ids for deduplication window (minimum 24 hours)"
    ],
    "idempotency": [
      "Deduplicate by event_id; if event_id already processed, return 200 without re-processing",
      "Deduplication window is 24 hours; events older than 24 hours may be reprocessed"
    ],
    "processing": [
      "Store raw payload before processing (audit trail and replay capability)",
      "Route events to registered handlers based on source + event_type combination",
      "Process handlers asynchronously; return 200 immediately to webhook sender",
      "Failed handlers retry 3 times with exponential backoff (1s, 4s, 16s)"
    ],
    "response": [
      "Always return 200 status to webhook sender after signature verification",
      "Return 401 for invalid signatures; return 400 for malformed payloads",
      "Provider will retry on non-2xx responses; avoid unnecessary retries by responding quickly"
    ]
  },
  "outcomes": {
    "webhook_received_and_verified": {
      "priority": 1,
      "given": [
        {
          "field": "signature",
          "source": "request",
          "operator": "exists"
        },
        {
          "field": "timestamp",
          "source": "request",
          "operator": "exists"
        },
        {
          "field": "event_id",
          "source": "request",
          "operator": "exists"
        },
        "Signature verification passes against raw payload",
        "Timestamp is within 5-minute replay window"
      ],
      "then": [
        {
          "action": "create_record",
          "type": "webhook_event",
          "fields": [
            "source",
            "event_type",
            "payload",
            "event_id",
            "timestamp"
          ]
        },
        {
          "action": "set_field",
          "target": "processing_status",
          "value": "verified"
        },
        {
          "action": "emit_event",
          "event": "webhook.received",
          "payload": [
            "source",
            "event_type",
            "event_id"
          ]
        },
        {
          "action": "emit_event",
          "event": "webhook.verified",
          "payload": [
            "source",
            "event_type",
            "event_id"
          ]
        }
      ],
      "result": "Webhook stored and verified; 200 returned to sender; async processing initiated"
    },
    "webhook_processed_successfully": {
      "priority": 2,
      "given": [
        {
          "field": "processing_status",
          "source": "db",
          "operator": "eq",
          "value": "verified"
        },
        "Handler registered for source + event_type combination",
        "Handler executes without error"
      ],
      "then": [
        {
          "action": "set_field",
          "target": "processing_status",
          "value": "completed"
        },
        {
          "action": "emit_event",
          "event": "webhook.processed",
          "payload": [
            "source",
            "event_type",
            "event_id",
            "handler_id"
          ]
        }
      ],
      "result": "Webhook event processed by registered handler; status marked completed"
    },
    "webhook_handler_failed_with_retry": {
      "priority": 3,
      "given": [
        {
          "field": "processing_status",
          "source": "db",
          "operator": "in",
          "value": [
            "verified",
            "processing"
          ]
        },
        "Handler throws an error during processing",
        {
          "field": "retry_count",
          "source": "db",
          "operator": "lt",
          "value": 3
        }
      ],
      "then": [
        {
          "action": "set_field",
          "target": "retry_count",
          "value": "retry_count + 1"
        },
        {
          "action": "set_field",
          "target": "processing_status",
          "value": "processing"
        }
      ],
      "result": "Handler retried with exponential backoff; retry count incremented"
    },
    "webhook_handler_failed_permanently": {
      "priority": 4,
      "error": "WEBHOOK_HANDLER_FAILED",
      "given": [
        {
          "field": "retry_count",
          "source": "db",
          "operator": "gte",
          "value": 3
        }
      ],
      "then": [
        {
          "action": "set_field",
          "target": "processing_status",
          "value": "failed"
        },
        {
          "action": "emit_event",
          "event": "webhook.failed",
          "payload": [
            "source",
            "event_type",
            "event_id",
            "handler_id",
            "error_message"
          ]
        }
      ],
      "result": "Handler exhausted all retries; webhook marked as failed for manual investigation"
    },
    "webhook_signature_invalid": {
      "priority": 5,
      "error": "WEBHOOK_SIGNATURE_INVALID",
      "given": [
        "Computed signature does not match provided signature"
      ],
      "then": [
        {
          "action": "emit_event",
          "event": "webhook.failed",
          "payload": [
            "source",
            "event_type",
            "error_reason"
          ]
        }
      ],
      "result": "401 returned; webhook rejected without storing or processing payload"
    },
    "webhook_replay_detected": {
      "priority": 6,
      "error": "WEBHOOK_REPLAY_DETECTED",
      "given": [
        "Timestamp is older than 5 minutes from current server time"
      ],
      "then": [
        {
          "action": "emit_event",
          "event": "webhook.failed",
          "payload": [
            "source",
            "event_id",
            "timestamp",
            "error_reason"
          ]
        }
      ],
      "result": "400 returned; stale webhook rejected as potential replay attack"
    },
    "webhook_duplicate_event": {
      "priority": 7,
      "given": [
        "event_id already exists in processed events store"
      ],
      "then": [],
      "result": "200 returned without re-processing; duplicate event silently ignored"
    },
    "webhook_no_handler": {
      "priority": 8,
      "error": "WEBHOOK_NO_HANDLER",
      "given": [
        "No handler registered for the source + event_type combination"
      ],
      "then": [
        {
          "action": "set_field",
          "target": "processing_status",
          "value": "completed"
        },
        {
          "action": "emit_event",
          "event": "webhook.received",
          "payload": [
            "source",
            "event_type",
            "event_id"
          ]
        }
      ],
      "result": "Webhook stored for audit but no processing performed; 200 returned to sender"
    }
  },
  "errors": [
    {
      "code": "WEBHOOK_SIGNATURE_INVALID",
      "status": 401,
      "message": "Webhook signature verification failed. Payload may have been tampered with."
    },
    {
      "code": "WEBHOOK_REPLAY_DETECTED",
      "status": 400,
      "message": "Webhook timestamp is too old. Possible replay attack detected."
    },
    {
      "code": "WEBHOOK_HANDLER_FAILED",
      "status": 500,
      "message": "Webhook handler failed after all retry attempts. Manual investigation required."
    },
    {
      "code": "WEBHOOK_NO_HANDLER",
      "status": 400,
      "message": "No handler registered for this event type. Webhook stored but not processed."
    },
    {
      "code": "WEBHOOK_PAYLOAD_MALFORMED",
      "status": 400,
      "message": "Webhook payload could not be parsed. Expected valid JSON body."
    }
  ],
  "events": [
    {
      "name": "webhook.received",
      "payload": [
        "source",
        "event_type",
        "event_id",
        "timestamp"
      ]
    },
    {
      "name": "webhook.verified",
      "payload": [
        "source",
        "event_type",
        "event_id"
      ]
    },
    {
      "name": "webhook.processed",
      "payload": [
        "source",
        "event_type",
        "event_id",
        "handler_id"
      ]
    },
    {
      "name": "webhook.failed",
      "payload": [
        "source",
        "event_type",
        "event_id",
        "error_code",
        "error_message"
      ]
    }
  ],
  "related": [
    {
      "feature": "payment-gateway",
      "type": "recommended",
      "reason": "Receive payment status updates via provider webhooks"
    },
    {
      "feature": "email-service",
      "type": "optional",
      "reason": "Receive email delivery status notifications via webhooks"
    },
    {
      "feature": "message-queue",
      "type": "recommended",
      "reason": "Queue verified webhooks for async handler processing"
    },
    {
      "feature": "api-gateway",
      "type": "optional",
      "reason": "Route incoming webhooks through gateway for rate limiting"
    }
  ],
  "agi": {
    "goals": [
      {
        "id": "secure_ingestion",
        "description": "Process incoming webhooks securely with zero tolerance for forged or replayed events",
        "success_metrics": [
          {
            "metric": "signature_verification_rate",
            "target": "100%",
            "measurement": "All processed webhooks have valid signatures"
          },
          {
            "metric": "replay_rejection_rate",
            "target": "100%",
            "measurement": "All duplicate event_ids within the dedup window are rejected"
          },
          {
            "metric": "processing_latency",
            "target": "< 200ms to acknowledge",
            "measurement": "Time from webhook receipt to HTTP 200 response"
          }
        ],
        "constraints": [
          {
            "type": "security",
            "description": "Reject any webhook that fails signature verification — no fallback to unverified processing",
            "negotiable": false
          },
          {
            "type": "performance",
            "description": "Acknowledge receipt quickly, process asynchronously",
            "negotiable": true
          }
        ]
      }
    ],
    "autonomy": {
      "level": "supervised",
      "human_checkpoints": [
        "before registering a new webhook source with signing keys",
        "before disabling signature verification for any source"
      ],
      "escalation_triggers": [
        "invalid_signature_rate > 5",
        "replay_attempt_rate > 10",
        "handler_error_rate > 3"
      ]
    },
    "verification": {
      "invariants": [
        "every processed webhook has a verified signature",
        "no event_id is processed more than once within the deduplication window",
        "webhook payloads are never modified between receipt and handler delivery",
        "signing keys are stored encrypted and never logged"
      ],
      "acceptance_tests": [
        {
          "scenario": "valid webhook processing",
          "given": "a webhook with valid HMAC signature and unique event_id",
          "when": "the webhook is received",
          "expect": "HTTP 200 returned and payload dispatched to correct handler"
        },
        {
          "scenario": "invalid signature rejection",
          "given": "a webhook with tampered signature",
          "when": "the webhook is received",
          "expect": "HTTP 401 returned and event logged as security incident"
        },
        {
          "scenario": "replay protection",
          "given": "a webhook with an event_id that was already processed",
          "when": "the duplicate webhook is received",
          "expect": "HTTP 200 returned (idempotent) but handler is not invoked again"
        },
        {
          "scenario": "handler failure retry",
          "given": "a valid webhook whose handler fails with a transient error",
          "when": "the handler returns a 5xx status",
          "expect": "webhook queued for retry with exponential backoff up to max_retries"
        }
      ]
    },
    "safety": {
      "action_permissions": [
        {
          "action": "process_webhook",
          "permission": "autonomous"
        },
        {
          "action": "register_source",
          "permission": "supervised"
        },
        {
          "action": "disable_source",
          "permission": "human_required"
        },
        {
          "action": "rotate_signing_keys",
          "permission": "human_required"
        },
        {
          "action": "purge_dedup_cache",
          "permission": "supervised",
          "cooldown": "1h"
        }
      ]
    }
  }
}