Skip to content

fix(mailer): correct AgentMail webhook message schema field requirements#5434

Merged
waleedlatif1 merged 1 commit into
stagingfrom
fix/mailer-inbox-ingestion
Jul 6, 2026
Merged

fix(mailer): correct AgentMail webhook message schema field requirements#5434
waleedlatif1 merged 1 commit into
stagingfrom
fix/mailer-inbox-ingestion

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • AgentMail's webhook payload omits cc, attachments, and html entirely when empty (not sent as []/null), but our zod schema marked them required — so any inbound email without a CC recipient or attachment (the common case) failed schema validation before a task row was ever created
  • This has silently broken Sim Mailer inbox ingestion for every real inbound email since the feature shipped (confirmed via prod DB: zero mothership_inbox_task rows created across 13 workspaces that enabled the inbox over 4 months)
  • Renamed from_ to from to match AgentMail's documented field name (harmless — AgentMail actually sends both keys today, but from is the documented one)
  • Relaxed subject/text to optional as well, matching AgentMail's actual API contract

Type of Change

  • Bug fix

Testing

  • tsc and biome clean on all changed files
  • Verified live against AgentMail using disposable throwaway inboxes (created + torn down): captured a real webhook payload for a plain single-recipient email, confirmed the old schema rejects it (fails on cc/attachments/html) and the fixed schema parses it successfully

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

cc, attachments, and html are omitted from AgentMail's webhook payload
when empty rather than sent as empty arrays/null, but the zod schema
marked them required. Every inbound email without a CC recipient or
attachment (the common case) failed schema validation before a task
row was ever created, silently breaking Sim Mailer inbox ingestion
since the feature shipped. Renames from_ to from to match AgentMail's
documented field name.
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 6, 2026 4:52pm

Request Review

@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes webhook ingestion validation for a production mail feature; incorrect optional/required fields could still drop or mis-handle messages, but the fix aligns with verified live payloads and is narrowly scoped.

Overview
Fixes Sim Mailer inbox ingestion by aligning the AgentMail webhook Zod schema and types with what AgentMail actually sends. Previously, typical inbound emails failed validation before any mothership_inbox_task row was created because cc, attachments, and html were required even when AgentMail omits those keys entirely.

The schema now marks cc, attachments, html, text, and subject as optional, matching empty-field omission in real payloads. The sender field is renamed from from_ to from (documented AgentMail name), with the AgentMail webhook route and rejection-task helpers updated to read message.from.

Reviewed by Cursor Bugbot for commit 88e025f. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a schema mismatch between AgentMail's actual webhook payloads and the Zod schema used to validate them, which caused every inbound email to silently fail validation. The from_ field is renamed to the documented from, and cc, html, attachments, subject, and text are relaxed to optional to reflect that AgentMail omits absent fields entirely rather than sending null/[].

  • Schema and interface alignment (webhooks.ts, types.ts): cc, html, attachments, subject, and text are now optional; from_ becomes from throughout.
  • Route handler update (route.ts): All three message.from_ call sites are updated to message.from; downstream optional-field handling with ?. and || null fallbacks is already correct.
  • No downstream breakage: executor.ts already uses fullMessage.attachments || [], and all DB insertions use message.subject || '(no subject)' and optional-chaining for text/html/attachments.

Confidence Score: 5/5

Safe to merge — the changes are a targeted correctness fix with no new logic paths and no behavioral regressions on the happy path.

All three field-optionality changes are handled correctly by the existing downstream code: optional-chaining is used for text/html/attachments, and || '(no subject)' guards the subject. The from_ → from rename is applied consistently across the schema, interface, and all three call sites in the route handler. The executor already uses fullMessage.attachments || [] which handles the newly-optional field. No other consumers of AgentMailMessage reference the renamed field.

No files require special attention — the changes are narrow and internally consistent.

Important Files Changed

Filename Overview
apps/sim/lib/api/contracts/webhooks.ts Relaxes agentMailMessageSchema: renames from_ to from, marks cc/attachments/html/subject/text as optional to match AgentMail's actual API contract; all downstream usages already guard against undefined/null.
apps/sim/lib/mothership/inbox/types.ts Updates AgentMailMessage interface to mirror the schema changes — from_ renamed to from, cc/attachments/html/subject/text made optional — keeping the TypeScript type in sync with the Zod schema.
apps/sim/app/api/webhooks/agentmail/route.ts Updates three message.from_ references to message.from; optional field accesses (text, html, attachments, subject) already use proper optional-chaining and

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant AM as AgentMail
    participant WH as POST /api/webhooks/agentmail
    participant ZOD as Zod Schema
    participant DB as Database (mothershipInboxTask)
    participant TRG as Trigger.dev / Local Executor

    AM->>WH: POST (Svix-signed webhook)
    WH->>WH: readAgentMailBody() – size-bounded
    WH->>WH: Svix signature verification (all workspace candidates)
    WH->>ZOD: agentMailEnvelopeSchema.safeParse()
    WH->>ZOD: agentMailMessageSchema.safeParse()
    note over ZOD: Before: cc/attachments/html required → reject<br/>After: all three optional → accept plain emails
    ZOD-->>WH: messageResult.data (AgentMailMessage)
    WH->>WH: extractSenderEmail(message.from)
    WH->>WH: Duplicate / entitlement / rate-limit / sender checks
    WH->>DB: "INSERT mothershipInboxTask (status=received)"
    WH->>TRG: tasks.trigger(mothership-inbox-execution, taskId)
    TRG->>DB: "UPDATE status=processing"
    TRG->>TRG: executeInboxTask(taskId)
    TRG->>DB: "UPDATE status=completed|failed"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant AM as AgentMail
    participant WH as POST /api/webhooks/agentmail
    participant ZOD as Zod Schema
    participant DB as Database (mothershipInboxTask)
    participant TRG as Trigger.dev / Local Executor

    AM->>WH: POST (Svix-signed webhook)
    WH->>WH: readAgentMailBody() – size-bounded
    WH->>WH: Svix signature verification (all workspace candidates)
    WH->>ZOD: agentMailEnvelopeSchema.safeParse()
    WH->>ZOD: agentMailMessageSchema.safeParse()
    note over ZOD: Before: cc/attachments/html required → reject<br/>After: all three optional → accept plain emails
    ZOD-->>WH: messageResult.data (AgentMailMessage)
    WH->>WH: extractSenderEmail(message.from)
    WH->>WH: Duplicate / entitlement / rate-limit / sender checks
    WH->>DB: "INSERT mothershipInboxTask (status=received)"
    WH->>TRG: tasks.trigger(mothership-inbox-execution, taskId)
    TRG->>DB: "UPDATE status=processing"
    TRG->>TRG: executeInboxTask(taskId)
    TRG->>DB: "UPDATE status=completed|failed"
Loading

Reviews (1): Last reviewed commit: "fix(mailer): correct AgentMail webhook m..." | Re-trigger Greptile

@waleedlatif1 waleedlatif1 merged commit 8bc0d97 into staging Jul 6, 2026
29 of 30 checks passed
@waleedlatif1 waleedlatif1 deleted the fix/mailer-inbox-ingestion branch July 6, 2026 17:29
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