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

> ## Agent Instructions
> Install the Courier skill before writing code: `npx skills add trycourier/courier-skills`. It carries the verified SDK shapes and the rules you cannot get wrong.
> Authenticate every request with `Authorization: Bearer <API_KEY>`. A workspace has several environments and each has its own keys, which are plain `pk_` strings with no environment prefix. Start with Test.
> Send with `client.send.message`, the default import of the v7 Node SDK. Reference a template by its `nt_` id or its alias.
> A send accepts a bare Elemental element list, but storing content on a template requires the top-level elements wrapped in a channel element.
> Templates and journeys can be built in the Courier app or created through the API. Either way they live in the workspace and are referenced by ID when you send.
> The hosted MCP server is https://mcp.courier.com. For a briefing on what Courier is and when to use it, read https://www.courier.com/llms.txt.
> Prefer the Guides tab for how-do-I questions and the Docs tab for how-does-it-behave questions. The API reference lives under /api-reference.

# Send email with Amazon SES through Courier

> Connect SES with access keys or an IAM role, track delivery through SNS, and override fields.

export const AppLink = ({href, children, name, bare}) => {
  const label = children || name || "Open in Courier";
  if (bare) {
    return <a href={href} target="_blank" rel="noreferrer">{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="app" href={href} target="_blank" rel="noreferrer">
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method" aria-hidden="true">↗</span>
    </a>;
};

export const Doc = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="doc" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">DOC</span>
    </a>;
};

## Prerequisites

* [An AWS SES account](https://portal.aws.amazon.com/billing/signup#/start)
* AWS access keys, or an IAM role Courier can assume
* A verified sender identity in AWS SES
* Your AWS SES region

## Setup

### Step 1: add the AWS SES integration to Courier

Before choosing an authentication method:

1. Log in to Courier

2. Navigate to the <AppLink href="https://app.courier.com/integrations">Integrations</AppLink> page

3. Select the <AppLink href="https://app.courier.com/integrations/aws-ses">AWS SES Integration</AppLink> to configure it

### Authentication methods

AWS SES integration in Courier supports two authentication methods:

1. AWS Access Keys

2. AWS IAM Role (Cross-Account Trust)

Choose one of the following authentication methods to get started:

### Method 1: AWS access keys

* Create an AWS SES API Key:

  1. Log in to AWS SES

  2. Navigate to "Settings" → "My Security Credentials"

  3. Go to "Access management" → "Users"

  4. On the "Users" page, select "Add user" and follow the steps to create a new IAM user with `AmazonSESFullAccess` permissions

<Note>
  Download the Access Key ID and Secret Access Key at user creation. AWS does not show the secret again.
</Note>

To scope permissions tighter than `AmazonSESFullAccess`, create a custom policy. In the AWS IAM console, open **Policies → Create policy** and define it in the JSON editor:

```json theme={null}
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "VisualEditor0",
        "Effect": "Allow",
        "Action": ["ses:SendRawEmail", "ses:GetSendStatistics"],
        "Resource": "*"
    }
  ]
}
```

Attach it to the IAM user you created for AWS SES.

* Integrate AWS SES API Key with Courier:
  After creating the IAM user and obtaining the API keys, add them to the Courier AWS SES integration page.

### Method 2: AWS IAM role (Cross-account trust)

* Configure the minimum required IAM policy for sending emails:

```json theme={null}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": [
              "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:identity/example.com",
              "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:identity/sarah@acme-corp.com"
            ]
        }
    ]
}
```

* Create an IAM Role with the following trust policy:

```json theme={null}
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"AWS": "464962053586"
			},
			"Action": "sts:AssumeRole",
			"Condition": {
				"StringEquals": {
					"sts:ExternalId": "${YOUR_COURIER_WORKSPACE_ID}"
				}
			}
		}
	]
}
```

* Users will need to add `/test` to the end of the `ExternalId` value that you set in the role policy you create in your AWS instance for working in **Courier's test environment**.

* After creating the role, copy its ARN and paste it in the "Role ARN" field in the Courier AWS SES integration settings.

<Note>
  When using IAM Role authentication, you'll need to replace the following placeholders:

  * `${YOUR_AWS_ACCOUNT_ID}`: Your AWS account ID

  * `${YOUR_COURIER_WORKSPACE_ID}`: Your Courier workspace ID
</Note>

### Finish the setup

After configuring your chosen authentication method, complete the following steps:

#### Step 1: add a verified "from" address in Courier

1. Add a verified email address (e.g., [support@acme-corp.com](mailto:support@acme-corp.com)) to the "From Address" field in Courier.

   * The "From" email address you set will be used for all emails sent via the AWS SES integration. You can <Doc href="/docs/send/overrides">override this default "From" address</Doc> on a per channel basis within your templates.

2. Ensure the "From" address is a verified identity in your AWS-SES account.

3. For more information on verifying identities, see [Verifying an Identity for Amazon SES Sending Authorization <Icon icon="arrow-up-right-from-square" iconType="solid" />](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/sending-authorization-identity-owner-tasks-verification.html).

<Warning>
  A new AWS SES account starts in the **SES sandbox**, which only sends to verified addresses and domains. Reaching anyone else means asking AWS to lift it: [Moving out of the Amazon SES sandbox](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html).
</Warning>

#### Step 2: configure AWS SES region

Select your preferred AWS SES region from the dropdown menu in the Courier AWS SES integration.

#### Step 3: create and send a Courier notification using AWS SES

Refer to <Doc href="/docs/send/overview">Create and Send a Message</Doc> for instructions on building your notification template and sending a message with the Courier API using cURL.

Addressing a recipient and sending are the same on every email provider, so they are documented once: <Doc href="/docs/integrations/email/overview#profile-requirements">profile requirements</Doc> and <Doc href="/docs/integrations/email/overview#send-to-a-recipient">send to a recipient</Doc>.

## Delivery tracking

<Info>
  SES tracks delivery through SNS webhooks rather than polling, so without the setup below every message sits at `SENT` in your <Doc href="/docs/monitor/overview">logs</Doc> forever.
</Info>

To track delivery, set up Amazon SNS topics. Then configure AWS SES to publish delivery notifications to those topics. Every event carries the SES `MessageId`, which is what your own webhook uses to match the event to the message.

```mermaid theme={null}
flowchart LR
    A["SES sends"] --> B["Event to SNS"]
    B --> C["SNS to Courier"]
    C --> D["Status updated"]
    D --> E["Your webhook"]
```

<AccordionGroup>
  <Accordion title="Setup instructions">
    <Steps>
      <Step title="Get Your Courier Webhook URL">
        You'll need your Courier Message Events webhook URL for AWS SES.

        Via Courier App:

        * Navigate to Channels → Email → AWS SES provider
        * Look for "Message Events Webhook URL" section
        * Copy the webhook URL

        `Important: Keep this URL secure - it authenticates webhooks from AWS to Courier.`
      </Step>

      <Step title="Create AWS SES Configuration Set">
        Configuration Sets in AWS SES enable event publishing.

        * Open the AWS Console and navigate to Amazon SES
        * Click Configuration Sets in the left sidebar
        * Click Create Configuration Set
        * Enter a name (e.g., `courier-delivery-tracking`)
        * Click Create
      </Step>

      <Step title="Add SNS Event Destination">
        * Click on your newly created Configuration Set
        * Navigate to the Event destinations tab
        * Click Add destination
        * Select Amazon SNS as the destination type
        * Configure the destination:
        * Event types: Select `Bounce`, `Delivery`, and `Reject`
        * SNS Topic:
          * Choose Create new SNS topic if you don't have one
          * Or select an existing topic
        * Topic Name (if creating new): `courier-ses-delivery-events`
        * Click `Next` and then `Add destination`
      </Step>

      <Step title="Configure SNS Subscription to Courier">
        * Navigate to Amazon SNS in AWS Console
        * Click `Topics` in the left sidebar
        * Find and click the topic you created/selected in Step 3
        * Click `Create subscription`
        * Configure the subscription:
        * Protocol: `Select HTTPS`
        * Endpoint: Paste your Courier webhook URL from Step 1
        * Enable raw message delivery: `UNCHECKED` (very important!)
        * Click `Create subscription`

        **Automatic Confirmation:** AWS will send a subscription confirmation request to Courier. Courier automatically confirms the subscription - no action needed on your part. Wait 30 seconds, then refresh the page to see the subscription status change to "Confirmed".
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Assign configuration set to your identity">
    Assign the Configuration Set as the default for your verified email address or domain. This ensures all emails sent from that identity automatically use the Configuration Set for delivery tracking.

    **For a Verified Email Address:**

    * In AWS SES Console, navigate to Verified identities
    * Click on your verified email address (e.g., [noreply@yourdomain.com](mailto:noreply@yourdomain.com))
    * Go to the Configuration set tab
    * Click Edit
    * Select your Configuration Set (courier-delivery-tracking)
    * Click Save changes

    **For a Verified Domain:**

    * In AWS SES Console, navigate to Verified identities
    * Click on your verified domain (e.g., yourdomain.com)
    * Go to the Configuration set tab
    * Click Edit
    * Select your Configuration Set (courier-delivery-tracking)
    * Click Save changes

    <Warning>
      **Grant the Configuration Set in your IAM policy.**<br />
      A default Configuration Set on an identity makes SES authorize `ses:SendRawEmail` against it too. A policy scoped to ARNs without it fails every send.
    </Warning>

    The error names the resource it could not reach:

    ```
      User `arn:aws:sts::<account>:assumed-role/customer-ses-role/...` is not authorized to
      perform `ses:SendRawEmail` on resource
      `arn:aws:ses:us-east-1:<account>:configuration-set/courier-delivery-tracking`
    ```

    Add the Configuration Set ARN to the `Resource` list of the policy attached to the IAM user (Method 1) or the assumed role (Method 2):

    ```json theme={null}
    "Resource": [
      "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:identity/example.com",
      "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:configuration-set/courier-delivery-tracking"
    ]
    ```

    A policy using `"Resource": "*"` needs no change. Sends recover on the next retry.
  </Accordion>
</AccordionGroup>

## Overrides

<Doc href="/docs/send/overrides#how-overrides-work">How overrides work</Doc> covers the two levels and which one wins. <Doc href="/docs/integrations/email/overview#channel-overrides">Email channel overrides</Doc> lists the fields every email provider takes.

A provider override changes what Courier sends to SES's [SendRawEmail API](https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html). `body` takes any field that method accepts, and `config` swaps the AWS credentials and region for one send.

### Raw MIME message

`override.body.RawMessage.Data` sends a MIME 1.0 message of your own in place of the rendered template:

<CodeGroup>
  ```javascript Node.js highlight={11-23} theme={null}
  const { requestId } = await courier.send.message({
    message: {
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      to: {
        email: "sarah@acme-corp.com",
      },
      data: {
        name: "Sarah Bennett",
      },
      providers: {
        "aws-ses": {
          override: {
            body: {
              RawMessage: {
                Data: "<Mime 1.0 compatible message>",
              },
            },
            config: {
              accessKeyId: "<Access Key ID>",
              secretAccessKey: "<Secret Access Key>",
            },
          },
        },
      },
    },
  });
  ```

  ```python Python highlight={11-23} theme={null}
  response = client.send.message(
      message={
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "to": {
              "email": "sarah@acme-corp.com",
          },
          "data": {
              "name": "Sarah Bennett",
          },
          "providers": {
              "aws-ses": {
                  "override": {
                      "body": {
                          "RawMessage": {
                              "Data": "<Mime 1.0 compatible message>",
                          },
                      },
                      "config": {
                          "accessKeyId": "<Access Key ID>",
                          "secretAccessKey": "<Secret Access Key>",
                      },
                  },
              },
          },
      },
  )
  ```

  ```bash cURL highlight={14-26} wrap theme={null}
  curl -X POST https://api.courier.com/send \
    -H "Authorization: Bearer $COURIER_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "message": {
        "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
        "to": {
          "email": "sarah@acme-corp.com"
        },
        "data": {
          "name": "Sarah Bennett"
        },
        "providers": {
          "aws-ses": {
            "override": {
              "body": {
                "RawMessage": {
                  "Data": "<Mime 1.0 compatible message>"
                }
              },
              "config": {
                "accessKeyId": "<Access Key ID>",
                "secretAccessKey": "<Secret Access Key>"
              }
            }
          }
        }
      }
    }'
  ```

  ```ruby Ruby highlight={11-23} theme={null}
  response = courier.send_.message(
    message: {
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      to: {
        email: "sarah@acme-corp.com"
      },
      data: {
        name: "Sarah Bennett"
      },
      providers: {
        "aws-ses": {
          override: {
            body: {
              RawMessage: {
                Data: "<Mime 1.0 compatible message>"
              }
            },
            config: {
              accessKeyId: "<Access Key ID>",
              secretAccessKey: "<Secret Access Key>"
            }
          }
        }
      }
    }
  )
  ```

  ```go Go highlight={13-25} theme={null}
  response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
  	Message: courier.SendMessageParamsMessage{
  		To: courier.SendMessageParamsMessageToUnion{
  			OfUserRecipient: &shared.UserRecipientParam{
  				Email: courier.String("sarah@acme-corp.com"),
  			},
  		},
  		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
  		Data: map[string]any{
  			"name": "Sarah Bennett",
  		},
  		Providers: shared.MessageProvidersParam{
  			"aws-ses": shared.MessageProvidersTypeParam{
  				Override: map[string]any{
  					"body": map[string]any{
  						"RawMessage": map[string]any{
  							"Data": "<Mime 1.0 compatible message>",
  						},
  					},
  					"config": map[string]any{
  						"accessKeyId": "<Access Key ID>",
  						"secretAccessKey": "<Secret Access Key>",
  					},
  				},
  			},
  		},
  	},
  })
  ```

  ```java Java highlight={9-19} theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(UserRecipient.builder().email("sarah@acme-corp.com").build())
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .data(JsonValue.from(java.util.Map.of(
              "name", "Sarah Bennett"
          )))
          .providers(MessageProviders.builder()
              .putAdditionalProperty("aws-ses", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
                      "body", java.util.Map.of(
                          "RawMessage", java.util.Map.of(
                              "Data", "<Mime 1.0 compatible message>"
                          )
                      ),
                      "config", java.util.Map.of(
                          "accessKeyId", "<Access Key ID>",
                          "secretAccessKey", "<Secret Access Key>"
                      )
                  ))))
              .build())
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={11-23} theme={null}
  $response = $client->send->message(
    message: [
      'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
      'to' => [
        'email' => 'sarah@acme-corp.com',
      ],
      'data' => [
        'name' => 'Sarah Bennett',
      ],
      'providers' => [
        'aws-ses' => [
          'override' => [
            'body' => [
              'RawMessage' => [
                'Data' => '<Mime 1.0 compatible message>',
              ],
            ],
            'config' => [
              'accessKeyId' => '<Access Key ID>',
              'secretAccessKey' => '<Secret Access Key>',
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={13-33} theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { Email = "sarah@acme-corp.com" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Data = new Dictionary<string, JsonElement>()
          {
              { "name", JsonSerializer.SerializeToElement("Sarah Bennett") },
          },
          Providers = new Dictionary<string, MessageProvidersType>()
          {
              {
                  "aws-ses",
                  new()
                  {
                      Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
                          """
                          {
                            "body": {
                              "RawMessage": {
                                "Data": "<Mime 1.0 compatible message>"
                              }
                            },
                            "config": {
                              "accessKeyId": "<Access Key ID>",
                              "secretAccessKey": "<Secret Access Key>"
                            }
                          }
                          """
                      ),
                  }
              },
          },
      },
  };

  var response = await client.Send.Message(parameters);
  ```

  ```bash CLI highlight={6} wrap theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"email": "sarah@acme-corp.com"}' \
    --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
    --message.data '{"name": "Sarah Bennett"}' \
    --message.providers '{"aws-ses": {"override": {"body": {"RawMessage": {"Data": "<Mime 1.0 compatible message>"}}, "config": {"accessKeyId": "<Access Key ID>", "secretAccessKey": "<Secret Access Key>"}}}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my template to sarah@acme-corp.com and override the SES raw message.
  ```
</CodeGroup>

### Attachments

`override.attachments` adds files to the email. Each entry carries a `filename`, a `contentType`, and base64-encoded `data`:

<CodeGroup>
  ```javascript Node.js highlight={11-21} theme={null}
  const { requestId } = await courier.send.message({
    message: {
      to: {
        email: "sarah@acme-corp.com",
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      data: {
        name: "Sarah Bennett",
      },
      providers: {
        "aws-ses": {
          override: {
            attachments: [
              {
                filename: "hello.txt",
                contentType: "text/plain",
                data: "SGk=",
              },
            ],
          },
        },
      },
    },
  });
  ```

  ```python Python highlight={11-21} theme={null}
  response = client.send.message(
      message={
          "to": {
              "email": "sarah@acme-corp.com",
          },
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "data": {
              "name": "Sarah Bennett",
          },
          "providers": {
              "aws-ses": {
                  "override": {
                      "attachments": [
                          {
                              "filename": "hello.txt",
                              "contentType": "text/plain",
                              "data": "SGk=",
                          },
                      ],
                  },
              },
          },
      },
  )
  ```

  ```bash cURL highlight={14-24} wrap theme={null}
  curl -X POST https://api.courier.com/send \
    -H "Authorization: Bearer $COURIER_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "message": {
        "to": {
          "email": "sarah@acme-corp.com"
        },
        "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
        "data": {
          "name": "Sarah Bennett"
        },
        "providers": {
          "aws-ses": {
            "override": {
              "attachments": [
                {
                  "filename": "hello.txt",
                  "contentType": "text/plain",
                  "data": "SGk="
                }
              ]
            }
          }
        }
      }
    }'
  ```

  ```ruby Ruby highlight={11-21} theme={null}
  response = courier.send_.message(
    message: {
      to: {
        email: "sarah@acme-corp.com"
      },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      data: {
        name: "Sarah Bennett"
      },
      providers: {
        "aws-ses": {
          override: {
            attachments: [
              {
                filename: "hello.txt",
                contentType: "text/plain",
                data: "SGk="
              }
            ]
          }
        }
      }
    }
  )
  ```

  ```go Go highlight={13-23} theme={null}
  response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
  	Message: courier.SendMessageParamsMessage{
  		To: courier.SendMessageParamsMessageToUnion{
  			OfUserRecipient: &shared.UserRecipientParam{
  				Email: courier.String("sarah@acme-corp.com"),
  			},
  		},
  		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
  		Data: map[string]any{
  			"name": "Sarah Bennett",
  		},
  		Providers: shared.MessageProvidersParam{
  			"aws-ses": shared.MessageProvidersTypeParam{
  				Override: map[string]any{
  					"attachments": []any{
  						map[string]any{
  							"filename": "hello.txt",
  							"contentType": "text/plain",
  							"data": "SGk=",
  						},
  					},
  				},
  			},
  		},
  	},
  })
  ```

  ```java Java highlight={9-17} theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(UserRecipient.builder().email("sarah@acme-corp.com").build())
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .data(JsonValue.from(java.util.Map.of(
              "name", "Sarah Bennett"
          )))
          .providers(MessageProviders.builder()
              .putAdditionalProperty("aws-ses", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
                      "attachments", java.util.List.of(
                          java.util.Map.of(
                              "filename", "hello.txt",
                              "contentType", "text/plain",
                              "data", "SGk="
                          )
                      )
                  ))))
              .build())
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={11-21} theme={null}
  $response = $client->send->message(
    message: [
      'to' => [
        'email' => 'sarah@acme-corp.com',
      ],
      'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
      'data' => [
        'name' => 'Sarah Bennett',
      ],
      'providers' => [
        'aws-ses' => [
          'override' => [
            'attachments' => [
              [
                'filename' => 'hello.txt',
                'contentType' => 'text/plain',
                'data' => 'SGk=',
              ],
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={13-31} theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { Email = "sarah@acme-corp.com" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Data = new Dictionary<string, JsonElement>()
          {
              { "name", JsonSerializer.SerializeToElement("Sarah Bennett") },
          },
          Providers = new Dictionary<string, MessageProvidersType>()
          {
              {
                  "aws-ses",
                  new()
                  {
                      Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
                          """
                          {
                            "attachments": [
                              {
                                "filename": "hello.txt",
                                "contentType": "text/plain",
                                "data": "SGk="
                              }
                            ]
                          }
                          """
                      ),
                  }
              },
          },
      },
  };

  var response = await client.Send.Message(parameters);
  ```

  ```bash CLI highlight={6} wrap theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"email": "sarah@acme-corp.com"}' \
    --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
    --message.data '{"name": "Sarah Bennett"}' \
    --message.providers '{"aws-ses": {"override": {"attachments": [{"filename": "hello.txt", "contentType": "text/plain", "data": "SGk="}]}}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my template to sarah@acme-corp.com with an attachment through SES.
  ```
</CodeGroup>

## Troubleshooting

> Dealing with Amazon SES requests can result in some errors. You can find them below to help you troubleshoot. You can also check the <Doc href="/docs/monitor/overview">Courier Logs</Doc> to help debug any provider errors you may encounter. For anything else, you may contact [Courier Support](mailto:support@courier.com).

<AccordionGroup>
  <Accordion title="Amazon SES 554 error">
    This error occurs due to numerous reasons.

    1. It occurs when you have not verified the sender email (sender identity) on Amazon SES.

    2. If you're using the Amazon SES in the sandbox environment and have not verified the recipient's email address, you may encounter this error.

    3. You may encounter this error when you've provided an invalid recipient email address.

    ### Solution

    You can try the [following](https://docs.aws.amazon.com/ses/latest/dg/troubleshoot-smtp.html) steps mentioned below.

    * Open the Amazon SES console and verify that the sender email identity you are using has a verification status of `verified`.

    * If you're using the sandbox environment, ensure that you have added the recipient email address as a verified identity on the SES console. It is mandatory to add the recipient emails on Amazon SES when running in the Sandbox environment.

    * If both the sender and recipient email addresses are verified, ensure that you have provided the correct recipient email address for the "To" parameter.

    * If none of the above works for you, verify that the region specified in your AWS SDK is the same region that contains the verified identities. For example, if the verified identities are located in the Virginia region (us-east-1), you should initialize the Amazon SES instance in the same region.
  </Accordion>

  <Accordion title="Amazon SES email address is not verified">
    This error occurs if you try to send emails using an unverified identity in the region specified.

    Additionally, this error occurs when sending emails in the sandbox environment using unverified sender and recipient email identities.

    ### Solution

    You may try the following to [resolve](https://docs.aws.amazon.com/ses/latest/dg/troubleshoot-error-messages.html) the error.

    1. **Verify the region**

    Verify that you are connected to SES in the region where all your verified identities are located.

    1. **Confirm identity verification**

    Confirm that the sender's identity has been verified. If you are using the sandbox environment, confirm the verification status of the recipient identities as well.

    To do so, visit the SES Console and navigate to your verified identities. The status of the identities should be marked as "Verified" as shown below.

    <Frame caption="Viewing the verified identities on SES">
      <img src="https://mintcdn.com/courier-4f1f25dc/A-IH_41Pkuff3UAy/assets/amazon-ses-verified-identities.webp?fit=max&auto=format&n=A-IH_41Pkuff3UAy&q=85&s=f4ec4772f51bc5f438cd0184ef2b716a" width="1200" height="343" data-path="assets/amazon-ses-verified-identities.webp" />
    </Frame>

    If the status for your identity is `Unverified` you will have to verify the identity before sending the email.

    1. **Verify email addresses**

    If the identities have been verified, ensure that the email addresses you have provided are correctly spelled.
  </Accordion>

  <Accordion title="AWS SES timeout">
    This error occurs when the client (such as an EC2) cannot establish a TCP connection to the public endpoint of Amazon SES.

    This usually means the client (EC2) has a firewall blocking outgoing connections on the SMTP ports (25, 587, or 465). It can also mean the client has no internet connection.

    ### Solution

    To resolve the error, ensure that the client has an active/stable internet connection.

    Hereafter, update the firewall rules on the client to allow outgoing connections on ports 25, 587, and 465 (depending on the port you use).
  </Accordion>

  <Accordion title="AWS SES BCC not working">
    This error occurs when the recipient email in the "TO" field is present in the "BCC" field. Certain email providers do not allow the email to contain duplicate recipients.

    Additionally, this error may occur if the email address in the "BCC" field does not exist.

    ### Solution

    To resolve the error, ensure that the recipient's email address is not the same as the `BCC` email address.

    If the email addresses in the `BCC` are unique, verify the validity of the email addresses specified in the "BCC" list.
  </Accordion>

  <Accordion title="Error: is not authorized to perform ses sendemail">
    This error occurs when an AWS service such as a Lambda function is not authorized to send an email using Amazon SES.

    ### Solution

    To resolve the error, you will need to attach a policy to the IAM role to allow the AWS resource to execute the `ses:SendEmail` action.

    For example, to let a Lambda function send email with SES, attach an inline policy to the function's IAM role that allows the `ses:SendEmail` action. The inline policy is shown below.

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement" : [
         {
           "Sid": "Inline Policy for SES Send Email",
           "Effect": "Allow",
           "Resource" : "*",
           "Actions":[
             "ses:SendEmail"
           ]
         }
      ]
    }
    ```

    The inline policy shown above will ensure that the AWS service is allowed to execute the `SendEmail` action on an Amazon SES resource and will resolve the permission error.
  </Accordion>

  <Accordion title="Amazon SES authentication credentials invalid">
    This error occurs when the SMTP username and password provided to connect to the SMTP endpoint of Amazon SES are incorrect.

    ### Solution

    1. **Verify credentials:** Ensure that the username and password you enter are correct and the same one SES provided.

    2. **Verify the region:** SMTP credentials in Amazon SES differ per region. Therefore, ensure that the credentials used are associated with your region.

    3. **Use SMTP credentials and not console credentials:**

    * The SMTP endpoint credentials are not your AWS credentials. Use the Amazon SES SMTP credentials to reach the Amazon SES SMTP interface.

    * You will have to create an IAM user that can invoke the SES services and generate SMTP credentials for the newly created IAM user. It can be done using the SES console.

    * First, navigate to your SES account dashboard. You will see a section titled - "SMTP Settings." Under this, you should see the output shown below.

    <Frame caption="Viewing SES Settings in AWS Console">
      <img src="https://mintcdn.com/courier-4f1f25dc/LdpdyPjJHKHJqFY9/assets/aws-ses-authentication.webp?fit=max&auto=format&n=LdpdyPjJHKHJqFY9&q=85&s=7b92f079a90d5b3799b83f86a88e1cea" width="1200" height="158" data-path="assets/aws-ses-authentication.webp" />
    </Frame>

    Click "Create SMTP Credentials." The IAM Console opens and prompts you to create an IAM User with the policies required to invoke SES.

    <Frame caption="Creating the IAM user">
      <img src="https://mintcdn.com/courier-4f1f25dc/LdpdyPjJHKHJqFY9/assets/aws-ses-settings.webp?fit=max&auto=format&n=LdpdyPjJHKHJqFY9&q=85&s=b44ea3959c2533bf682c6214d0f29c42" width="1200" height="652" data-path="assets/aws-ses-settings.webp" />
    </Frame>

    Afterward, click "Create." This will create the IAM User, generate the credentials, and display the output below.

    <Frame caption="Viewing the SMTP credentials for IAM user">
      <img src="https://mintcdn.com/courier-4f1f25dc/LdpdyPjJHKHJqFY9/assets/aws-ses-create-iam-user.webp?fit=max&auto=format&n=LdpdyPjJHKHJqFY9&q=85&s=c0f4e4b6d8f41e3a8515577ed3d4b895" width="1200" height="658" data-path="assets/aws-ses-create-iam-user.webp" />
    </Frame>

    To resolve the error, you can download the generated credentials and provide these values for the SMTP username/password.
  </Accordion>

  <Accordion title="Amazon SES 530 authentication required">
    This error occurs when the SMTP credentials provided to Amazon SES are invalid. This can be the username, password, port, and endpoint. Additionally, this error may occur if you have not used TLS.

    ### Solution

    You can try the following to see which one fixes the error.

    1. **Verify credentials:** Ensure that the SMTP username and password you provide are the same credentials you created for the IAM User with permissions to invoke SES.

    2. **Verify the region:** Verify that you connect to SES in the region where all your verified identities are located.

    3. **Verify SMTP configurations:** Visit the SES console and navigate to your account dashboard. In the account dashboard, you should see the SMTP configurations for SES.

    <Frame caption="SMTP configurations for SES">
      <img src="https://mintcdn.com/courier-4f1f25dc/A-IH_41Pkuff3UAy/assets/amazon-ses-smtp-settings.webp?fit=max&auto=format&n=A-IH_41Pkuff3UAy&q=85&s=db5e958099b26187fb1fbfb7050a9ba5" width="1200" height="491" data-path="assets/amazon-ses-smtp-settings.webp" />
    </Frame>

    Cross-check the SMTP configurations shown in the SES console with the endpoint and the port you've provided to ensure that SES has been configured correctly.

    1. **Use the correct port:** Ensure that the port used is port - 587. Some users have experienced issues using the TLS Wrapper port and found that using port 587 (TLS port) fixes the error.
  </Accordion>

  <Accordion title="AWS SES rate limit">
    Amazon SES has a limit of **one email per second** in the sandbox environment. However, you can exceed this rate for a short period, not for long periods.

    ### Solution

    To [resolve this error](https://docs.aws.amazon.com/ses/latest/dg/manage-sending-quotas.html), contact AWS and request production access for SES. Your request will be reviewed, and based on your use case, AWS will grant a reasonable email rate for your SES account. Later on, you can increase this rate by contacting AWS.
  </Accordion>

  <Accordion title="AWS SES email not received">
    This error occurs if the templated email is missing a handlebar parameter. For example, if the email template requires five handlebar parameters and you've specified only four, Amazon SES will send the email and will not display any error. However, the email will not get delivered to the recipient, causing this. It may be possible to debug by viewing the <Doc href="/docs/monitor/overview">Courier logs</Doc> for any rendering errors.

    ### Solution

    To resolve the error, verify that all the required handlebar parameters have been added to the templated email parameters when sending the email.
  </Accordion>
</AccordionGroup>

## Provider details

```text theme={null}
aws-ses
```

Courier recommends routing to the channel. Naming this key in `routing.channels` instead is supported, and sends through just this provider.

<Card title="Send to a specific provider" icon="bullseye-arrow" href="/docs/send/send-to-a-provider" horizontal arrow="true">
  When that is worth doing, and what you give up: failover, channel priority, and providers you add later.
</Card>
