ALTER TABLE webhook_templates ADD COLUMN IF NOT EXISTS version INT NOT NULL DEFAULT 1; ALTER TABLE webhook_templates ADD COLUMN IF NOT EXISTS signing_secret TEXT NOT NULL DEFAULT ''; ALTER TABLE webhook_templates ADD COLUMN IF NOT EXISTS max_retries INT NOT NULL DEFAULT 3; ALTER TABLE webhook_templates DROP CONSTRAINT IF EXISTS webhook_templates_max_retries_chk; ALTER TABLE webhook_templates ADD CONSTRAINT webhook_templates_max_retries_chk CHECK (max_retries >= 0 AND max_retries <= 10) NOT VALID; CREATE TABLE IF NOT EXISTS webhook_template_versions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), template_id UUID NOT NULL REFERENCES webhook_templates(id) ON DELETE CASCADE, version INT NOT NULL, method TEXT NOT NULL DEFAULT 'POST', headers JSONB NOT NULL DEFAULT '{}', body_template TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(template_id, version) ); INSERT INTO webhook_template_versions (template_id, version, method, headers, body_template) SELECT wt.id, wt.version, wt.method, wt.headers, wt.body_template FROM webhook_templates wt WHERE NOT EXISTS ( SELECT 1 FROM webhook_template_versions wtv WHERE wtv.template_id = wt.id AND wtv.version = wt.version ); ALTER TABLE webhook_logs ADD COLUMN IF NOT EXISTS attempt_count INT NOT NULL DEFAULT 1; ALTER TABLE webhook_logs ADD COLUMN IF NOT EXISTS payload_preview TEXT NOT NULL DEFAULT ''; ALTER TABLE webhook_logs ADD COLUMN IF NOT EXISTS payload_truncated BOOLEAN NOT NULL DEFAULT false; CREATE TABLE IF NOT EXISTS webhook_dead_letters ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), template_id UUID NOT NULL REFERENCES webhook_templates(id) ON DELETE CASCADE, message_id UUID REFERENCES messages(id) ON DELETE SET NULL, attempt_count INT NOT NULL DEFAULT 0, last_status_code INT NOT NULL DEFAULT 0, error TEXT NOT NULL DEFAULT '', payload_preview TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_webhook_dead_letters_template ON webhook_dead_letters(template_id, created_at DESC);