Skip to content

feat(migrations): create email_send_log table#37

Merged
sweetmantech merged 1 commit into
mainfrom
feat/email-send-log-table
Jun 30, 2026
Merged

feat(migrations): create email_send_log table#37
sweetmantech merged 1 commit into
mainfrom
feat/email-send-log-table

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What

Adds email_send_log — a durable record of every POST /api/emails attempt (sent / send_failed / rejected), so a send is debuggable several days back without the ephemeral agent sandbox.

Why

Empty footer-only "Message from Recoup" emails reached customers and we could not recover what the API received (malformed bodies swallowed, route logged nothing, sandbox ephemeral). This table captures it.

Design (KISS — reviewed twice)

Append-only; loose ids, no FKs (like error_logs). 7 columns:

id, created_at, account_id, chat_id, status, resend_id. raw_body (full — bounded by the platform request-size limit), error + (created_at DESC) / (account_id) indexes.

  • chat_id only (TEXT, matching chats.id). chats.session_id reaches the session and sessions.account_id the account, so a separate session_id column would be redundant — and session_id isn't in the /api/emails contract anyway, so dropping it avoids extending that contract.
  • account_id denormalized (free at the API; the main query dimension).
  • Dropped earlier to_count / subject / html_length / text_length (derivable from raw_body).
  • Resend delivery lifecycle (delivered/bounced/opened) is a deliberate future concern — a separate email_events table keyed off resend_id, not more columns here. (A retention/purge job for raw_body (the real size/PII control, since we keep the full body now) and a chat_id index for the verify-the-send lookup are follow-ups alongside that work.)

Pairs with

Part of recoupable/chat#1829.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Email send attempts are now recorded with delivery status, sender, chat reference, timestamps, and related message details.
    • Added faster lookup support for recent entries and sender-based views.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1e2966c-3493-4734-bd67-f65853819596

📥 Commits

Reviewing files that changed from the base of the PR and between 6bcf3fa and 6834e42.

📒 Files selected for processing (1)
  • supabase/migrations/20260630210000_create_email_send_log.sql
📝 Walkthrough

Walkthrough

This PR adds a single Supabase migration creating the public.email_send_log table to record every POST /api/emails attempt, including status, account, chat ID, raw body, parsing flag, and error fields, plus indexes on created_at and account_id.

Changes

Email Send Log Migration

Layer / File(s) Summary
Table and index definition
supabase/migrations/20260630210000_create_email_send_log.sql
Creates public.email_send_log table with columns for account_id, chat_id, status, resend_id, body_parsed, raw_body, and error, plus indexes on created_at (descending) and account_id.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Poem

A table hops in, fresh and new,
To log each email sent and due.
With status flags and raw_body kept,
No failed delivery goes unswept.
Indexed neat by date and name—
This bunny logs the email game! 🐇📬

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a migration to create the email_send_log table.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/email-send-log-table

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabase Bot commented Jun 30, 2026

Copy link
Copy Markdown

Updates to Preview Branch (feat/email-send-log-table) ↗︎

Deployments Status Updated
Database Tue, 30 Jun 2026 21:11:34 UTC
Services Tue, 30 Jun 2026 21:11:34 UTC
APIs Tue, 30 Jun 2026 21:11:34 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Tue, 30 Jun 2026 21:11:34 UTC
Migrations Tue, 30 Jun 2026 21:11:35 UTC
Seeding Tue, 30 Jun 2026 21:11:35 UTC
Edge Functions Tue, 30 Jun 2026 21:11:35 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/migrations/20260630210000_create_email_send_log.sql">

<violation number="1" location="supabase/migrations/20260630210000_create_email_send_log.sql:11">
P1: Enable RLS on email_send_log before merge. Otherwise a public Supabase table containing captured email request bodies may be exposed or mutable through client roles depending on grants/policies.</violation>

<violation number="2" location="supabase/migrations/20260630210000_create_email_send_log.sql:16">
P2: Add a CHECK constraint for the three supported status values. This keeps the log queryable for sent/send_failed/rejected instead of silently accepting misspelled states.</violation>

<violation number="3" location="supabase/migrations/20260630210000_create_email_send_log.sql:18">
P2: Enforce the raw_body truncation in the schema, not only in the API writer. A CHECK length limit or bounded VARCHAR prevents accidental storage of full customer email payloads.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

