> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paubox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Amazon SES

> Switch from Amazon SES to the Paubox Email API for HIPAA-compliant transactional email.

## Why migrate

AWS will sign a Business Associate Agreement (BAA) that covers SES as a service, but the BAA does not protect Protected Health Information (PHI) in the body of the mail once it leaves AWS. SES TLS enforcement defaults to opportunistic (`TlsPolicy: OPTIONAL`), so messages silently fall back to plaintext when the recipient server doesn't advertise STARTTLS — the customer carries the encryption-in-transit obligation.

Paubox closes that gap with always-on TLS, an automatic Secure Portal fallback when the recipient cannot accept encrypted mail, and a BAA signed with every customer. The send-side API is REST/JSON and the migration is straightforward as most patterns map one-to-one.

## What stays the same

* REST API over HTTPS with JSON request bodies (when using SESv2; SES v1 form-encoded callers will move to JSON)
* Domain authentication: SPF and DKIM records required
* SMTP as an alternative to the HTTP API
* Webhook-based event notifications for delivery status
* Per-domain sending configuration

## Key differences

| Dimension           | Amazon SES (SESv2)                                                             | Paubox Email API                                                                                                             |
| :------------------ | :----------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
| Base URL            | `https://email.{region}.amazonaws.com/v2/email/outbound-emails`                | `https://api.paubox.net/v1/$PAUBOX_ENDPOINT/messages`                                                                        |
| Region selection    | Required (`us-east-1`, `eu-west-1`, …)                                         | Single global endpoint, no region                                                                                            |
| Auth method         | AWS SigV4 (access key + secret + region, or IAM role)                          | Bearer token — `Authorization: Bearer $PAUBOX_API_KEY`                                                                       |
| SDK                 | `@aws-sdk/client-sesv2`, `boto3` `sesv2`, `Aws::SESV2`, etc.                   | `paubox-node`, `paubox-python3`, `paubox_ruby`, `paubox-php`, `paubox-java`, `paubox-csharp` — or plain `fetch` / `requests` |
| Request body shape  | `Content.Simple.Subject.Data` / `Body.Html.Data` / `Destination.ToAddresses[]` | `data.message.headers.subject` / `content["text/html"]` / `recipients[]`                                                     |
| Attachments         | Raw MIME via `SendRawEmail` only                                               | First-class `data.message.attachments[]` with base64 `content`                                                               |
| Bulk send           | `SendBulkEmail` (templated only, up to 50 destinations)                        | `/bulk_messages` endpoint, recommended max 50 per request                                                                    |
| Templates           | SES templates (`CreateTemplate` API, Handlebars-style syntax)                  | Paubox Dynamic Templates (`/dynamic_templates` CRUD, Handlebars)                                                             |
| SMTP host           | `email-smtp.{region}.amazonaws.com`                                            | `smtp.paubox.com`                                                                                                            |
| SMTP credentials    | IAM-user-derived SMTP username + password (separate from the AWS API key)      | Literal string `apikey` as username, Paubox API key as password                                                              |
| SMTP port           | 25, 465, 587, or 2587                                                          | 587 (also supports 465, 25)                                                                                                  |
| TLS enforcement     | `TlsPolicy: OPTIONAL \| REQUIRE` (per configuration set)                       | Always-on, cannot be disabled                                                                                                |
| Delivery feedback   | SNS topic subscription → HTTPS endpoint                                        | Paubox webhooks (HTTPS POST directly to your endpoint)                                                                       |
| Message ID returned | `MessageId`                                                                    | `sourceTrackingId`                                                                                                           |

## Send a single email

