[Yandex Cloud documentation](../../index.md) > [Tutorials](../index.md) > [Serverless technologies](index.md) > Sending emails in Yandex Cloud Postbox using the AWS SDK > Python

# Sending emails using the AWS SDK for Python

In this tutorial, you will learn how to send emails via Yandex Cloud Postbox using the [AWS SDK for Python (Boto3)](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html).

To start sending emails:

1. [Get your cloud ready](#before-begin).
1. [Configure a directory for authentication data](#auth).
1. [Create and run the application](#app).
1. [Check the result](#check-result).

If you no longer need the resources you created, [delete them](#clear-out).


## Get your cloud ready {#before-begin}

Sign up for Yandex Cloud and create a [billing account](../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../billing/quickstart/index.md) and [link](../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../resource-manager/concepts/resources-hierarchy.md).


### Required paid resources {#paid-resources}

The infrastructure support cost includes:

* Fee for using Yandex Cloud Postbox (see [Yandex Cloud Postbox pricing](../../postbox/pricing.md)).
* Fee for public DNS queries and [DNS zones](../../dns/concepts/dns-zone.md), if you are creating a resource record in Cloud DNS (see [Cloud DNS pricing](../../dns/pricing.md)).


### Set up resources {#infrastructure}

1. [Create](../../iam/operations/sa/create.md) a service account.
1. [Assign](../../iam/operations/sa/assign-role-for-sa.md) the `postbox.sender` [role](../../postbox/security/index.md#postbox-sender) to the service account.
1. [Create](../../iam/operations/authentication/manage-access-keys.md) a static access key for the service account. Save the ID and secret key.
1. Create an [address](../../postbox/operations/create-address.md).
1. [Pass](../../postbox/operations/check-domain.md) a domain ownership check.


## Configure a directory for authentication data {#auth}

1. Create a directory to store the authentication data in and navigate to it: 

    For macOS and Linux:

    ```bash
    mkdir ~/.aws/
    ```

    For Windows:

    ```bash
    mkdir C:\Users\<user_name>\.aws\
    ```

1. In the `.aws` directory, create a file named `credentials`, copy the credentials you got when [creating a static access key](#infrastructure), and paste them into it:

    ```text
    [default]
    aws_access_key_id = <static_key_ID>
    aws_secret_access_key = <secret_key>
    ```

1. Create a file named `config` with the default region settings and copy the following information to it:

    ```text
    [default]
    region = ru-central1
    endpoint_url = https://postbox.cloud.yandex.net
    ```


### Using environment variables {#variables}

By default, the AWS SDK uses authentication data from environment variables if they are set. These variables have priority over authentication data from the `.aws/credentials` file.

The following environment variables are supported:

* `AWS_ACCESS_KEY_ID`: Static key ID.
* `AWS_SECRET_ACCESS_KEY`: Secret key.

To set environment variables, depending on your operating system, follow these steps:

{% list tabs group=operating_system %}

- Linux/macOS

    In the terminal, run this command:

    ```bash
    export AWS_ACCESS_KEY_ID=<static_key_ID>
    export AWS_SECRET_ACCESS_KEY=<secret_key>
    ```

- Windows

    In PowerShell, run:

    ```powershell
    $Env:AWS_ACCESS_KEY_ID=<static_key_ID>
    $Env:AWS_SECRET_ACCESS_KEY=<secret_key>
    ```

{% endlist %}


## Create and run the application {#app}

1. Get the application code:

    {% list tabs %}

    - Repository

      1. Clone the repository:
         
         ```bash
         git clone https://github.com/yandex-cloud-examples/yc-postbox-examples
         ```
      1. Navigate to the folder in the cloned `/` repository.
      1. In the `raw.py` file, specify the following:
         
         * Sender's email address in the `SENDER` field.
         
             The sender's email domain must match the one specified in the Yandex Cloud Postbox address you created when [getting started](#infrastructure). For example, if your verified domain is `yourdomain.com`, you can use addresses like `noreply@yourdomain.com` or `admin@yourdomain.com` but not `user@mail.yourdomain.com`.
         
         * Recipient's email address in the `RECIPIENT` field, e.g., `receiver@yourdomain.com`. You will need access to this email address for the next step.

    - Manually

      1. Create a directory named `postbox-python` and open it.
      1. Create a file named `raw.py` and paste this code into it:

          ```python
          import boto3
          from botocore.config import Config
          from email.mime.multipart import MIMEMultipart
          from email.mime.text import MIMEText
          from email.mime.application import MIMEApplication
          import os

          # The sender's address must be verified using Amazon SES.
          SENDER = "<sender_address>"

          # Recipient's address.
          RECIPIENT = "<recipient_address>"

          # Email subject:
          SUBJECT = "Yandex Cloud Postbox Raw Email Test via AWS SDK for Python"

          # Path to the file you need to attach to the email.
          ATTACHMENT = "attachment.txt"

          # HTML text of the email.
          HTML_BODY = """<h1>Amazon SES Raw Email Test (AWS SDK for Python)</h1>
          <p>This email was sent with <a href='https://yandex.cloud/en/docs/postbox/quickstart'>Yandex Cloud Postbox</a> using the 
          <a href='https://aws.amazon.com/sdk-for-python/'>AWS SDK for Python</a> with raw email format.</p>
          <p>Please see the attached file.</p>"""

          # Email text for email clients without HTML support.
          TEXT_BODY = "This email was sent with Yandex Cloud Postbox using the AWS SDK for Python with raw email format. Please see the attached file."

          # Character encoding in the email.
          CHARSET = "UTF-8"

          def main():
              # Creating a custom endpoint resolver for Yandex Cloud Postbox
              endpoint_url = "https://postbox.cloud.yandex.net"

              # Configuring a SES client with a custom endpoint
              config = Config(
                  region_name="ru-central1",
                  # Uncomment the following line to enable debug logging
                  # parameter_validation=False,
              )

              # Creating a SES client
              # By default, the SDK uses the default credential provider chain.
              # You can use static credentials by uncommenting and changing the following lines:
              # session = boto3.Session(
              #     aws_access_key_id='accessKeyID',
              #     aws_secret_access_key='secretAccessKey',
              # )
              # ses_client = session.client('sesv2', config=config, endpoint_url=endpoint_url)

              # Using default credentials
              ses_client = boto3.client('sesv2', config=config, endpoint_url=endpoint_url)

              # Creating a parent multipart/mixed container
              msg = MIMEMultipart('mixed')
              # Adding lines: subject, from, to
              msg['Subject'] = SUBJECT
              msg['From'] = SENDER
              msg['To'] = RECIPIENT

              # Creating a child multipart/alternative container
              msg_body = MIMEMultipart('alternative')

              # Enconding the content in text and HTML formats and setting the character encoding
              textpart = MIMEText(TEXT_BODY.encode(CHARSET), 'plain', CHARSET)
              htmlpart = MIMEText(HTML_BODY.encode(CHARSET), 'html', CHARSET)

              # Adding parts with text and HTML content to the child container
              msg_body.attach(textpart)
              msg_body.attach(htmlpart)

              # Identifying the part with attachments and enconding it with MIMEApplication
              try:
                  att = MIMEApplication(open(ATTACHMENT, 'rb').read())
                  # Adding a header to inform the email client that this part is an attachment
                  att.add_header('Content-Disposition', 'attachment', filename=os.path.basename(ATTACHMENT))
                  # Assigning the attachment to the parent container
                  msg.attach(att)
              except FileNotFoundError:
                  print(f"Warning: Attachment file {ATTACHMENT} not found. Sending email without attachment.")

              # Assigning the child multipart/alternative container to the parent multipart/mixed container
              msg.attach(msg_body)

              # Converting the MIME message into a string and then into bytes
              raw_message = str(msg)
              raw_message_bytes = bytes(raw_message, CHARSET)

              try:
                  # Sending an email
                  response = ses_client.send_email(
                      FromEmailAddress=SENDER,
                      Destination={
                          'ToAddresses': [RECIPIENT]
                      },
                      Content={
                          'Raw': {
                              'Data': raw_message_bytes
                          }
                      }
                  )
                  # Printing the email ID
                  print(f"Email sent! Message ID: {response['MessageId']}")
              except Exception as e:
                  print(f"Error sending email: {e}")
                  raise

          if __name__ == "__main__":
              main()
          ```

      1. In the `raw.py` file, specify the following:
         
         * Sender's email address in the `SENDER` field.
         
             The sender's email domain must match the one specified in the Yandex Cloud Postbox address you created when [getting started](#infrastructure). For example, if your verified domain is `yourdomain.com`, you can use addresses like `noreply@yourdomain.com` or `admin@yourdomain.com` but not `user@mail.yourdomain.com`.
         
         * Recipient's email address in the `RECIPIENT` field, e.g., `receiver@yourdomain.com`. You will need access to this email address for the next step.
      1. Create a file named `requirements.txt` and insert this line into it: `boto3`.
      1. Create a file named `attachment.txt` and insert any text into it.

    {% endlist %}

1. Create a virtual environment and install the dependencies:

    {% list tabs group=operating_system %}

    - Linux/MacOS {#linux-macos}

      ```bash
      python -m venv venv
      source venv/bin/activate
      pip install -r requirements.txt
      ```

    - Windows {#windows}

      ```bash
      python -m venv venv
      source venv\Scripts\activate
      pip install -r requirements.txt
      ```

    {% endlist %}

    Result:

    ```text
    ...
    Successfully installed boto3-1.39.4 botocore-1.39.4 jmespath-1.0.1 python-dateutil-2.9.0.post0 s3transfer-0.13.0 six-1.17.0 urllib3-2.5.0
    ```

1. Run the application:

    ```bash
    python raw.py
    ```

    Result:

    ```text
    Email sent! Message ID: DB9WSLG38TUS.11PW8********@ingress2-klg
    ```


## Check the result {#check-result}

Make sure the recipient specified in the file named `raw.py` in the `RECIPIENT` field has received an email with the specified parameters. The email should contain the attachment, i.e., the `attachment.txt` file.


## How to delete the resources you created {#clear-out}

To stop paying for the resources you created:

1. Delete the [address](../../postbox/concepts/glossary.md#adress).
1. [Delete](../../dns/operations/zone-delete.md) the DNS zone if you created a resource record in it.