diff --git a/db/db.ddl b/db/db.ddl
index 8fa36b32660939a5c85ca30d3f5e52b9ab457385..72df2e9107df3d68e0aa59d21bef597f2ff2df94 100644
--- a/db/db.ddl
+++ b/db/db.ddl
@@ -26,6 +26,7 @@ CREATE TABLE users (
 	password_hash	varchar(255)	DEFAULT NULL,			-- heš hesla (je-li nastaveno)
 	note		text		NOT NULL DEFAULT '',		-- poznámka viditelná pro orgy
 	email_notify	boolean		NOT NULL DEFAULT true		-- přeje si dostávat mailové notifikace
+	dsn_id		int		DEFAULT NULL,			-- mailová nedoručenka (REFERENCES později)
 );
 
 -- Uživatel s user_id=0 vždy existuje a je to systémový uživatel s právy admina.
@@ -166,6 +167,7 @@ CREATE TABLE contests (
 	place_id		int		NOT NULL REFERENCES places(place_id),
 	state			round_state	NOT NULL DEFAULT 'preparing',	-- používá se, pokud round.state='delegate', jinak kopíruje round.state
 	scoretable_id		int		DEFAULT NULL,			-- odkaz na snapshot představující oficiální výsledkovou listinu soutěže
+										-- (REFERENCES později)
 	tex_hacks		text		NOT NULL DEFAULT '',		-- speciální nastavení pro sazbu výsledkovky
 	online_submit		boolean		NOT NULL DEFAULT true,		-- účastníkům je povoleno elektronické odevzdávání
 	UNIQUE (round_id, place_id)
@@ -415,6 +417,7 @@ CREATE TABLE reg_requests (
 	captcha_token	varchar(255)	DEFAULT NULL,			-- token pro fázi 1 registrace
 	email_token	varchar(255)	UNIQUE NOT NULL,		-- token pro fázi 2 registrace
 	user_id		int		DEFAULT NULL REFERENCES users(user_id) ON DELETE CASCADE,
+	dsn_id		int		DEFAULT NULL,			-- mailová nedoručenka (REFERENCES později)
 	client		varchar(255)	NOT NULL			-- kdo si registraci vyžádal
 );
 
@@ -506,3 +509,21 @@ CREATE TABLE sent_email (
 	sent_at		timestamp with time zone	NOT NULL DEFAULT CURRENT_TIMESTAMP,
 	PRIMARY KEY (user_id, key)
 );
+
+-- Mailové nedoručenky (Delivery Status Notifications)
+
+CREATE TABLE email_dsns (
+	dsn_id		serial		PRIMARY KEY,
+	token		text		UNIQUE NOT NULL,
+	user_id		int		DEFAULT NULL REFERENCES users(user_id) ON DELETE CASCADE ,
+	reg_id		int		DEFAULT NULL REFERENCES reg_requests(reg_id) ON DELETE CASCADE,
+	arrived_at	timestamp with time zone	NOT NULL DEFAULT CURRENT_TIMESTAMP,
+	message_id	text		DEFAULT NULL,			-- MessageID nedoručenky
+	status		text		DEFAULT NULL,			-- SMTP enhanced status code (e.g., 4.1.1)
+	remote_mta	text		DEFAULT NULL,			-- IP adresa nebo doménové jméno odesilatele DSN
+	diag_code	text		DEFAULT NULL,			-- zdůvodnění od odesilatele DSN
+	CHECK (user_id IS NOT NULL OR reg_id IS NOT NULL)
+);
+
+ALTER TABLE reg_requests ADD CONSTRAINT "reg_requests_dsn_id" FOREIGN KEY (dsn_id) REFERENCES email_dsns(dsn_id) ON DELETE SET NULL;
+ALTER TABLE users ADD CONSTRAINT "users_dsn_id" FOREIGN KEY (dsn_id) REFERENCES email_dsns(dsn_id) ON DELETE SET NULL;
diff --git a/db/upgrade-20250123.sql b/db/upgrade-20250123.sql
new file mode 100644
index 0000000000000000000000000000000000000000..bb93a480451876dda35baed99b896213dc3ffd3e
--- /dev/null
+++ b/db/upgrade-20250123.sql
@@ -0,0 +1,21 @@
+ALTER TABLE users ADD COLUMN
+	dsn_id		int		DEFAULT NULL;
+
+ALTER TABLE reg_requests ADD COLUMN
+	dsn_id		int		DEFAULT NULL;
+
+CREATE TABLE email_dsns (
+	dsn_id		serial		PRIMARY KEY,
+	token		text		UNIQUE NOT NULL,
+	user_id		int		DEFAULT NULL REFERENCES users(user_id) ON DELETE CASCADE ,
+	reg_id		int		DEFAULT NULL REFERENCES reg_requests(reg_id) ON DELETE CASCADE,
+	arrived_at	timestamp with time zone	NOT NULL DEFAULT CURRENT_TIMESTAMP,
+	message_id	text		DEFAULT NULL,			-- MessageID nedoručenky
+	status		text		DEFAULT NULL,			-- SMTP extended status code (e.g., 4.1.1)
+	remote_mta	text		DEFAULT NULL,			-- IP adresa nebo doménové jméno odesilatele DSN
+	diag_code	text		DEFAULT NULL,			-- zdůvodnění od odesilatele DSN
+	CHECK (user_id IS NOT NULL OR reg_id IS NOT NULL)
+);
+
+ALTER TABLE reg_requests ADD CONSTRAINT "reg_requests_dsn_id" FOREIGN KEY (dsn_id) REFERENCES email_dsns(dsn_id) ON DELETE SET NULL;
+ALTER TABLE users ADD CONSTRAINT "users_dsn_id" FOREIGN KEY (dsn_id) REFERENCES email_dsns(dsn_id) ON DELETE SET NULL;
diff --git a/mo/db.py b/mo/db.py
index 4e2dfe2a2368aaa5cf6824c9dd5887c72f5cc4e5..d11cce4190345bd971d7ea00ca7c90e0fb5056a8 100644
--- a/mo/db.py
+++ b/mo/db.py
@@ -441,9 +441,11 @@ class User(Base):
     password_hash = Column(String(255), server_default=text("NULL::character varying"))
     note = Column(Text, nullable=False, server_default=text("''::text"))
     email_notify = Column(Boolean, nullable=False, server_default=text("true"))
+    dsn_id = Column(Integer, ForeignKey('email_dsns.dsn_id'))
 
     roles = relationship('UserRole', primaryjoin='UserRole.user_id == User.user_id', back_populates='user')
     participants = relationship('Participant', primaryjoin='Participant.user_id == User.user_id', back_populates='user')
+    dsn = relationship('EmailDSN', primaryjoin='EmailDSN.dsn_id == User.dsn_id', back_populates='user', viewonly=True)
 
     def full_name(self) -> str:
         return self.first_name + ' ' + self.last_name
@@ -932,9 +934,11 @@ class RegRequest(Base):
     email = Column(Text)
     email_token = Column(Text, nullable=False, unique=True)
     user_id = Column(Integer, ForeignKey('users.user_id'))
+    dsn_id = Column(Integer, ForeignKey('email_dsns.dsn_id'))
     client = Column(Text, nullable=False)
 
     user = relationship('User')
+    dsn = relationship('EmailDSN', primaryjoin='EmailDSN.dsn_id == RegRequest.dsn_id')
 
 
 class RegionDescendant(Base):
@@ -1045,6 +1049,23 @@ class SentEmail(Base):
     user = relationship('User')
 
 
+class EmailDSN(Base):
+    __tablename__ = 'email_dsns'
+
+    dsn_id = Column(Integer, primary_key=True, server_default=text("nextval('email_dsn_dsn_id_seq'::regclass)"))
+    token = Column(Text, nullable=False, unique=True)
+    user_id = Column(Integer, ForeignKey('users.user_id'))
+    reg_id = Column(Integer, ForeignKey('reg_requests.reg_id'))
+    arrived_at = Column(DateTime(True), nullable=False, server_default=text("CURRENT_TIMESTAMP"))
+    message_id = Column(Text)
+    status = Column(Text)
+    remote_mta = Column(Text)
+    diag_code = Column(Text)
+
+    user = relationship('User', primaryjoin='User.user_id == EmailDSN.user_id')
+    reg = relationship('RegRequest', primaryjoin='RegRequest.reg_id == EmailDSN.reg_id')
+
+
 _engine: Optional[Engine] = None
 _session: Optional[Session] = None
 flask_db: Any = None