# Quickstart (/docs/v/0.6.1/getting-started/quickstart)



This page takes you from zero to a verified production-ready send path: one adapter, one `send()` call, a CLI check, and a fallback route.

<Steps>
  <Step>
    ### Install the package [#install-the-package]

    <PackageInstallTabs />

    The SDK and CLI run server-side on Node 20+ and Bun 1.1+. See [Install](/docs/v/0.6.1/getting-started/install) for CLI-only usage.
  </Step>

  <Step>
    ### Create a client [#create-a-client]

    These docs use Resend first — shortest setup, broad field support. Any [adapter](/docs/v/0.6.1/adapters) works the same way; only the factory and credentials change.

    Set `RESEND_API_KEY`, then:

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

    export const email = createEmailClient({
      adapters: [resend({ apiKey: process.env.RESEND_API_KEY! })],
    });
    ```
  </Step>

  <Step>
    ### Send an email [#send-an-email]

    ```ts
    const result = await email.send({
      from: "Acme <hello@acme.com>",
      to: "user@example.com",
      subject: "Welcome to Acme",
      text: "Your account is ready.",
    });

    console.log(result.provider); // "resend" — the adapter that handled the send
    console.log(result.id); // provider message id, when the API returns one
    ```

    `send` resolves with a normalized [response](/docs/v/0.6.1/reference/client#response): `provider` tells you which adapter actually delivered, `id`/`messageId` carry the provider's message id, and `raw` keeps the untouched provider payload.
  </Step>

  <Step>
    ### Verify from the CLI [#verify-from-the-cli]

    Check the environment, then validate a full message without sending:

    ```bash
    RESEND_API_KEY="re_..." npx email-sdk doctor --adapter resend
    ```

    ```bash
    npx email-sdk send \
      --adapter resend \
      --from "Acme <hello@acme.com>" \
      --to "user@example.com" \
      --subject "Hello" \
      --text "It works" \
      --dry-run
    ```

    `--dry-run` validates the message shape and the adapter's [field support](/docs/v/0.6.1/adapters/field-support) and prints the message it would send. Drop the flag for one real smoke send.
  </Step>

  <Step>
    ### Add a fallback route [#add-a-fallback-route]

    A second adapter keeps simple transactional email flowing when the primary fails. SMTP is built in (no Nodemailer) and pairs well with API providers for plain text/HTML messages:

    ```ts title="lib/email.ts"
    import { createEmailClient } from "@opencoredev/email-sdk";
    import { resend } from "@opencoredev/email-sdk/resend";
    import { smtp } from "@opencoredev/email-sdk/smtp";

    export const email = createEmailClient({
      adapters: [
        resend({ apiKey: process.env.RESEND_API_KEY! }),
        smtp({
          host: process.env.SMTP_HOST!,
          port: 587,
          auth: { user: process.env.SMTP_USER!, pass: process.env.SMTP_PASS! },
        }),
      ],
      fallback: ["smtp"],
    });
    ```

    Your application code does not change — `email.send(...)` now tries Resend, then SMTP.

    <Callout type="warn" title="Fallbacks must match your message shape">
      A fallback only helps if the backup adapter supports the fields you send. SMTP cannot carry
      tags, metadata, or attachments — sends using those fields throw instead of silently dropping
      data. Check [field support](/docs/v/0.6.1/adapters/field-support) when picking backup routes.
    </Callout>
  </Step>
</Steps>

## Next [#next]

<Cards>
  <Card title="Production send pipeline" href="/docs/v/0.6.1/guides/production-send-pipeline" description="Retries, idempotency keys, observability, and tests around this exact setup." />

  <Card title="Fallbacks and retries" href="/docs/v/0.6.1/concepts/fallbacks-and-retries" description="How route order, retry backoff, and error classification actually work." />

  <Card title="All adapters" href="/docs/v/0.6.1/adapters" description="20 providers with per-adapter setup, options, and CLI smoke tests." />

  <Card title="Test email behavior" href="/docs/v/0.6.1/guides/test-email-behavior" description="Assert sends in unit tests with memory adapters and the capture plugin." />
</Cards>
