CREATE TABLE notification_channels ( id uuid PRIMARY KEY, name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 160), channel_type text NOT NULL CHECK (channel_type IN ('memory', 'webhook', 'email')), enabled boolean NOT NULL DEFAULT true, secret_ref text NOT NULL CHECK (char_length(secret_ref) BETWEEN 1 AND 255), configuration jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(configuration) = 'object'), revision bigint NOT NULL DEFAULT 1 CHECK (revision > 0), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE notification_outbox ( id uuid PRIMARY KEY, idempotency_key text NOT NULL UNIQUE CHECK (char_length(idempotency_key) BETWEEN 1 AND 255), channel_id uuid NOT NULL REFERENCES notification_channels(id) ON DELETE RESTRICT, event_type text NOT NULL CHECK (event_type IN ('firing', 'recovery', 'unknown')), subject text NOT NULL CHECK (char_length(subject) BETWEEN 1 AND 240), body text NOT NULL CHECK (char_length(body) BETWEEN 1 AND 8000), status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'delivering', 'retry', 'delivered', 'failed')), attempts integer NOT NULL DEFAULT 0 CHECK (attempts >= 0 AND attempts <= 10), next_attempt_at timestamptz NOT NULL DEFAULT now(), locked_until timestamptz, last_error text CHECK (last_error IS NULL OR char_length(last_error) <= 500), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), delivered_at timestamptz ); CREATE TABLE notification_deliveries ( id uuid PRIMARY KEY, outbox_id uuid NOT NULL REFERENCES notification_outbox(id) ON DELETE CASCADE, attempt integer NOT NULL CHECK (attempt > 0), status text NOT NULL CHECK (status IN ('delivering', 'delivered', 'failed')), error text CHECK (error IS NULL OR char_length(error) <= 500), occurred_at timestamptz NOT NULL DEFAULT now(), UNIQUE (outbox_id, attempt) ); CREATE INDEX notification_outbox_due_idx ON notification_outbox (next_attempt_at, id) WHERE status IN ('pending', 'retry'); CREATE INDEX notification_outbox_channel_idx ON notification_outbox (channel_id, status, updated_at DESC, id ASC); CREATE INDEX notification_deliveries_history_idx ON notification_deliveries (outbox_id, occurred_at DESC, id ASC);