# Plugins (/docs/v/0.6.1/plugins)



A plugin bundles send-pipeline behavior into one reusable unit you mount with `plugins: [...]`. One plugin can register adapters, transform messages before validation, observe every attempt, and add typed helpers to the client — so shared policy lives in one place instead of being copied around every `createEmailClient` call.

```ts title="lib/email.ts"
import { createEmailClient } from "@opencoredev/email-sdk";
import { defaultsPlugin } from "@opencoredev/email-sdk/plugins/defaults";
import { observabilityPlugin } from "@opencoredev/email-sdk/plugins/observability";
import { resend } from "@opencoredev/email-sdk/resend";

export const email = createEmailClient({
  adapters: [resend({ apiKey: process.env.RESEND_API_KEY! })],
  plugins: [
    defaultsPlugin({ headers: { "X-App": "acme" }, replyTo: "support@acme.com" }),
    observabilityPlugin({ log: (event) => console.log(event.type, event.provider) }),
  ],
});
```

## What a plugin can do [#what-a-plugin-can-do]

A plugin is a plain object with an `id` and any combination of four capabilities:

| Capability | Field          | What it does                                                                                                                          |
| ---------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Add routes | `adapters`     | Registers providers into the client, same as the `adapters` option — how community provider packages ship one-call setup.             |
| Transform  | `middleware`   | `beforeSend` runs once per send, before validation, and can replace the message or options. `afterSend` / `onError` observe outcomes. |
| Observe    | `hooks`        | The same [hooks](/docs/v/0.6.1/concepts/hooks) as the client option — per-attempt, observe-only, failures swallowed.                          |
| Extend     | `extendClient` | Adds typed properties to the returned client, like `email.capture` from the capture plugin.                                           |

Middleware is the capability only plugins have: client-level `hooks` can watch a send, but only plugin middleware can change it.

## Built-in plugins [#built-in-plugins]

Three plugins ship with the SDK, each from its own entry point:

| Plugin                                                | Import                                         | Use it when                                                               |
| ----------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------- |
| [Defaults](/docs/v/0.6.1/plugins/built-in/defaults)           | `@opencoredev/email-sdk/plugins/defaults`      | Every send needs the same headers, tags, reply-to, or idempotency prefix. |
| [Observability](/docs/v/0.6.1/plugins/built-in/observability) | `@opencoredev/email-sdk/plugins/observability` | You want logs, metrics, or traces without leaking recipients or bodies.   |
| [Capture](/docs/v/0.6.1/plugins/built-in/capture)             | `@opencoredev/email-sdk/plugins/capture`       | Tests need to assert attempted sends, retries, responses, and errors.     |

## Registration rules [#registration-rules]

Plugins register in array order, and the order is deterministic:

1. Direct `adapters` register first, then each plugin's adapters in plugin order.
2. Middleware and hooks run in plugin order; plugin hooks run before client `hooks`.
3. Duplicate plugin ids and duplicate adapter names throw an `EmailValidationError` — mount two instances of the same plugin with distinct `id`s (and distinct extension keys, e.g. `capturePlugin({ id: "capture:audit", clientKey: "auditCapture" })`).

The full rules, event shapes, and error cases are in the [plugin API reference](/docs/v/0.6.1/plugins/api).

## Explore [#explore]

<Cards>
  <Card title="Defaults plugin" href="/docs/v/0.6.1/plugins/built-in/defaults" description="Merge org-wide headers, tags, reply-to, and idempotency prefixes into every send." />

  <Card title="Observability plugin" href="/docs/v/0.6.1/plugins/built-in/observability" description="Redacted sent/retry/error events for logs, metrics, and traces." />

  <Card title="Capture plugin" href="/docs/v/0.6.1/plugins/built-in/capture" description="Record send lifecycle events in tests via a typed client.capture store." />

  <Card title="Writing plugins" href="/docs/v/0.6.1/plugins/writing-plugins" description="Anatomy of EmailPlugin: hooks vs middleware, plugin adapters, typed extensions." />

  <Card title="Plugin API reference" href="/docs/v/0.6.1/plugins/api" description="EmailPlugin, EmailPluginContext, middleware events, and extension typing." />

  <Card title="Community plugins" href="/docs/v/0.6.1/plugins/community" description="Third-party adapters and plugins, plus how to list your own." />
</Cards>
