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

# Upload Rendering Results

By default, Nexrender stores rendered outputs on its own infrastructure and returns a time-limited `outputUrl` pointing to that storage. If you need outputs in your own bucket - for data residency, CDN integration, or clean room operation - add an `upload` object to any job payload.

## How It Works

When `upload` is present on a job, Nexrender uploads the rendered file to your bucket as soon as rendering completes. The `outputUrl` in the job response reflects the path in your storage rather than Nexrender's.

## Fields

<ParamField path="upload.provider" type="string">
  Storage provider. Currently `"s3"` (works with any S3-compatible endpoint including AWS S3, Cloudflare R2, Google Cloud Storage, MinIO, and others).
</ParamField>

<ParamField path="upload.prefix" type="string">
  Folder path prefix applied to the uploaded filename. Use this to organise outputs by project, campaign, or date. For example, `"renders/campaign-42/"` will place outputs at `renders/campaign-42/<filename>.mp4` inside the bucket.
</ParamField>

<ParamField path="upload.outputUrl" type="string">
  Base URL used to construct the `outputUrl` returned in the job response. If your bucket is served through a CDN, set this to your CDN base URL so the returned link is immediately usable in your product.
</ParamField>

<ParamField path="upload.params.endpoint" type="string">
  S3-compatible API endpoint. Defaults to `https://s3.amazonaws.com` (AWS). Set this for non-AWS providers - see examples below.
</ParamField>

<ParamField path="upload.params.region" type="string" required>
  Storage region (e.g. `us-east-1`, `auto`).
</ParamField>

<ParamField path="upload.params.bucket" type="string" required>
  Target bucket name.
</ParamField>

<ParamField path="upload.params.acl" type="string">
  Canned ACL applied to uploaded objects. Common values: `public-read`, `private`. Omit if your bucket policy manages access.
</ParamField>

<ParamField path="upload.params.accessKeyId" type="string" required>
  Access key ID for the storage provider. Use a [secret reference](/docs/cloud/jobs/secrets) - e.g. `${secrets.S3_KEY_ID}`.
</ParamField>

<ParamField path="upload.params.accessKeySecret" type="string" required>
  Secret access key. Use a [secret reference](/docs/cloud/jobs/secrets) - e.g. `${secrets.S3_KEY_SECRET}`.
</ParamField>

## Examples

<CodeGroup>
  ```json AWS S3 theme={null}
  {
    "template": { "id": "YOUR_TEMPLATE_ID", "composition": "main" },
    "upload": {
      "provider": "s3",
      "prefix": "renders/campaign-42/",
      "outputUrl": "https://cdn.yourcompany.com/media",
      "params": {
        "region": "us-east-1",
        "bucket": "your-render-outputs",
        "acl": "public-read",
        "accessKeyId": "${secrets.S3_KEY_ID}",
        "accessKeySecret": "${secrets.S3_KEY_SECRET}"
      }
    }
  }
  ```

  ```json Cloudflare R2 theme={null}
  {
    "template": { "id": "YOUR_TEMPLATE_ID", "composition": "main" },
    "upload": {
      "provider": "s3",
      "prefix": "renders/",
      "outputUrl": "https://pub.r2.dev/your-bucket",
      "params": {
        "endpoint": "https://<ACCOUNT_ID>.r2.cloudflarestorage.com",
        "region": "auto",
        "bucket": "your-bucket",
        "accessKeyId": "${secrets.R2_ACCESS_KEY_ID}",
        "accessKeySecret": "${secrets.R2_ACCESS_KEY_SECRET}"
      }
    }
  }
  ```

  ```json Google Cloud Storage theme={null}
  {
    "template": { "id": "YOUR_TEMPLATE_ID", "composition": "main" },
    "upload": {
      "provider": "s3",
      "prefix": "renders/",
      "params": {
        "endpoint": "https://storage.googleapis.com",
        "region": "auto",
        "bucket": "your-gcs-bucket",
        "accessKeyId": "${secret.GCS_ACCESS_KEY_ID}",
        "accessKeySecret": "${secret.GCS_ACCESS_KEY_SECRET}"
      }
    }
  }
  ```
</CodeGroup>

<Warning>
  Never embed storage credentials directly in a job payload. Store them as Nexrender secrets and reference them with the `${secrets.NAME}` syntax. See [Secrets Management](/docs/cloud/jobs/secrets).
</Warning>

## The outputUrl Field

The `outputUrl` you provide is a base URL that Nexrender uses to build the public link returned in the job response. Nexrender appends the output filename to this base:

```text theme={null}
outputUrl: "https://cdn.yourcompany.com/media"
prefix:    "renders/campaign-42/"
filename:  "01JOB_ID.mp4"

→ returned outputUrl: "https://cdn.yourcompany.com/media/renders/campaign-42/01JOB_ID.mp4"
```

If `outputUrl` is omitted, the returned link points directly into the bucket using the endpoint URL.

## Clean Room Setup

For a fully zero-retention configuration, combine `upload` with `template.src` so that neither template files nor render outputs are stored on Nexrender's infrastructure. See [Clean Room Setup](/docs/cloud/clean_room) for the full guide.

<CardGroup cols={2}>
  <Card title="Secrets Management" icon="key" href="/docs/cloud/jobs/secrets">
    Store and reference storage credentials securely
  </Card>

  <Card title="Clean Room Setup" icon="shield" href="/docs/cloud/clean-room">
    Zero data retention with your own storage
  </Card>
</CardGroup>
