> For the complete documentation index, see [llms.txt](https://docs.powerfulform.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.powerfulform.com/public-api/submissions/download-a-file.md).

# Download a file

Fetch a file a customer uploaded, using a URL that does not expire.

```
GET /submissions/{id}/files/{uuid}
```

Requires the `submissions:read` scope.

Unlike every other endpoint, this one returns **the file itself**, not JSON.

## Find the file

Every submission lists its attachments in `files`:

```json
{
  "data": {
    "id": 101,
    "files": [
      {
        "uuid": "01JB7Q2M8XK4N0V9ZC3T5RWPDE",
        "field_key": "receipt",
        "field_label": "Upload your receipt",
        "original_name": "receipt-photo.png",
        "mime_type": "image/png",
        "size": 284117,
        "download_url": "https://app.powerfulform.com/api/public/v1/submissions/101/files/01JB7Q2M8XK4N0V9ZC3T5RWPDE"
      }
    ]
  }
}
```

<table><thead><tr><th width="180">Field</th><th>Meaning</th></tr></thead><tbody><tr><td><code>uuid</code></td><td>The file's identifier, used in the path.</td></tr><tr><td><code>field_key</code></td><td>Which form field the file came from.</td></tr><tr><td><code>field_label</code></td><td>The label that field shows to customers.</td></tr><tr><td><code>original_name</code></td><td>The filename as uploaded.</td></tr><tr><td><code>mime_type</code></td><td>The file's content type.</td></tr><tr><td><code>size</code></td><td>Size in bytes.</td></tr><tr><td><code>download_url</code></td><td>The full URL to fetch it. Use this rather than building the path yourself.</td></tr></tbody></table>

## Download it

The URL needs the same API key as every other call:

```bash
curl -L -o receipt-photo.png \
  "https://app.powerfulform.com/api/public/v1/submissions/101/files/01JB7Q2M8XK4N0V9ZC3T5RWPDE" \
  -H "X-PFB-API-KEY: pfb_your_key_here"
```

A success is `200` with the file bytes, `Content-Type` set from the file, and a `Content-Disposition` carrying the original filename.

{% hint style="info" %}
`download_url` does not expire, so it is safe to store next to your own record and fetch again months later. It is not a public link: without a valid API key it returns `401`. Do not paste it into a shared document or an email expecting it to open in a browser.
{% endhint %}

Links inside `content` are rewritten to the same stable form, so a file referenced there and the matching entry in `files` point at the same place.

## Errors

The error body is JSON even though a success is not.

<table><thead><tr><th width="120">Status</th><th width="230">Code</th><th>Cause</th></tr></thead><tbody><tr><td>401</td><td><code>unauthorized</code></td><td>No API key on the request. The URL alone grants nothing.</td></tr><tr><td>403</td><td><code>insufficient_scope</code></td><td>The key is missing <code>submissions:read</code>.</td></tr><tr><td>403</td><td><code>plan_upgrade_required</code></td><td>The store is over its storage allowance, so attachments are locked. See <a href="/settings/plans-and-billing.md">Plans and billing</a>.</td></tr><tr><td>404</td><td><code>not_found</code></td><td>The submission or the file does not exist, belongs to another store, or the stored file is gone — the message says which.</td></tr></tbody></table>

{% hint style="warning" %}
When a store is over its storage allowance, `files` comes back empty and `hidden_reason` is `storage_exceeded`. There is nothing to download until storage is freed or the plan is raised — see [Uploaded files and storage](/troubleshooting/file-storage.md).
{% endhint %}

<br>

## Next steps

* [Submissions](/public-api/submissions.md) — read the submission a file belongs to.
* [Create a submission](/public-api/submissions/create-a-submission.md#uploading-files) — send files the other way.
* [Uploaded files and storage](/troubleshooting/file-storage.md) — how uploads count against your allowance.
