Recipe: Send email from agent frameworks

Wrap Notify in a LangChain, CrewAI, or Vercel AI SDK tool — one fetch to POST /api/email/send.

Use this when you’re building an agent in code (LangChain / LangGraph, CrewAI, Vercel AI SDK) and the model should call a send_email tool. Still transactional, not newsletters.

The framework only needs a tool that fetches Notify. Put NOTIFY_API_KEY in server env — never in the model prompt or client bundle.

Prerequisites

  • API key (prefer a domain-locked sending_access key)
  • Verified domain for production custom from
  • Node or Python runtime where your agent tools run

Shared send helper (TypeScript)

export async function sendWithNotify(input: {
  to: string;
  subject: string;
  html: string;
  from?: string;
}) {
  const res = await fetch('https://notify.cx/api/email/send', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': process.env.NOTIFY_API_KEY!
    },
    body: JSON.stringify({
      to: input.to,
      from: input.from ?? 'noreply@your-verified-domain.com',
      subject: input.subject,
      html: input.html
    })
  });
  const data = await res.json();
  if (!res.ok) throw new Error(JSON.stringify(data));
  return data;
}

html is preferred; message and text also work.

Vercel AI SDK

import { tool } from 'ai';
import { z } from 'zod';
import { sendWithNotify } from './notify';

export const sendEmailTool = tool({
  description:
    'Send a transactional email via Notify (password reset, receipt, notice). Not for newsletters.',
  inputSchema: z.object({
    to: z.string().email(),
    subject: z.string(),
    html: z.string()
  }),
  execute: async ({ to, subject, html }) => sendWithNotify({ to, subject, html })
});

Pass sendEmailTool in your tools map when calling generateText / streamText.

LangChain / LangGraph (JS)

import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { sendWithNotify } from './notify';

export const sendEmailTool = tool(
  async ({ to, subject, html }) => {
    const data = await sendWithNotify({ to, subject, html });
    return JSON.stringify(data);
  },
  {
    name: 'send_email',
    description:
      'Send a transactional email via Notify. Use for receipts, resets, notices — not newsletters.',
    schema: z.object({
      to: z.string().email(),
      subject: z.string(),
      html: z.string()
    })
  }
);

Bind the tool to your agent / graph the same way you bind any other tool. Python LangChain is the same idea: an @tool that httpx/requests POSTs to the same URL with x-api-key.

CrewAI

Expose a CrewAI tool (or a plain Python function registered as a tool) that POSTs to https://notify.cx/api/email/send with headers Content-Type: application/json and x-api-key: $NOTIFY_API_KEY, and a JSON body of to / from / subject / html. Keep the key in env; describe the tool as transactional-only so the agent doesn’t treat it as a blast mailer.

Guardrails

  • Run tools on the server (or a trusted worker), not in the browser.
  • Prefer asking the user (or a human approval step) before send in high-risk agents.
  • Production custom from needs a verified domain.

Sandbox vs production

Use sandbox vs production or POST /api/email/send/test until the domain is verified.

Where to next?