# Mailcap > Hosted email- and message-capture for dev, staging, and beta. A Mailtrap > alternative, built by Elevora Technologies. Captures outbound email from > non-production environments into one searchable inbox instead of a real > recipient's — and doubles as your production send path via a > zero-branching SDK. This file is for AI coding agents integrating Mailcap into a codebase. If a user asks you to "add Mailcap" or "wire up email capture," follow this. ## The integration contract One import, one function call, identical in every environment. Env vars alone decide whether an email is captured or delivered for real — never a mode flag, never an if/else on environment. ```ts import { sendEmail } from "@elevora-tech/mailcap"; await sendEmail({ from, to, subject, html }); // or { text }, or { react } via React Email ``` ```bash # dev/staging -> captured, never delivered to a real recipient MAILCAP_API_KEY=mc_... MAILCAP_URL=https://mailcap.dev # production -> delivered for real (no capture key present) MAIL_PROVIDER=resend # or sendgrid | mailgun RESEND_API_KEY=re_... ``` Install: `npm i @elevora-tech/mailcap` (or pnpm/yarn), then set the env block above. That is the entire integration for a new project. ## Brownfield repos — an existing provider call site If the repo already calls a provider SDK directly (e.g. `mailgun.messages.create(...)`, `sgMail.send(...)`, `resend.emails.send(...)`) at one or more call sites, DO NOT rewrite those call sites. Instead wrap the client where it's constructed — every existing call site keeps working unchanged: ```ts import { wrapMailgunClient, wrapSendGridClient, wrapResendClient } from "@elevora-tech/mailcap"; // Before: const mailgun = new Mailgun(...).client({ key }); const mailgun = wrapMailgunClient(new Mailgun(...).client({ key })); // Every mailgun.messages.create(...) call site elsewhere is now captured in // dev/staging with zero further edits. ``` Decision rule for an agent integrating Mailcap: - No existing provider call site (new project, or all sends already funnel through one local function) -> use `sendEmail()` directly. - Existing direct provider calls at multiple call sites -> use the matching `wrap*Client` at the client-construction line only. - A call shape none of the wrappers fit -> the manual escape hatch: `isMailcapCaptureEnabled()` (boolean) + `captureRaw(providerName, payload)`. ## Safety guarantees - No mode flag can be misconfigured to capture in prod or deliver in dev — capture activates purely from `MAILCAP_API_KEY` being present. - Real delivery outside `NODE_ENV=production` is refused unless `MAILCAP_ALLOW_REAL_SEND=true` is explicitly set. - Misconfiguration (no capture key and no provider, or a provider named without its key) throws immediately, naming the missing variable — never a silent no-op. ## API (for CI, or an agent reading captured mail directly) Bearer-key auth, same key as the SDK's `MAILCAP_API_KEY`, scoped to one environment: - `GET https://mailcap.dev/api/emails?to=&latest=1` — most recent email to an address, including extracted `links` and `codes` (OTP-looking sequences). - `GET https://mailcap.dev/api/emails?subject=&from=` — filter by subject or sender substring. - `GET https://mailcap.dev/api/emails/:id` — fetch one email by id. - `DELETE https://mailcap.dev/api/emails?id=` — clean up after a test. This is what lets a project's own CI do "sign up -> fetch OTP -> complete verification" headlessly. ## MCP server Mailcap also exposes an MCP server at `https://mailcap.dev/api/mcp` (streamable HTTP) so an AI agent can query captured mail directly instead of shelling out to curl. Two ways to authenticate: - **OAuth** (recommended for clients that support it — Claude.ai's remote connector, Claude Desktop, Claude Code): just add the server URL above. The client discovers everything else via `https://mailcap.dev/.well-known/oauth-authorization-server` and `https://mailcap.dev/.well-known/oauth-protected-resource`, prompts a login + consent screen, and is done. This path is user-scoped, not tied to one environment: `list_projects` and `list_environments` let the agent find what it has access to first. For Claude Code specifically: `claude mcp add --transport http mailcap https://mailcap.dev/api/mcp`, then run `/mcp` inside Claude Code to sign in when prompted. - **Environment API key** (for clients that don't support OAuth yet, or headless/CI use): `Authorization: Bearer `, scoped to that one environment, no discovery needed. Tools either way: `list_emails`, `get_email`, `get_latest_for`, `delete_email` (plus `list_projects`/`list_environments` under OAuth). ## Links - Sign up: https://mailcap.dev/signup - Install prompt (copy-paste for an AI coding agent): https://mailcap.dev/install - SDK source: https://github.com/Elevora-Tech/mailcap-sdk