Transactional email provider integration, templates, and delivery rules.
Skill by Mihir-Rabari
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/mihir-rabari-email-8fbd63
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: productivity
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/email
What's inside
- Skill name
email- Description
- Transactional email provider integration, templates, and delivery rules.
- License
- MIT
- Instructions
- 57 lines in SKILL.md · view on GitHub
Agent Skills spec issues
- files: referenced but missing: templates/index.ts
Files (1)
- SKILL.md
SKILL.md (first lines)
# Email Skill
## 1. When to Use
Use this skill when sending any email: verification codes, password resets, invitations, notices, or integrating an email provider (Resend).
## 2. How It Is Built
All of it lives in `packages/shared/src/email/`. Nothing else in the codebase talks to a provider.
| Piece | File | Job |
|---|---|---|
| `EmailService` | `email-service.ts` | The one entry point. Validates the recipient, derives a plain-text part, picks a transport. |
| Transports | `transports.ts` | `ResendTransport` (real), `ConsoleTransport` (prints, for development), `MemoryTransport` (records, for tests). |
| Templates | `templates/` | One shared layout plus one function per email. Each returns `{ subject, html, text }`. |
| `Mailer` | `apps/api/src/lib/mailer.ts` | App layer: knows the product name and the web origin for links, and how a route should treat a failed send. |
The API creates the service in `plugins/services.ts` and exposes it as `fastify.email`. Routes use `createMailer(fastify)`, not `fastify.email` directly.
## 3. Configuration
- `RESEND_API_KEY`, `RESEND_FROM` in `.env`. `WEB_URL` is the origin used for every link in an email.
- **No key, development or test**: the console transport prints the message (including any code or link) to the API log. Signup, reset and invite flows work with no provider account.
- **No key, anywhere else**: sending throws `EmailNotConfiguredError`. Production fails loudly instead of silently dropping mail, and `POST /auth/otp/request` answers `503 EMAIL_NOT_CONFIGURED`.
- `email.isConfigured()` is true only for a real provider. The console transport reports `false`, which is how the API knows it may return a `devCode` to the signup form. A `devCode` is never returned in production.
## 4. The Emails
| Template | Sent when | Failure handling |
|---|---|---|
| `verificationCodeEmail` | `POST /auth/otp/request` | Awaited. In production a failure is a 5xx; in development the code is returned as `devCode` instead. |
| `welcomeEmail` | A workspace is created | Background. The account exists either way. |
| `passwordResetEmail` | `POST /auth/password/forgot` | Background, so response time does not reveal whether the address has an account. |
| `passwordChangedEmail` | Password reset or change | Background. A security notice, so a takeover is noticed. |
| `inviteEmail` | `POST /iam/users`, `POST /iam/users/:id/invite` | Awaited; the response carries `inviteSent`. The account exists regardless, and the administrator can resend. |
## 5. Invariants
- **Escape everything user-controlled.** Templates build HTML from workspace names and display names. Interpolate through `escapeHtml`; never concatenate raw input into markup.
- **Links are absolute http(s) only.** `safeUrl` rejects `javascript:` and relative URLs; `renderLayout` calls it for every button.
- **No header injection.** Recipients are validated with a strict email schema and subjects may not contain line breaks. `EmailService` enforces both.
- **Tokens in links are single-use and expiring.** They come from `ActionTokenService` (`@packages/auth`), which stores only a SHA-256 of each token. A reset token lives an hour, an invite three days.
- **Never put a password in an email.** Invitations carry a link to *choose* one.
- **Never reveal whether an address has an account.** `POST /auth/password/forgot` answers identically either way and sends nothing for unknown, suspended or disabled accounts.
- **Do not log addresses or message bodies** outside the console transport, which exists for development.
- **A `text` part is always sent**, derived from the HTML. Do not write a template whose meaning lives only in markup.
## 6. Adding an Email
1. Add a function to `templates/index.ts` that calls `renderLayout` and returns `finish(subject, html)`.
2. Decide how a failure should behave (table above) and pick `mailer.send` or `mailer.sendInBackground` accordingly.
3. Test it with `MemoryTransport` (see Testing).
## 7. Mandatory Testing Expectations
- Never call a live provider in automated tests. Use `MemoryTransport` through `buildApp({}, { email })`; the route tests in `apps/api/src/tenancy.test.ts` show the pattern.
- Each template: subject, html and text all carry the essential content (code, link).
- Escaping: a hostile name (`<script>`, `"><img onerror>`) must come out inert.
- Recipient validation, including a CRLF injection attempt.
- Behaviour with no provider: refuses in production, prints in development.
- Delivery failure: the invite still creates the account and reports `inviteSent: false`.
- Enumeration: forgot-password sends nothing and answers identically for an unknown address.From Mihir-Rabari/create-odoo-app, MIT.
More from Mihir-Rabari/create-odoo-app
- 0.561score
design
Mihir-Rabari
Visual design law for the generated app — screen structure, colour tokens, typography, icons, spacing, and the states every screen owes the user.
SkillAFreshness: AClaude Code +9 - 0.558score
api
Mihir-Rabari
Fastify route development, Zod type providers, OpenAPI documentation, structured logging, and error responses.
SkillAFreshness: AClaude Code +9 - 0.558score
architecture
Mihir-Rabari
Monorepo package boundaries, layering rules, and extension patterns.
SkillAFreshness: AClaude Code +9 - 0.558score
authorization
Mihir-Rabari
Identity and Access Management, policy evaluation engine, declarative route guards, and permissions.
SkillAFreshness: AClaude Code +9 - 0.558score
authentication
Mihir-Rabari
Password hashing (scrypt), server-side session tokens, HTTP-only cookies, and auth events.
SkillAFreshness: AClaude Code +9 - 0.558score
frontend
Mihir-Rabari
Next.js App Router, shadcn/ui components, Tailwind CSS, TanStack Query, AuthContext, and permission-aware UI.
SkillAFreshness: AClaude Code +9 - 0.558score
database
Mihir-Rabari
PostgreSQL schema conventions, Drizzle ORM, migrations, deterministic seeding, and database integrity.
SkillAFreshness: AClaude Code +9 - 0.528score
testing
Mihir-Rabari
Testing doctrine, pyramid (unit, integration, API, security, smoke), AAA pattern, and coverage enforcement.
SkillAFreshness: AClaude Code +9 - 0.518score
observability
Mihir-Rabari
Structured logging, request correlation, Prometheus metrics, health probes, and sensitive data redaction.
SkillBFreshness: BClaude Code +9 - 0.518score
security
Mihir-Rabari
Zero-trust input rules, server-side authorization, credential protection, and adversarial security testing.
SkillBFreshness: BClaude Code +9 - 0.518score
storage
Mihir-Rabari
S3/MinIO StorageService abstraction, bucket management, and object storage conventions.
SkillBFreshness: BClaude Code +9 - 0.518score
validation
Mihir-Rabari
Runtime Zod validation schemas, business bounds, date ordering, and pagination limits.
SkillBFreshness: BClaude Code +9
Signals
- Stars
- 0
- Forks
- 1
- Open issues
- 3
- Last commit (this folder)
- Oct 2, 2026 (26 days ago)
- Last release
- Sep 4, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/Mihir-Rabari/create-odoo-app
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Same skill elsewhere
Copies of this SKILL.md published by others; search shows only one of them.
More from Mihir-Rabari
- 0.561score
agent
Mihir-Rabari
AI chat agent that acts as the signed-in user, tool registry, staged write actions with confirmation, permission-filtered tools, and prompt-injection rules.
SkillAFreshness: AClaude Code +9 - 0.561score
design
Mihir-Rabari
Visual design law for the generated app — screen structure, colour tokens, typography, icons, spacing, and the states every screen owes the user.
SkillAFreshness: AClaude Code +9 - 0.558score
authentication
Mihir-Rabari
Password hashing (scrypt), server-side session tokens, HTTP-only cookies, and auth events.
SkillAFreshness: AClaude Code +9 - 0.558score
architecture
Mihir-Rabari
Monorepo package boundaries, layering rules, and extension patterns.
SkillAFreshness: AClaude Code +9 - 0.558score
api
Mihir-Rabari
Fastify route development, Zod type providers, OpenAPI documentation, structured logging, and error responses.
SkillAFreshness: AClaude Code +9 - 0.558score
database
Mihir-Rabari
PostgreSQL schema conventions, Drizzle ORM, migrations, deterministic seeding, and database integrity.
SkillAFreshness: AClaude Code +9
Cite this page
email skill. Agent Mart. https://agentmart.dev/skills/mihir-rabari-email-8fbd63 (last updated 2026-10-24)