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

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.omni.co/feedback

```json
{
  "path": "/share/deliveries/amazon-s3",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# Delivering Omni content to Amazon S3

> Deliver scheduled reports and dashboards to an Amazon S3 bucket using cross-account IAM role assumption.

Omni delivers content to Amazon S3 using cross-account IAM role assumption. No long-lived AWS credentials are stored — Omni obtains temporary credentials via AWS STS at delivery time.

## Requirements

To set up an Amazon S3 delivery, you'll need:

* An AWS account with permissions to create IAM roles
* An S3 bucket to deliver content to (or permissions to create one)

## Setup

<Steps>
  <Step title="Retrieve Omni's AWS information" titleSize="h3" id="omni-aws-info">
    1. Navigate to a **published** dashboard.
    2. Click **File > Deliveries & Alerts**. The delivery options will display on the left side of the page.
    3. Click **New** to create a new delivery.
    4. For **Destination**, select **Amazon S3**.
    5. Click the **Amazon S3 tab**.

    At the top of the form, note the following values — you'll need them to create an IAM role in the next step:

    * **Omni Deliverer Role ARN** — The identity Omni uses to access your S3 bucket. Copy this value.
    * **External ID** — Your organization's unique identifier. This value is the same for all Amazon S3 deliveries in your organization — you only need to configure your IAM trust policy once. Copy this value.
  </Step>

  <Step title="Create an IAM permissions policy in AWS" titleSize="h3" id="iam-permissions">
    In this step, you'll create a policy that defines the permissions the Omni IAM role will have to your S3 bucket. You'll attach this policy to the Omni role in the next step.

    1. Open a new tab in your browser and navigate to the AWS console.
    2. In the AWS Console, navigate to **IAM > Policies**.
    3. Click **Create policy**.
    4. In the **Policy editor** section, click the **JSON** toggle.
    5. Paste in the following, replacing `YOUR-BUCKET-NAME` with the name of your S3 bucket:

       ```json title="Omni S3 policy" highlight={13} theme={null}
       {
         "Version": "2012-10-17",
         "Statement": [
           {
             "Effect": "Allow",
             "Action": [
               "s3:PutObject",
               "s3:CreateMultipartUpload",
               "s3:UploadPart",
               "s3:CompleteMultipartUpload",
               "s3:AbortMultipartUpload"
             ],
             "Resource": "arn:aws:s3:::YOUR-BUCKET-NAME/*"
           }
         ]
       }
       ```

           <Tip>
             To restrict uploads to a specific folder in your bucket, change the permission policy resource to `arn:aws:s3:::YOUR-BUCKET-NAME/some/prefix/*`.
           </Tip>
    6. Click **Next**.
    7. Enter a name for the policy, such as `OmniDeliveryPolicy`.
    8. Click **Create policy**.
  </Step>

  <Step title="Create an IAM role in AWS" titleSize="h3" id="iam-role">
    In this step, you'll create an IAM role for Omni. This will associate the role with Omni's AWS identity and the permissions policy you created in the previous step, giving Omni access to write to the S3 bucket.

    1. In the AWS Console, navigate to **IAM > Roles**.

    2. Click **Create role**.

    3. Select **Custom trust policy** and paste in the following, replacing the placeholder values with the **Omni Deliverer Role ARN** and **External ID** from [step 1](#retrieve-omnis-aws-information) of this guide:

       ```json title="Omni custom trust policy" highlight={7,12} theme={null}
       {
         "Version": "2012-10-17",
         "Statement": [
           {
             "Effect": "Allow",
             "Principal": {
               "AWS": "<Omni Deliverer Role ARN>"
             },
             "Action": "sts:AssumeRole",
             "Condition": {
               "StringEquals": {
                 "sts:ExternalId": "<External ID>"
               }
             }
           }
         ]
       }
       ```

           <Note>
             **What's the External ID for?** This prevents [confused deputy attacks](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html), ensuring that only your Omni organization can assume this role.
           </Note>

    4. Click **Next**.

    5. In the list of permissions policies, locate and select the one you created in the previous step.

    6. Click **Next**.

    7. Enter a name for the role, such as `OmniS3DeliveryRole`.

    8. Click **Create role**.

    9. Open the newly created role and copy its **ARN** — you'll enter this in Omni in a later step.
  </Step>

  <Step title="Configure delivery settings" titleSize="h3" id="delivery-settings">
    1. Navigate back to the Omni tab in your browser. The delivery you started creating in [step 1](#retrieve-omnis-aws-information) should still be open.
    2. Fill in the delivery options:
       * **Delivery** - Select **Schedule** or **Alert**.
       * **Send** - Select the content you want to deliver.
       * **Destination** - This should already be set to **Amazon S3**.
       * **Name** - Enter a name for the delivery.

    **If creating an alert**, use the **Alert** tab to define the conditions that must be met to trigger the delivery. For example, you have a chart that tracks the **Total sales** for your ecommerce company. Using an alert, you can trigger a delivery when the total of your sales has changed.
  </Step>

  <Step title="Configure the delivery schedule" titleSize="h3" id="delivery-schedule">
    In this step, you'll define the cadence for the delivery:

    * For **schedules**, this determines when Omni will deliver the specified content to the destination
    * For **alerts**, this tells Omni when to check if the current query results meet the conditions required to send the delivery

    Schedules can be defined using the visual options or with cron:

    <AccordionGroup>
      <Accordion title="Visual schedule builder" icon="calendar" description="Easy point-and-click schedule builder">
        Use the UI options (**Daily**, **Weekly**, etc.) to select a time period.

        By default, schedules are set to send in the local timezone of the delivery creator's computer. Use the **Times are in** drop down to change the timezone.
      </Accordion>

      <Accordion title="Custom cron schedule (Advanced)" icon="asterisk" description="Code-based granular timing control">
        A cron expression is a string that describes the individual details of a schedule:

        | Order | Unit         | Allowed values  | Allowed special characters |
        | ----- | ------------ | --------------- | -------------------------- |
        | 1     | minute       | 0-59            | \* , - /                   |
        | 2     | hour         | 0-23            | \* , - /                   |
        | 3     | day of month | 1-31            | \* , - / L W ?             |
        | 4     | month        | 1-12 or JAN-DEC | \* , - /                   |
        | 5     | day of week  | 1-7 or SUN-SAT  | \* , - / L W ?             |
        | 6     | year         | any             | \* , - /                   |

        Using cron, you can create schedules like the following:

        ```markdown title="At 9:00 AM every day" theme={null}
        0 9 ? * * *
        ```

        ```markdown title="At 6:30AM on the last day of the month" theme={null}
        30 6 L * ? *
        ```

        ```markdown title="At 8:45 AM every day, Monday through Friday" theme={null}
        45 8 ? * MON-FRI *
        ```

        Omni uses Amazon Web Services' (AWS) syntax for cron expressions. Refer to the [AWS documentation](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html) for more information. By default, the most frequent you can configure a schedule is hourly.

        <Tip>
          If your organization has [AI enabled](/ai/settings/features), you can use the AI cron generator to create cron expressions from natural language. Click the sparkle icon next to the cron input field and describe your desired schedule, such as "every weekday at 9am" or "first Monday of each month at noon."
        </Tip>
      </Accordion>
    </AccordionGroup>

    <Tip>
      Schedule send timezone may be different than query run timezone. For example, if your **Database timezone** is `UTC` with no other timezone conversion settings and you set your schedule to send at `12:00 PM PST`, the query will execute at `8:00 PM UTC`.

      Refer to your [connection timezone settings](/connect-data/timezones) for more information.
    </Tip>
  </Step>

  <Step title="Select format and filter options" titleSize="h3" id="format-filter-options">
    <Tip>
      You can use filters to customize content for different recipients! For example, set a filter to `A` in a scheduled delivery to recipient A, and in another scheduled delivery to recipient B, set a filter to `B`.
    </Tip>

    In the **Dashboard** or **Chart** tab, you can:

    * **Select the format of the content**, such as PNG, PDF, XLSX, or CSV.
    * **Lightly customize the contents and layout**, such as expanding tables to include up to 1,000 rows, hiding filter values, or arranging tiles in a single column.
    * **Select the page(s) to include in the dashboard delivery**. This is applicable only to [advanced layouts](/visualize-present/dashboards/advanced-layout) with multiple pages.

      **Note**: PNGs can only include one page. You'll need to create multiple deliveries to output a PNG for each page.
    * **Set filter or control values for the delivery**. Some formats will have additional customization options. PDF formats, for example, will allow you to specify the orientation and page size for the PDF.

      For dashboard deliveries, the default filters and controls will automatically be applied upon creation. Subsequent default filter value updates will not change the filter values set for existing deliveries.
  </Step>

  <Step title="Configure Amazon S3 settings" titleSize="h3" id="amazon-s3-settings">
    Click the **Amazon S3** tab and enter the following:

    * **IAM role ARN** — **Required**. The ARN of the IAM role Omni will assume to write to your bucket. For example, `arn:aws:iam::123456789012:role/OmniS3DeliveryRole`.
    * **Bucket** — **Required**. The name of your Amazon S3 bucket.
    * **Optional path** — The folder that you want to save your data to, if any. For example, `reports/weekly/`.
    * **File name (without extension)** — A custom filename template. Supports [Mustache templates](/share/deliveries/dynamic-content) like `{{currentDate}}` and `{{scheduledTaskName}}`.
    * **Region** — **Required**. The Amazon services region where your S3 bucket is hosted. This must match the bucket's actual region.
  </Step>

  <Step title="Test the delivery" id="test-delivery" titleSize="h3">
    If you want to test the delivery before saving, click the **Test Now** button in the bottom left corner of the page. This will send the dashboard/chart to the destination using the current settings. For example, using **Test Now** would send the delivery to all **Recipients**.

    <Note>
      The **Test Now** button will be unavailable for alerts if the **Condition type** is `Results have changed` or `Results have stayed the same`. A workaround is to use the **Send Now** option to manually trigger the delivery, which is available once the delivery has been saved.

      Save the alert and then click the <Icon icon="ellipsis-vertical" type="solid" /> icon to display the **Send Now** option. This will initiate a check on the alert condition - if the condition isn't met, the delivery will show as successful but not send anything.
    </Note>
  </Step>

  <Step title="Save the delivery" id="save-delivery" titleSize="h3">
    When finished, click **Save** to create the delivery.
  </Step>
</Steps>

## Troubleshooting

If a delivery to Amazon S3 fails, an error email is sent to the delivery owner. The following table describes common errors and how to resolve them:

| Error                           | Resolution                                                                                                                                                                                                                                                                                                                                                                                      |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Unable to assume the IAM role   | Verify the **IAM Role ARN** in Omni matches the role you created. Check that the trust policy uses the correct **Omni Deliverer Role ARN** as the Principal and the correct **External ID** in the condition. See the [Create an AWS IAM role](#create-an-iam-role-in-aws) step of this guide for the required information.                                                                     |
| Access denied writing to bucket | Verify the IAM role's permission policy includes the required S3 actions (`s3:PutObject`, `s3:CreateMultipartUpload`, `s3:UploadPart`, `s3:CompleteMultipartUpload`, `s3:AbortMultipartUpload`) and that the `Resource` ARN matches your bucket name. See the [Create an AWS IAM permissions policy](#create-an-iam-permissions-policy-in-aws) step of this guide for the required information. |
| Bucket not found                | Verify the **Bucket name** and **Region** in Omni match the actual S3 bucket. Bucket names are case-sensitive.                                                                                                                                                                                                                                                                                  |
| Could not connect to S3         | Verify the **Region** in Omni matches the AWS region where the bucket was created.                                                                                                                                                                                                                                                                                                              |
