Skip to main content
Applies to:
  • Plan -
  • Deployment -

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:
{
  "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.
{
  "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 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. Minimal export config shape:
{
  "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:
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.