ALTER TABLE gateway.api_keys ADD COLUMN IF NOT EXISTS portal_user_id uuid REFERENCES gateway.portal_users(id) ON DELETE SET NULL; CREATE INDEX IF NOT EXISTS api_keys_portal_user_idx ON gateway.api_keys (portal_user_id, enabled, created_at DESC) WHERE portal_user_id IS NOT NULL; CREATE TABLE IF NOT EXISTS gateway.prompt_favorites ( portal_user_id uuid NOT NULL REFERENCES gateway.portal_users(id) ON DELETE CASCADE, prompt_id uuid NOT NULL REFERENCES gateway.prompt_templates(id) ON DELETE CASCADE, created_at timestamptz NOT NULL DEFAULT clock_timestamp(), PRIMARY KEY (portal_user_id, prompt_id) ); CREATE TABLE IF NOT EXISTS gateway.model_access_requests ( id uuid PRIMARY KEY, portal_user_id uuid NOT NULL REFERENCES gateway.portal_users(id) ON DELETE CASCADE, provider_code text NOT NULL DEFAULT '', model text NOT NULL CHECK (length(model) BETWEEN 1 AND 512), reason text NOT NULL DEFAULT '' CHECK (length(reason) <= 4000), requested_rpm integer NOT NULL DEFAULT 60 CHECK (requested_rpm BETWEEN 1 AND 100000), requested_monthly_tokens bigint NOT NULL DEFAULT 0 CHECK (requested_monthly_tokens >= 0), status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','rejected','cancelled')), decision_note text NOT NULL DEFAULT '' CHECK (length(decision_note) <= 4000), decided_by uuid REFERENCES gateway.admin_accounts(id) ON DELETE SET NULL, decided_at timestamptz, created_at timestamptz NOT NULL DEFAULT clock_timestamp(), updated_at timestamptz NOT NULL DEFAULT clock_timestamp() ); CREATE UNIQUE INDEX IF NOT EXISTS model_access_requests_one_pending_idx ON gateway.model_access_requests (portal_user_id, provider_code, model) WHERE status = 'pending'; CREATE INDEX IF NOT EXISTS model_access_requests_status_time_idx ON gateway.model_access_requests (status, created_at DESC); COMMENT ON COLUMN gateway.api_keys.portal_user_id IS 'Optional portal owner. It enables least-privilege self-service usage and audit views without exposing key material.';