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

# Storage — Boto Credentials

> Mint short-lived AWS credentials scoped to your org's storage area for use with boto3 / the AWS SDKs.

<div style={{ display: 'none' }} aria-hidden="false">
  ## Quick Answer

  **How do I upload or download files to/from Minerva?** Call this endpoint to receive short-lived AWS credentials scoped to your org's storage area, then use any AWS SDK (boto3, AWS CLI, etc.) to read/write objects.

  **Common questions this endpoint answers:**

  * How do I send large files into Minerva?
  * How do I retrieve exported / batch results?
  * How do I authenticate to Minerva's S3 storage?
  * How long are storage credentials valid?
  * Can I use the AWS CLI / boto3 directly?

  **What you need:** A valid Minerva API key (`x-api-key`).

  **What you get back:** Temporary AWS credentials (access key, secret, session token, expiration) plus the S3 bucket name and your org's prefix.
</div>

## Overview

This endpoint mints short-lived AWS STS credentials scoped to your org's
directory inside the Minerva client storage bucket. Use them with `boto3`,
the AWS CLI, or any AWS SDK to upload files for processing and download
exports.

Each org has a dedicated bucket; credentials are additionally scoped via
session policy to `{your_org}/*` only, so other orgs' data is unreachable
even if a key were misused.

By convention the org directory has two subfolders:

* `Incoming/` — files you send in (resolve/enrich batches, segment uploads, …).
* `Outgoing/` — files placed for you to read (exports, batch results, …).

<Tip>
  The Python SDK wraps this endpoint behind `mc.storage` — `upload`,
  `download`, `list`, plus bulk helpers — and handles credential
  refresh automatically. See the
  [Storage SDK guide](/sdk/guides/storage). The boto endpoint is
  documented here for AWS-SDK / CLI integrations and other languages.
</Tip>

## Request

### Headers

<ParamField header="x-api-key" type="string" required>
  Your Minerva API key.
</ParamField>

### Request Body

No body. Send `POST` with an empty body (or `{}`).

## Response

### Response Structure

<ResponseField name="credentials" type="object">
  Temporary AWS credentials scoped to your org's storage area.
</ResponseField>

<ResponseField name="bucket_info" type="object">
  Bucket name + prefix to scope your AWS-SDK calls to.
</ResponseField>

#### `credentials`

<ResponseField name="aws_region" type="string">
  AWS region the bucket lives in. Always `us-east-1` today.
</ResponseField>

<ResponseField name="aws_key_id" type="string">
  Temporary access key ID.
</ResponseField>

<ResponseField name="aws_secret_access_key" type="string">
  Temporary secret access key.
</ResponseField>

<ResponseField name="aws_session_token" type="string">
  Temporary session token. Required alongside the access key / secret for
  STS credentials.
</ResponseField>

<ResponseField name="aws_session_expiration" type="string">
  ISO 8601 timestamp at which the credentials expire. Typically 1 hour
  from issue. Refresh by calling this endpoint again before expiry.
</ResponseField>

#### `bucket_info`

<ResponseField name="name" type="string">
  S3 bucket name. Pass this as the `Bucket=` parameter on boto3 calls.
</ResponseField>

<ResponseField name="prefix" type="string">
  Your org's directory inside the bucket — every key you read or write
  must start with this prefix. Looks like `<your-minerva-org-id>/`.
</ResponseField>

<ResponseField name="uri" type="string">
  Convenience `s3://{name}/{prefix}` URI. Useful as a base for the AWS
  CLI (`aws s3 cp ./local.csv $URI/Incoming/local.csv`).
</ResponseField>

<ResponseExample>
  ```json 200 Response theme={null}
  {
    "credentials": {
      "aws_region": "us-east-1",
      "aws_key_id": "ASIAIOSFODNN7EXAMPLE",
      "aws_secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
      "aws_session_token": "FwoGZXIvYXdzEJr...truncated",
      "aws_session_expiration": "2026-06-29T16:30:00+00:00"
    },
    "bucket_info": {
      "name": "prod-minerva-client-acme",
      "prefix": "acme/",
      "uri": "s3://prod-minerva-client-acme/acme/"
    }
  }
  ```

  ```json 401 theme={null}
  {
    "code": "unauthorized",
    "message": "Unauthorized",
    "api_request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
  ```

  ```json 403 theme={null}
  {
    "code": "forbidden",
    "message": "Forbidden",
    "api_request_id": "b2c3d4e5-f6a7-1234-bcde-f12345678901"
  }
  ```

  ```json 500 theme={null}
  {
    "code": "internal_error",
    "message": "An unknown error occurred, please contact help@minerva.io if problem persists",
    "api_request_id": "c3d4e5f6-a7b8-2345-cdef-012345678901"
  }
  ```
</ResponseExample>

## Error Responses

* `401` — Unauthorized: invalid or missing API key.
* `403` — Forbidden: API key isn't entitled to storage, or org identity couldn't be resolved.
* `500` — Internal Server Error: credential provisioning failed.

## Example — boto3

```python theme={null}
import boto3
import requests

resp = requests.post(
    "https://api.minerva.io/storage/boto",
    headers={"x-api-key": "<your-api-key>"},
).json()

creds = resp["credentials"]
bucket = resp["bucket_info"]["name"]
prefix = resp["bucket_info"]["prefix"]

s3 = boto3.client(
    "s3",
    aws_access_key_id=creds["aws_key_id"],
    aws_secret_access_key=creds["aws_secret_access_key"],
    aws_session_token=creds["aws_session_token"],
    region_name=creds["aws_region"],
)

# Upload into your Incoming/ folder
s3.upload_file("local.csv", bucket, f"{prefix}Incoming/local.csv")

# List what's waiting in Outgoing/
for obj in s3.list_objects_v2(Bucket=bucket, Prefix=f"{prefix}Outgoing/").get("Contents", []):
    print(obj["Key"], obj["Size"])
```

## Example — AWS CLI

Export the credentials and use the AWS CLI like normal:

```bash theme={null}
RESPONSE=$(curl -s -X POST https://api.minerva.io/storage/boto \
  -H "x-api-key: $MINERVA_API_KEY")

export AWS_ACCESS_KEY_ID=$(echo "$RESPONSE" | jq -r '.credentials.aws_key_id')
export AWS_SECRET_ACCESS_KEY=$(echo "$RESPONSE" | jq -r '.credentials.aws_secret_access_key')
export AWS_SESSION_TOKEN=$(echo "$RESPONSE" | jq -r '.credentials.aws_session_token')
export AWS_DEFAULT_REGION=us-east-1

URI=$(echo "$RESPONSE" | jq -r '.bucket_info.uri')

aws s3 cp ./local.csv "${URI}Incoming/local.csv"
aws s3 ls "${URI}Outgoing/"
```

## Notes

* **Credential lifetime**: typically 1 hour. Refresh by calling the endpoint again before `aws_session_expiration`.
* **Scope**: credentials are limited to `s3:GetObject` / `s3:PutObject` / `s3:DeleteObject` on `{prefix}*` and `s3:ListBucket` filtered to that same prefix. Other prefixes and operations fail closed.
* **Caching**: the Python SDK caches the credentials in-process and auto-refreshes them near expiry. If you call this endpoint directly, implement the same pattern in your own client (don't call it per request — keep a single set of creds and refresh ahead of expiry).
* **Region**: bucket lives in `us-east-1` today.
* **Convention**: use `Incoming/` for client-to-Minerva transfers and read from `Outgoing/` for Minerva-to-client transfers. The session policy permits writes to both, but downstream pipelines watch `Incoming/`.