-- so a send is debuggable several days back. Written by api `logEmailAttempt`
-- (recoupable/api PR #731). Append-only; no updated_at.

CREATE TABLE IF NOT EXISTS public.email_send_log (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Enable RLS on email_send_log before merge. Otherwise a public Supabase table containing captured email request bodies may be exposed or mutable through client roles depending on grants/policies.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260630210000_create_email_send_log.sql, line 11:

<comment>Enable RLS on email_send_log before merge. Otherwise a public Supabase table containing captured email request bodies may be exposed or mutable through client roles depending on grants/policies.</comment>

<file context>
@@ -0,0 +1,28 @@
+-- so a send is debuggable several days back. Written by api `logEmailAttempt`
+-- (recoupable/api PR #731). Append-only; no updated_at.
+
+CREATE TABLE IF NOT EXISTS public.email_send_log (
+    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
</file context>

created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
account_id UUID,
chat_id TEXT,
status TEXT NOT NULL, -- 'sent' | 'send_failed' | 'rejected'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Add a CHECK constraint for the three supported status values. This keeps the log queryable for sent/send_failed/rejected instead of silently accepting misspelled states.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260630210000_create_email_send_log.sql, line 16:

<comment>Add a CHECK constraint for the three supported status values. This keeps the log queryable for sent/send_failed/rejected instead of silently accepting misspelled states.</comment>

<file context>
@@ -0,0 +1,28 @@
+    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
+    account_id UUID,
+    chat_id TEXT,
+    status TEXT NOT NULL,            -- 'sent' | 'send_failed' | 'rejected'
+    body_parsed BOOLEAN NOT NULL DEFAULT false,
+    raw_body TEXT,                   -- truncated copy of the request body
</file context>

chat_id TEXT,
status TEXT NOT NULL, -- 'sent' | 'send_failed' | 'rejected'
body_parsed BOOLEAN NOT NULL DEFAULT false,
raw_body TEXT, -- truncated copy of the request body

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Enforce the raw_body truncation in the schema, not only in the API writer. A CHECK length limit or bounded VARCHAR prevents accidental storage of full customer email payloads.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260630210000_create_email_send_log.sql, line 18:

<comment>Enforce the raw_body truncation in the schema, not only in the API writer. A CHECK length limit or bounded VARCHAR prevents accidental storage of full customer email payloads.</comment>

<file context>
@@ -0,0 +1,28 @@
+    chat_id TEXT,
+    status TEXT NOT NULL,            -- 'sent' | 'send_failed' | 'rejected'
+    body_parsed BOOLEAN NOT NULL DEFAULT false,
+    raw_body TEXT,                   -- truncated copy of the request body
+    to_count INTEGER,
+    subject TEXT,
</file context>

@sweetmantech sweetmantech force-pushed the feat/email-send-log-table branch 5 times, most recently from 6bcf3fa to 0b683e8 Compare June 30, 2026 21:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
supabase/migrations/20260630210000_create_email_send_log.sql (1)

23-23: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider a CHECK constraint to enforce the status enum.

status is documented as 'sent' | 'send_failed' | 'rejected' only via comment, with no DB-level enforcement. A typo or future code change could insert an unexpected value silently.

♻️ Suggested constraint
-    status      TEXT NOT NULL,   -- 'sent' | 'send_failed' | 'rejected'
+    status      TEXT NOT NULL CHECK (status IN ('sent', 'send_failed', 'rejected')),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/migrations/20260630210000_create_email_send_log.sql` at line 23, The
email send log table currently documents status values only in a comment, so add
a database-level CHECK constraint in the migration that creates the table to
enforce the allowed values. Update the CREATE TABLE statement for the email send
log table so the status column is restricted to the existing enum-like set used
by the schema comment: sent, send_failed, and rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@supabase/migrations/20260630210000_create_email_send_log.sql`:
- Line 19: The `email_send_log` table definition uses `uuid_generate_v4()`
without ensuring the required extension is enabled. Update the migration in
`create_email_send_log.sql` to either add the missing `uuid-ossp` extension
setup before the `CREATE TABLE` statement or switch the `id` default to
`gen_random_uuid()` to match the existing schema pattern.

---

Nitpick comments:
In `@supabase/migrations/20260630210000_create_email_send_log.sql`:
- Line 23: The email send log table currently documents status values only in a
comment, so add a database-level CHECK constraint in the migration that creates
the table to enforce the allowed values. Update the CREATE TABLE statement for
the email send log table so the status column is restricted to the existing
enum-like set used by the schema comment: sent, send_failed, and rejected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 09a094ca-f41b-4c56-92a9-9e83ee406ac4

📥 Commits

Reviewing files that changed from the base of the PR and between fada52e and 6bcf3fa.

📒 Files selected for processing (1)
  • supabase/migrations/20260630210000_create_email_send_log.sql

-- lifecycle is a future, separate concern keyed off resend_id.

CREATE TABLE IF NOT EXISTS public.email_send_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -i "create extension.*uuid" supabase/migrations

Repository: recoupable/database

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- target migration ---'
cat -n supabase/migrations/20260630210000_create_email_send_log.sql

echo
echo '--- repository search: uuid_generate_v4 / uuid-ossp / gen_random_uuid / create extension ---'
rg -n -i "uuid_generate_v4|uuid-ossp|gen_random_uuid|create extension" .

echo
echo '--- migration files mentioning uuid ---'
rg -n -i "uuid_generate_v4|uuid-ossp|gen_random_uuid|create extension" supabase/migrations

Repository: recoupable/database

Length of output: 29884


Add the UUID extension or switch to gen_random_uuid()

uuid_generate_v4() depends on uuid-ossp, and there’s no migration here that enables it. This table creation will fail unless the extension is provisioned elsewhere; using gen_random_uuid() would match the rest of the schema.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/migrations/20260630210000_create_email_send_log.sql` at line 19, The
`email_send_log` table definition uses `uuid_generate_v4()` without ensuring the
required extension is enabled. Update the migration in
`create_email_send_log.sql` to either add the missing `uuid-ossp` extension
setup before the `CREATE TABLE` statement or switch the `id` default to
`gen_random_uuid()` to match the existing schema pattern.

Durable log of every POST /api/emails attempt (sent / send_failed / rejected)
so an empty/malformed send is debuggable within ~a week without the ephemeral
agent sandbox. Captures what the API received (raw_body, full) + the outcome
(status, resend_id) tied to account_id/chat_id/created_at. Append-only, loose
ids, no FKs (like error_logs). 7 columns — the simplest shape that meets the
observability goal. Written by recoupable/api PR #731.

Ref: recoupable/chat#1829

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sweetmantech sweetmantech force-pushed the feat/email-send-log-table branch from 0b683e8 to 6834e42 Compare June 30, 2026 21:10
@sweetmantech sweetmantech merged commit 1b0a059 into main Jun 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant