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

# Scale S3 export automation across multiple projects

export const plans_0 = "Enterprise"

export const deployments_0 = "Braintrust-hosted"

export const data_plane_version_0 = undefined

export const use_case_0 = "Use case - Organizations with many projects that want to export logs to S3 using a single shared IAM role and apply export automations programmatically at scale"

<Note>
  **Applies to:**

  * Plan - {plans_0}
  * Deployment - {deployments_0}
  * {data_plane_version_0}
  * {use_case_0}
</Note>

## Summary

**Goal:** Configure a single shared AWS IAM role for S3 exports across all Braintrust projects using a wildcard ExternalId.

**Features:** Cloud storage export, IAM trust policy, project automation API.

## Configuration steps

### Step 1: Find your Braintrust org ID

In the Braintrust UI, hover over the org name in the top-left corner. A dialog appears with a copy option for your org ID.

### Step 2: Create the IAM role trust policy

Use `arn:aws:iam::<braintrust-account-id>:root` as the Principal. Do not pin a specific role ARN — Braintrust does not expose a stable worker role ARN, and it can change.

Use a wildcard ExternalId so the same role works for all projects without policy updates:

```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<braintrust-account-id>:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringLike": {
          "sts:ExternalId": "bt:<org-id>:*"
        }
      }
    }
  ]
}
```

Replace `<braintrust-account-id>` with the account ID shown in the in-product export setup dialog.

### Step 3: Attach the S3 permissions policy to the IAM role

Attach this permissions policy to the IAM role you created. It grants Braintrust access to write export files under the configured S3 prefix after the role is assumed.

```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BraintrustS3Export",
      "Effect": "Allow",
      "Action": [
        "s3:List*",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:PutObject",
        "s3:PutObjectAcl",
        "s3:AbortMultipartUpload"
      ],
      "Resource": [
        "arn:aws:s3:::your-bucket",
        "arn:aws:s3:::your-bucket/braintrust/*"
      ]
    }
  ]
}
```

### Step 4: Apply export automations across all projects via API

Exports are project-scoped, so each project needs its own export automation. You can use the same IAM role ARN for each project. Each automation still needs its own `project_id`, `export_path`, and generated `external_id` suffix, but the IAM trust policy does not need to change because it allows `bt:<org-id>:*`.

Use the [apply log retention policies across all projects](https://www.braintrust.dev/docs/kb/apply-log-retention-policies-across-all-projects-via-api) script as a template for listing projects and skipping projects that already have an automation. Replace the retention automation config with the current export automation config from [Export to cloud storage](https://www.braintrust.dev/docs/admin/automations/export-to-cloud-storage#api).

Minimal export config shape:

```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
{
  "project_id": "<project-id>",
  "name": "Logs export",
  "config": {
    "event_type": "btql_export",
    "export_definition": { "type": "log_traces" },
    "export_path": "s3://your-bucket/braintrust/<project-slug>",
    "format": "parquet",
    "interval_seconds": 3600,
    "credentials": {
      "type": "aws_iam",
      "role_arn": "arn:aws:iam::<your-account-id>:role/<your-role-name>",
      "external_id": "<unique-external-id-suffix>"
    }
  }
}
```

After creating each automation, register it with the data plane:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl --request POST \
  --url https://api.braintrust.dev/brainstore/automation/reset-cursors \
  --header "Authorization: Bearer $BRAINTRUST_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "automation_id": "<automation-id>",
    "object_id": "project_logs:<project-id>",
    "start_xact_id": "0"
  }'
```

Notes:

* The `external_id` in the API config is only the suffix. Braintrust uses it to form the full STS ExternalId: `bt:<org-id>:<project-id>:<external_id>`.
* For data plane v2.0 and earlier, the registration endpoint is `POST /automation/cron` instead of `POST /brainstore/automation/reset-cursors`.
* Use `export_definition: { "type": "log_spans" }` if you need one row per span instead of one summary row per trace.