<CodeGroup>
  ```js Amazon SES (before) theme={null}
  import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";

  const ses = new SESv2Client({ region: process.env.AWS_REGION });

  await ses.send(
    new SendEmailCommand({
      FromEmailAddress: "provider@yourclinic.com",
      Destination: { ToAddresses: ["patient@example.com"] },
      Content: {
        Simple: {
          Subject: { Data: "Your appointment summary" },
          Body: { Html: { Data: "<p>See attached.</p>" } },
        },
      },
    }),
  );
  ```

  ```bash Paubox (after) theme={null}
  curl -X POST https://api.paubox.net/v1/$PAUBOX_ENDPOINT/messages \
    -H "Authorization: Bearer $PAUBOX_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "data": {
        "message": {
          "recipients": ["patient@example.com"],
          "headers": {
            "subject": "Your appointment summary",
            "from": "provider@yourclinic.com"
          },
          "content": {
            "text/html": "<p>See attached.</p>"
          }
        }
      }
    }'
  ```
</CodeGroup>

<Note>
  **Note:**

  `$PAUBOX_ENDPOINT` is the username shown on your [Paubox Email API > Settings](https://next.paubox.com/emailapi/settings) page — not your email address.
</Note>

<Note>
  **Note:**

  SES uses AWS SigV4 over the AWS credential chain (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, or an IAM role). Paubox uses a single Bearer token. After cutover, remove the AWS SDK dependency and any AWS credentials scoped to `ses:SendEmail` from your runtime environment.
</Note>

## SMTP configuration

<CodeGroup>
  ```js Amazon SES (before) theme={null}
  const transporter = nodemailer.createTransport({
    host: "email-smtp.us-east-1.amazonaws.com",
    port: 587,
    auth: {
      user: process.env.SES_SMTP_USERNAME, // IAM-derived SMTP username
      pass: process.env.SES_SMTP_PASSWORD, // IAM-derived SMTP password
    },
  });
  ```

  ```js Paubox (after) theme={null}
  const transporter = nodemailer.createTransport({
    host: "smtp.paubox.com",
    port: 587,
    auth: {
      user: "apikey",
      pass: process.env.PAUBOX_API_KEY,
    },
  });
  ```
</CodeGroup>

<Tip>
  **Tip:**

  SES SMTP credentials are not the same as your AWS API key — they are derived from a dedicated IAM user via the SES console. After traffic has moved to Paubox, delete that IAM user so the SMTP credential is fully revoked.
</Tip>

## Webhook event mapping

SES does not push HTTP webhooks directly — bounces, complaints, and deliveries are published to an SNS topic that fans out to an HTTPS subscription. Paubox replaces both layers with a direct HTTPS POST to your endpoint.

| SES SNS event                      | Paubox event key                 | Notes                                                                          |
| :--------------------------------- | :------------------------------- | :----------------------------------------------------------------------------- |
| `Delivery`                         | `api_mail_log_delivered`         |                                                                                |
| `Open`                             | `api_mail_log_opened`            |                                                                                |
| `Bounce` (`bounceType: Permanent`) | `api_mail_log_permanent_failure` |                                                                                |
| `Bounce` (`bounceType: Transient`) | `api_mail_log_temporary_failure` |                                                                                |
| `Click`                            | — no equivalent                  | Poll click data via [Get message receipt](/email-api/message-receipt)          |
| `Complaint`                        | — no equivalent                  | Manage unsubscribes in the Paubox dashboard                                    |
| `Reject`                           | — no equivalent                  | Paubox returns rejections synchronously in the POST response, not as a webhook |
| `Send`                             | — no equivalent                  | `delivered` confirms acceptance; remove handler                                |

<Note>
  **Note:**

  Click tracking is available by polling `GET /message_receipt?sourceTrackingId=...` — it is not delivered as a push webhook event.
</Note>

<Note>
  **Note:**

  Paubox webhooks POST directly to your HTTPS endpoint. There is no intermediary SNS topic, so the JSON envelope is flatter than the SES → SNS → subscription payload. Configure endpoints in the Paubox dashboard under Email API → Webhooks. See the [Webhooks reference](/email-api/webhooks) for the payload shape.
</Note>

## Template migration

If you use SES templates (`CreateTemplate` / `SendTemplatedEmail` / `SendBulkTemplatedEmail`), recreate each one as a Paubox Dynamic Template. Both systems use Handlebars-style `{{variable}}` syntax, so most template bodies port verbatim.

<Steps>
  <Step title="Export each SES template">
    Run `aws ses get-template --template-name <name>` (or `aws sesv2 get-email-template`) and capture the `Subject`, `HtmlPart`, and `TextPart` fields.
  </Step>

  <Step title="Create the Paubox Dynamic Template">
    POST each template to `/dynamic_templates` with the body remapped to Paubox's shape. See the [Dynamic templates reference](/email-api/dynamic-templates) for the request format.
  </Step>

  <Step title="Switch from `SendTemplatedEmail` to templated messages">
    Replace `SendTemplatedEmail` / `SendBulkTemplatedEmail` calls with `POST /messages` (or `/bulk_messages`) using the `template_id` and `template_variables` fields.
  </Step>
</Steps>

## Migration checklist

<Steps>
  <Step title="Sign a BAA with Paubox">
    Required before go-live. Contact Paubox to initiate the Business Associate Agreement.
  </Step>

  <Step title="Create an account and verify your sending domain">
    Add your domain on the [Paubox Email API > Settings](https://next.paubox.com/emailapi/settings) page and complete the TXT record verification. See the [Quickstart guide](/email-api/quickstart) for step-by-step instructions.
  </Step>

  <Step title="Generate a Paubox API key">
    From the Settings page, generate an API key and note your customer endpoint (`https://api.paubox.net/v1/YOUR_USERNAME`).
  </Step>

  <Step title="Replace the AWS SDK call with a Paubox request">
    Remove `@aws-sdk/client-ses` / `@aws-sdk/client-sesv2` (or the `boto3` / `Aws::SESV2` equivalent) and call Paubox via `fetch` / one of the Paubox SDKs. Restructure the request body to use the `data.message` shape shown above.
  </Step>

  <Step title="Remove AWS credentials from runtime config">
    Drop `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, and any IAM role permissions scoped to `ses:SendEmail` / `ses:SendRawEmail` / `ses:SendBulkEmail`.
  </Step>

  <Step title="Update SMTP credentials">
    If you use the SMTP path, update `host` to `smtp.paubox.com`, set `username` to the literal string `apikey`, and `password` to your Paubox API key. Delete the IAM user that was created for SES SMTP credentials.
  </Step>

  <Step title="Recreate SES templates as Paubox Dynamic Templates">
    See [Template migration](#template-migration) above.
  </Step>

  <Step title="Tear down the SNS topic and HTTPS subscription">
    Remap your bounce / complaint / delivery handler to the Paubox webhook payload using the [event mapping table](#webhook-event-mapping) above, then delete the SNS topic and configuration-set event destination used for SES feedback.
  </Step>

  <Step title="Remove any TLS policy overrides">
    Paubox enforces TLS unconditionally — remove `TlsPolicy: OPTIONAL` or `TlsPolicy: REQUIRE` from any SES configuration set you were referencing.
  </Step>

  <Step title="Send a test message">
    Confirm delivery using the [Get message receipt](/email-api/message-receipt) endpoint with the `sourceTrackingId` returned from your test send.
  </Step>

  <Step title="Swap DNS records">
    Replace SES SPF and DKIM records with the Paubox records shown in your Settings page.
  </Step>

  <Step title="Revoke AWS credentials">
    Once traffic has fully moved to Paubox, delete the IAM user and access keys used by the SES integration.
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Quickstart guide" icon="rocket" href="/email-api/quickstart">
    Full setup walkthrough from account creation to first send
  </Card>

  <Card title="Webhooks reference" icon="webhook" href="/email-api/webhooks">
    Configure delivery event notifications
  </Card>

  <Card title="Batch send" icon="layer-group" href="/email-api/bulk-messages">
    Send up to 50 messages in a single request
  </Card>

  <Card title="SMTP API" icon="server" href="/email-api/smtp">
    Connect via SMTP instead of REST
  </Card>
</CardGroup>
