Skip to content

Research what a switch from Resend needs from posthog-node and a skill #522

Closed

Description

@Silthus

Question

What does a switch from Resend to PostHog need from posthog-node and from an agent skill, React Email templates included?

Michael wants transactional email from code to feel like Resend, so a team can switch with one skill: posthog.emails.send(...) mirrors the Resend Node SDK closely enough, and React Email templates work directly. It doesn't need a 100% mirror.

Answer, with links to Resend's docs and source (resend/resend-node, resend/react-email) and file:line evidence from the posthog-node draft PostHog/posthog-js#5312 (branch feat/node-emails-send on the Silthus fork):

  1. Resend's send surface. The resend.emails.send fields (from, to, subject, html, text, react, replyTo, cc, bcc, headers, tags, attachments, scheduledAt, template), the { data, error } return, the error names and HTTP codes, the idempotency key, batch.send, and Resend's hosted templates with variables. Which of these does a typical transactional codebase use? Which does Think through the Elements tab PostHog/posthog#5312 already cover, and how close are the names?
  2. React Email. How does Resend render react? Name the package, its peer dependencies, and whether it renders in the SDK or on the server. What do render() and toPlainText produce? Can a React Email component render once into a stored PostHog template whose per-send values are Liquid {{ variables.* }} placeholders, or does every send need its own render? Where does that break (conditionals, loops, formatting of props)? How do other providers that support React Email (for example Loops, Plunk, useSend, Postmark) handle it?
  3. Keys and senders at Resend. Resend API key permissions (full_access, sending_access) and the per-domain restriction. How do Resend users keep staging from sending from the production domain? What do Resend's docs recommend?
  4. Migration. What would a "move from Resend to PostHog" skill do, step by step, in a codebase: find the call sites, map fields, move templates, move domains and DNS (can a domain stay verified at both providers during the switch?), swap the key, and prove a send? Do Resend or its competitors publish such migration guides or agent skills? Link them.
  5. Gaps. The smallest list of posthog-node additions that makes a typical Resend call site a mechanical rewrite.

Write the report on branch research/resend-parity-sdk. Resolve this ticket with a summary and the branch pointer.

Activity

  1. Silthus commented on Oct 20, 2026

    @Silthus
    OwnerAuthor

    Runtime: T3 Code
    Task: node:delegated-task:command%3Amcp%3Acbc48805-d1ea-4076-9165-d9cb3e20ac16%3Adelegate-task%3Amap461-r522-v1
    Child thread: thread:delegated-task:command%3Amcp%3Acbc48805-d1ea-4076-9165-d9cb3e20ac16%3Adelegate-task%3Amap461-r522-v1
    Cast: Sonnet 5.5 high. Wall-time cap: 60 minutes.

  2. self-assigned this
    on Oct 20, 2026
  3. Silthus commented on Oct 20, 2026

    @Silthus
    OwnerAuthor

    Claimed by the research sub-agent (Sonnet 5.5 high). Report goes on branch research/resend-parity-sdk.

  4. Silthus commented on Oct 20, 2026

    @Silthus
    OwnerAuthor

    Resolution

    Report: research-resend-parity-sdk.md on branch research/resend-parity-sdk (commit 8c794abc870). No PR, per the map's research convention.

    1. Send surface. #5312 shares Resend's { data: { id }, error } return and the never-throws rule. Its input is { template, to, data, distinctId }, so it has no from, subject, html, text, react, replyTo or idempotency key. Resend's error field is name; the draft's is code. A public-code sample shows html: and react: as the most common content fields, replyTo and text common, and attachments, tags, hosted templates, cc, idempotency keys, batch and scheduling each rare.
    2. React Email. Resend renders react in the SDK on every send, with a dynamic import of the optional peer @react-email/render (peers react and react-dom). The SDK sends HTML only; the Resend API builds the plain text. Rendering once into a stored {{ variables.* }} template works only for substitution-only emails. A local probe showed that quotes inside Liquid expressions are HTML-escaped, and JS conditionals, loops and formatting run at render time. Resend's own hosted templates have the same limit. Postmark, Plunk, useSend and Loops all treat React Email as a send-time renderer.
    3. Keys. Resend keys are full_access or sending_access, plus a domain_id on sending keys. Resend recommends the narrowest scope, one key per service and one domain per key. No Resend page prescribes a staging or production split. PostHog's phs_ key has scopes only and no sender field, and a template's sender is fixed. Isolation today means one project per environment.
    4. Migration. No competitor publishes a coding-agent skill for leaving Resend. Bird's "Hand this to your agent" prompt and Postmark's guide are the closest. The report lists an eight-step skill and the DNS collision points. Both providers' records can coexist if the MAIL FROM label differs from Resend's and the root SPF record is merged, not duplicated.
    5. Gaps. from, inline subject + html/text (a backend decision), to as an array, replyTo, an idempotencyKey argument, error.name, and later react on top of inline content. cc, bcc, attachments, tags, scheduledAt, headers and batch stay out; the types should refuse them.

    Two things for Michael:

    • Inline content or template-only decides whether a react: or html: call site is a one-line rewrite or a template conversion. The first reverses the 2026-10-16 re-chart.
    • The draft posts to /messaging/send/email/ and by_key routes that are not on upstream/master (2099bfc9e6f). That matches the planned by-key stack, not shipped code.

    Not verified: how the React Email preview app maps props to {{{VAR}}} when it uploads to Resend, the error Resend returns for a domain-restricted key on another domain, and Resend's exact DNS record names.

    The map's Decisions-so-far has a pointer to this answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions