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

# Custom Storage Integrations

> Read inputs from and write task outputs to your own S3 or GCS bucket

By default, AudioShake serves task outputs from AudioShake-hosted storage with short-lived download links. With a **storage integration**, you can read inputs from and write outputs to a prefix in your own cloud bucket — Amazon S3 or Google Cloud Storage.

This is useful for keeping audio in your own storage, meeting data-residency requirements, or wiring outputs straight into an existing pipeline.

> [!NOTE]
>
> S3 and GCS are supported for both reading inputs and writing outputs. When using a storage integration, pass `storageIntegrationId` and provide both a source `url` and a `writeDestination` with the matching scheme (`s3://` or `gs://`). `assetId` and HTTP `url` parameter values are not supported with a storage integration.

## How it works

1. You grant AudioShake short-lived access to your bucket (IAM role for AWS, or a service account via Workload Identity Federation for GCP).
2. You register the integration in AudioShake and receive a `storageIntegrationId`.
3. You include `storageIntegrationId` and both a `url` and a `writeDestination` with the matching scheme in your [`/tasks`](https://developer.audioshake.ai/api-reference/tasks/create) requests.

At runtime:

* **AWS** — AudioShake assumes your IAM role via STS and uses those credentials to read and write S3.
* **GCP** — AudioShake federates through your Workload Identity pool, impersonates your service account, and uses those credentials to read and write GCS.

AudioShake never stores long-lived cloud credentials for your account.

## AWS S3

### 1. Create an S3 bucket

In the AWS console, create the bucket AudioShake should read from and write to (or use an existing one). Bucket names are globally unique — pick something like `acme-audioshake-outputs`.

### 2. Create an IAM role in your AWS account

In the AWS IAM console, create a new role — for example, `AudioshakeS3AccessRole`. The role needs two policies: a **trust policy** that lets AudioShake assume the role, and a **permissions policy** that grants read and write access to your bucket prefix.

**Trust policy.** On the first step of the role wizard ("Select trusted entity"), choose **Custom trust policy** — not "AWS account" — so you can trust AudioShake's specific role rather than its entire account. Paste the JSON below into the editor. The `Principal` is AudioShake's published service ARN — copy it verbatim. The External ID is a placeholder for now; you'll fill it in after registering the integration in step 4.

```json Trust policy theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AudioshakeServicesS3Access",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::874456856160:role/AudioshakeServicesS3AccessRole"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<EXTERNAL_ID_PLACEHOLDER>"
        }
      }
    }
  ]
}
```

**Permissions policy.** Continue through the wizard. On the "Add permissions" step you can either skip it and add an inline policy after the role is created, or attach the policy now by clicking **Create policy**, switching to the JSON editor, and pasting the block below. Replace `<BUCKET_NAME>` and `<PREFIX>` with your bucket and the prefix AudioShake should read from and write under (e.g. `outputs`). `GetObject` is required to read `s3://` inputs. If your inputs and outputs live under different prefixes, include both in `Resource`.

```json Permissions policy theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadAndWriteObjectsInAssignedBucket",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:AbortMultipartUpload",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::<BUCKET_NAME>",
        "arn:aws:s3:::<BUCKET_NAME>/<PREFIX>/*"
      ]
    }
  ]
}
```

`AbortMultipartUpload` is needed for large files; `ListBucket` is needed for testing access.

### 3. (Optional) Restrict access via a bucket policy

For defense-in-depth, you can also add a bucket policy that allows only this role to read from and write to the prefix. In the bucket's **Permissions** tab, set the bucket policy to:

```json Bucket policy theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowRoleReadAndWrite",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/AudioshakeS3AccessRole"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:AbortMultipartUpload",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::<BUCKET_NAME>",
        "arn:aws:s3:::<BUCKET_NAME>/<PREFIX>/*"
      ]
    }
  ]
}
```

### 4. Register the integration with AudioShake

You can register the integration through the Dashboard or via the API.

> [tab] Dashboard
>
> In the [AudioShake Dashboard](https://dashboard.audioshake.ai), go to **Settings > Storage Integrations** and click **Add storage integration**. Fill in:
>
> * **Type** — AWS S3
> * **Bucket / Storage name** — your bucket name (no `s3://` prefix or path)
> * **AWS region** — e.g. `us-east-1`
> * **Role ARN** — the ARN of the role you created in step 2
>
> After creating the integration, the Dashboard displays the integration **ID** and a generated **External ID**. Copy both — you'll pass the ID as `storageIntegrationId` when creating tasks, and you'll need the External ID in step 5.

> [tab] API
>
> Send a `POST` to `/storage-integrations`:
>
> ```json theme={null}
> {
>   "type": "aws",
>   "storageName": "<BUCKET_NAME>",
>   "authConfig": {
>     "roleArn": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/AudioshakeS3AccessRole",
>     "region": "us-east-1"
>   }
> }
> ```
>
> The response includes a generated `externalId`:
>
> ```json theme={null}
> {
>   "id": "<storage-integration-id>",
>   "clientId": "<client-id>",
>   "type": "aws",
>   "storageName": "<BUCKET_NAME>",
>   "authConfig": {
>     "roleArn": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/AudioshakeS3AccessRole",
>     "region": "us-east-1",
>     "externalId": "<audioshake-generated-id>"
>   },
>   "createdAt": "2026-04-21T23:44:21.346Z",
>   "updatedAt": "2026-04-21T23:44:21.346Z"
> }
> ```
>
> Save the `id` — you'll pass it as `storageIntegrationId` when creating tasks. Save the `externalId` — you'll need it in step 5.

### 5. Add the External ID to your trust policy

Back in the AWS IAM console, open your role's **Trust relationships** tab and replace `<EXTERNAL_ID_PLACEHOLDER>` with the External ID from step 4:

```json theme={null}
"Condition": {
  "StringEquals": {
    "sts:ExternalId": "<AUDIOSHAKE_GENERATED_EXTERNAL_ID>"
  }
}
```

Save the policy. The integration is now ready to use.

### 6. (Optional) Test the integration

Verify that AudioShake can write to your bucket by sending a `POST` to `/storage-integrations/test`:

```json theme={null}
{
  "writeDestination": "s3://<BUCKET_NAME>/<PREFIX>"
}
```

AudioShake looks up the matching integration, assumes your role, and writes a small test file at `<writeDestination>/.verify/<timestamp>_<uuid>.json`. A `200` response means the integration is working.

> [!TIP]
>
> The `writeDestination` you test must start with the prefix you granted in your permissions policy. If your policy's `Resource` is `arn:aws:s3:::my-bucket/outputs/*`, the verify object must land under `outputs/` — test with `"s3://my-bucket/outputs"`, not `"s3://my-bucket"`.

## Google Cloud Storage

### 1. Enable required APIs

In the [Google Cloud Console](https://console.cloud.google.com), select your project, then go to **APIs & Services > Library** and enable:

* Cloud Storage API
* Identity and Access Management (IAM) API
* IAM Service Account Credentials API
* Security Token Service API (`sts.googleapis.com`)

### 2. Create a GCS bucket

Open **Cloud Storage > Buckets** and create a bucket (or use an existing one). Uniform bucket-level access is recommended. You do not need to make the bucket public.

### 3. Create a service account

Open **IAM & Admin > Service Accounts** and create a service account — for example, `audioshake-storage-access`. Skip project-wide roles; permissions should be on the bucket, not the whole project.

Copy the service account email, e.g. `audioshake-storage-access@YOUR_PROJECT_ID.iam.gserviceaccount.com`.

> [!WARNING]
>
> Do not create a JSON key. AudioShake uses Workload Identity Federation and service account impersonation — not a downloaded key.

### 4. Grant object read and write on the bucket

Open the bucket's **Permissions** tab and grant access to the service account email.

**Recommended:** assign **Storage Object User** (`roles/storage.objectUser`).

Alternatively, assign **Storage Object Creator** and **Storage Object Viewer**, or a custom role with `storage.objects.create` and `storage.objects.get`.

### 5. (Optional) Limit access to a prefix

When granting access, add an IAM condition so the service account can only touch a prefix (e.g. `outputs/`):

```
resource.name.startsWith("projects/_/buckets/<BUCKET_NAME>/objects/outputs/")
```

AudioShake verify writes under `{prefix}/.verify/...`, so if `writeDestination` is `gs://<BUCKET_NAME>/outputs`, the condition must include `outputs/`.

### 6. Create a Workload Identity Federation pool and AWS provider

AudioShake reaches GCS by federating from its AWS services role into your GCP project, then impersonating your service account:

```
AudioShake AWS account 874456856160
  └── AudioshakeServicesS3AccessRole
        ↓ STS session
Your WIF pool + AWS provider
        ↓ federated principal
Your GCS service account
        ↓
gs:// your bucket
```

1. Go to **IAM & Admin > Workload Identity Federation** and create a pool (e.g. pool ID `audioshake`).
2. Add an **AWS** provider (e.g. provider ID `audioshake-aws`).
3. Set **AWS account ID** to `874456856160` (AudioShake's services account — not your own).
4. Confirm attribute mappings include `google.subject` and `attribute.aws_role` (Console AWS defaults are usually fine).
5. Set an **attribute condition** so only AudioShake's role can federate:

```
attribute.aws_role == "arn:aws:iam::874456856160:role/AudioshakeServicesS3AccessRole"
```

6. After creation, open the provider and copy the **provider resource name**. It looks like:

```
projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/audioshake/providers/audioshake-aws
```

That string is `authConfig.workloadIdentityProvider` when you register the integration. Project **number** is not the same as project **ID** — find it under **IAM & Admin > Settings**.

### 7. Allow the federated identity to impersonate your service account

Federation alone is not enough — grant impersonation on the service account:

1. Open **IAM & Admin > Service Accounts** → your AudioShake service account.
2. Grant access to this principal (use the same role ARN as in your attribute condition):

```
principalSet://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/audioshake/attribute.aws_role/arn:aws:iam::874456856160:role/AudioshakeServicesS3AccessRole
```

3. Assign **Service Account Token Creator** (`roles/iam.serviceAccountTokenCreator`).

### 8. Register the integration with AudioShake

You can register the integration through the Dashboard or via the API.

> [tab] Dashboard
>
> In the [AudioShake Dashboard](https://dashboard.audioshake.ai), go to **Settings > Storage Integrations** and click **Add storage integration**. Fill in:
>
> * **Type** — Google Cloud Storage
> * **Bucket / Storage name** — your bucket name (no `gs://` prefix or path)
> * **Project ID** — your GCP project ID
> * **Service account email** — the SA from step 3
> * **Workload identity provider** — the provider resource name from step 6
>
> After creating the integration, the Dashboard displays the integration **ID**. Copy it — you'll pass it as `storageIntegrationId` when creating tasks.

> [tab] API
>
> Send a `POST` to `/storage-integrations`:
>
> ```json theme={null}
> {
>   "type": "gcp",
>   "storageName": "<BUCKET_NAME>",
>   "authConfig": {
>     "projectId": "<PROJECT_ID>",
>     "serviceAccountEmail": "audioshake-storage-access@<PROJECT_ID>.iam.gserviceaccount.com",
>     "workloadIdentityProvider": "projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/audioshake/providers/audioshake-aws"
>   }
> }
> ```
>
> Example response:
>
> ```json theme={null}
> {
>   "id": "<storage-integration-id>",
>   "clientId": "<client-id>",
>   "type": "gcp",
>   "storageName": "<BUCKET_NAME>",
>   "authConfig": {
>     "projectId": "<PROJECT_ID>",
>     "serviceAccountEmail": "audioshake-storage-access@<PROJECT_ID>.iam.gserviceaccount.com",
>     "workloadIdentityProvider": "projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/audioshake/providers/audioshake-aws"
>   },
>   "createdAt": "2026-09-09T03:25:12.000Z",
>   "updatedAt": "2026-09-09T03:25:12.000Z"
> }
> ```
>
> Save the `id` — you'll pass it as `storageIntegrationId` when creating tasks.

### 9. (Optional) Test the integration

Verify that AudioShake can write to your bucket by sending a `POST` to `/storage-integrations/test`:

```json theme={null}
{
  "writeDestination": "gs://<BUCKET_NAME>/<PREFIX>",
  "storageIntegrationId": "<storage-integration-id>"
}
```

AudioShake looks up the integration, federates via WIF, impersonates your service account, and writes a small test file under `<writeDestination>/.verify/`. A `200` response means the integration is working.

> [!TIP]
>
> The `writeDestination` you test must start with the prefix you granted in bucket IAM. If you conditioned access on `outputs/`, test with `"gs://my-bucket/outputs"`, not `"gs://my-bucket"`.

## Using a storage integration

Once the integration is active, include `storageIntegrationId` in your [Create Task](https://developer.audioshake.ai/api-reference/tasks/create.md) request. The source `url` and `writeDestination` must use the scheme for that provider (`s3://` for AWS, `gs://` for GCP) and point at the registered bucket.

**AWS S3:**

```json theme={null}
{
  "url": "s3://acme-audioshake-outputs/inputs/song.wav",
  "storageIntegrationId": "<storage-integration-id>",
  "writeDestination": "s3://acme-audioshake-outputs/outputs",
  "targets": [
    { "model": "vocals", "formats": ["wav"] },
    { "model": "drums", "formats": ["mp3"] }
  ]
}
```

**Google Cloud Storage:**

```json theme={null}
{
  "url": "gs://acme-audioshake-outputs/inputs/song.wav",
  "storageIntegrationId": "<storage-integration-id>",
  "writeDestination": "gs://acme-audioshake-outputs/outputs",
  "targets": [
    { "model": "vocals", "formats": ["wav"] },
    { "model": "drums", "formats": ["mp3"] }
  ]
}
```

Use the `id` returned when you registered the integration as `storageIntegrationId`.

> [!WARNING]
>
> If you omit `storageIntegrationId` or `writeDestination`, outputs are written to AudioShake-hosted storage by default — your custom integration is not used. When using a storage integration, the source `url` must use an `s3://` or `gs://` scheme; a public HTTPS URL is not sufficient.

### Where outputs land

AudioShake writes outputs under your `writeDestination` by task:

```
s3://<bucket>/<prefix>/<taskId>/*
gs://<bucket>/<prefix>/<taskId>/*
```

This keeps each task's outputs isolated and avoids accidental overwrites between tasks. The full object keys are returned on the Task object alongside the standard `output[].link` download URLs.

### Path rules

* `storageIntegrationId` is required and must match a registered storage integration.
* The source `url` must begin with `s3://` or `gs://`, followed by a bucket and object key in that integration's bucket.
* `writeDestination` must begin with the same scheme, followed by a bucket and a prefix.
* The bucket in `url` and `writeDestination` must match the registered storage integration.

## Security

* AudioShake uses short-lived credentials only at task runtime — an assumed IAM role session for AWS, or a federated / impersonated service account token for GCP.
* Access is bounded by the permissions you grant (IAM policy or GCS bucket IAM) — AudioShake can only do what you allow.
* On AWS, the External ID condition prevents the [confused-deputy problem](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html). On GCP, the WIF attribute condition locks federation to AudioShake's services role ARN.
* You can revoke access immediately by removing the trust relationship / WIF binding, detaching permissions, or deleting the role or service account.

## Troubleshooting

| Symptom | Likely cause |
| - | - |
| Task is rejected on submit | Missing `storageIntegrationId`, `url` is not an `s3://` or `gs://` path, or the bucket doesn't match the registered storage integration |
| Task fails with an `AssumeRole` error (AWS) | Trust policy missing AudioShake's principal, or the External ID doesn't match |
| Task fails during federation or impersonation (GCP) | WIF attribute condition doesn't match AudioShake's role, provider resource name is wrong, or the federated principal lacks Service Account Token Creator on the SA |
| Task creates but reads or writes fail with access denied | AWS: missing `s3:GetObject` / `s3:PutObject` on the prefix. GCP: SA missing object create/get on the bucket or prefix condition is too narrow |
| Outputs land in AudioShake storage instead of your bucket | `storageIntegrationId` or `writeDestination` was omitted from the `/tasks` request |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.