# Retrieving files

Retrieve policy-approved files through short-lived, controlled download URLs.

Safeput keeps every stored object private. After an upload passes its configured
policy, your backend can request a five-minute download URL. Clean files remain
available for 72 hours from completion of inspection.

## Before you retrieve

Poll `GET /v1/uploads/:id` until `status` is `clean` and
`download_available` is `true`. In Safeput, `clean` means that the file passed
the configured policy at scan time; it is not a guarantee that a file is
universally harmless.

```ts
const response = await fetch(
  `https://dev.safeput.dev/v1/uploads/${uploadId}/download-url`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SAFEPUT_API_KEY}`,
    },
  },
);

const { download } = await response.json();

// Use or redirect to download.url before download.expires_at.
```

The returned URL is a bearer credential. Anyone who receives it can download
the file until the link expires, so do not log, persist, or share it.

## Security checklist

- Create download URLs only on a trusted server.
- Authorize the current user before requesting a URL.
- Keep URL lifetimes short and avoid storing signed URLs.
- Treat downloaded content defensively even after it passed policy.
- Handle an expired link by requesting a new one while retention remains active.

## Expected errors

| HTTP status | Scenario |
| --- | --- |
| `404` | The upload is unknown or belongs to another project. |
| `409` | The file is not in a retrievable state. |
| `410` | Retention or the issued download link has expired. |
| `503` | Downloads are paused or storage is temporarily unavailable. |

Safeput constructs download headers from the sanitized filename and detected
content type produced by inspection. The original filename and client-declared
MIME type are never copied into response headers.
