256:class CampaignRecipient(Base, TimestampMixin):
259-    id = Column(Integer, primary_key=True)
260-    campaign_id = Column(Integer, ForeignKey("campaigns.id", ondelete="CASCADE"), nullable=False, index=True)
261-    recipient_id = Column(Integer, ForeignKey("recipients.id", ondelete="CASCADE"), nullable=False)
262-    email = Column(String(320), nullable=False, index=True)
263-    status = Column(String(30), default="pending")  # pending|queued|processing|sent|temp_failed|perm_failed|suppressed|skipped
264-    generated_invoice_number = Column(String(100), default="")
265-    generated_amount = Column(Float)
266-    generated_shipping = Column(Float)
267-    generated_subject_id = Column(Integer, ForeignKey("subject_lines.id", ondelete="SET NULL"))
268-    generated_template_id = Column(Integer, ForeignKey("html_templates.id", ondelete="SET NULL"))
269-    generated_phone = Column(String(60), default="")
270-    assigned_account_id = Column(Integer, ForeignKey("gmail_accounts.id", ondelete="SET NULL"))
271-    attempt_count = Column(Integer, default=0)
272-    last_error = Column(Text, default="")
273-    msg_id = Column(String(200), default="")       # Gmail message id
274-    thread_id = Column(String(200), default="")    # Gmail thread id
275-    sent_at = Column(DateTime(timezone=True))
276-    seq = Column(Integer, default=0)               # ordering within campaign
