Skip to content
Writing
NewVercel AI SDKNext.jsTypeScriptReact EmailApp Router

Vercel AI SDK 3.x & Next.js 15: Streaming Email Tool Calling Guide

Build modern AI chat interfaces in Next.js 15 that can draft, review, and dispatch transactional emails using the Vercel AI SDK.

Unified tool calling in modern Next.js 15 applications

The Vercel AI SDK provides a standard interface for connecting LLMs to external APIs across React Server Components, Server Actions, and Route Handlers.

When giving a chat assistant the ability to send emails, using tool() with a Zod schema guarantees type safety and enables client-side tool call rendering with interactive confirmation states.

Feature / RequirementServer Route Handler (/api/chat)Server Action Direct Call
Streaming ModelSSE via toDataStreamResponse()Action response return
Client RenderingAutomatic toolInvocation streamingManual hook state tracking
Cold StartNone — hosted HTTP endpointNone — hosted HTTP endpoint
Human ConfirmationClient intercepts invocation before dispatchRequires multi-step form token

Defining the email tool with Zod schema validation

In your Next.js route handler (app/api/chat/route.ts), define the email tool using the tool helper from the ai package:

TYPESCRIPT
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o'),
    messages,
    tools: {
      sendEmail: tool({
        description: 'Send a transactional email notification to a team member or customer.',
        parameters: z.object({
          to: z.string().email().describe('Destination email address'),
          subject: z.string().min(3).describe('Subject line of the email'),
          text: z.string().min(5).describe('Plain-text body content'),
        }),
        execute: async ({ to, subject, text }) => {
          const res = await fetch('https://api.sadasend.com/emails', {
            method: 'POST',
            headers: {
              'Authorization': `Bearer ${process.env.SADASEND_API_KEY}`,
              'Content-Type': 'application/json',
              'Idempotency-Key': crypto.randomUUID(),
            },
            body: JSON.stringify({ to, subject, text }),
          });
          const data = await res.json();
          return { success: res.ok, messageId: data.id, status: data.status };
        },
      }),
    },
  });

  return result.toDataStreamResponse();
}

Handling confirmation UI on the frontend with useChat

Because sending an email is a state-changing side effect, the frontend can inspect toolInvocations to display a rich preview card with a Confirm Send button before triggering the final API call:

TSX
'use client';
import { useChat } from 'ai/react';

export function EmailAssistant() {
  const { messages, input, handleInputChange, handleSubmit, addToolResult } = useChat();

  return (
    <div className="chat-container">
      {messages.map((m) => (
        <div key={m.id} className="message-bubble">
          <p>{m.content}</p>
          {m.toolInvocations?.map((inv) => {
            if (inv.toolName === 'sendEmail' && !('result' in inv)) {
              return (
                <div key={inv.toolCallId} className="confirmation-card">
                  <h4>Confirm Outbound Email</h4>
                  <p><strong>To:</strong> {inv.args.to}</p>
                  <p><strong>Subject:</strong> {inv.args.subject}</p>
                  <button onClick={() => addToolResult({ toolCallId: inv.toolCallId, result: { approved: true } })}>
                    Approve & Dispatch
                  </button>
                </div>
              );
            }
            return null;
          })}
        </div>
      ))}
    </div>
  );
}

Edge runtime isolation and credential scoping

Configure your API route with export const runtime = "edge" to ensure sub-millisecond cold starts across global points of presence. Use a scoped SadaSend API key restricted to your verified sender domains.

Free plan

Building AI agents that send email?

Scoped API keys, per-key recipient allowlists, approval mode and a hosted MCP server with ten tools — on the free plan, without a card.