---
title: "POST /storage/url — Get File URL"
url: https://lumean.app/en/developers/docs/api/post-storage-url
locale: en
description: "Get File URL"
generated: 2026-10-17
operationId: storage.url
method: POST
path: /storage/url
scopes: [orders.download]
spec_version: 0.0.1
---

# POST /storage/url

**Get File URL**

Issues a temporary download link for a result file.

Pass the `path` exactly as it came in `result.files[]` or `result.service_files[]` of
the order. The link in `data.url` lives about an hour and is fetched with a plain
`GET` — no `X-API-KEY` needed. Expired link: just ask for a new one, the file itself
lives until the order's retention limit.

**Required key permission:** `orders.download`.

- Required scope: `orders.download`

## Request

```bash
curl -X POST 'https://api.lumean.app/api/public/storage/url' \
  -H 'Accept: application/json' \
  -H 'X-API-KEY: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "path": "storage/42/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/result/output.mp3"
}'
```

## Parameters

No parameters.

## Request body

Returns a temporary link (`data.url`, TTL about an hour). Fetch it with a plain `GET` —
the `X-API-KEY` header is neither needed nor sent for the download itself.

Content-Type: `application/json`

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `path` | `string` | required | The full file path exactly as it arrived in the order's `result.files[]` or `result.service_files[]`. Do not assemble it by hand — it contains internal segments. |
| `download` | `boolean` | — | When `true`, the link is marked as an attachment: the file is saved under the human-readable order name instead of opening in a browser tab. |
| `number_base` | `integer` — `0` · `1` | — | Where chunk numbering starts in the file name: `1` → the first chunk is "001", `0` → "000". Defaults to `1`. |
| `number_padding` | `integer` — 1..4 | — | Width of the zero-padded chunk number, `1..4`: `3` → "007". Defaults to `3`. Keep it identical to the ZIP archive request, otherwise the same chunk is named differently on its own and inside the archive. |

**Example**

```json
{
  "path": "storage/42/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/result/output.mp3"
}
```

## Body scenarios

### Result file

The path is taken verbatim from the order's result.files[].

```json
{
  "path": "storage/42/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/result/output.mp3"
}
```

### Download link

download: true — the file is saved under the order name instead of opening in a tab.

```json
{
  "path": "storage/42/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/result/output.mp3",
  "download": true,
  "number_base": 1,
  "number_padding": 3
}
```

## Responses

### 200 — The temporary link is in `data.url`. It lives about an hour; fetch it with a plain `GET`, without `X-API-KEY`.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Success flag. Branch on the HTTP status code instead: any 2xx response carries `true` here, while an error comes back in a different shape that need not contain this field at all. |
| `message` | `string` | required | A human-readable note in the account language. Fine to show to a user; do not parse it — the wording changes without notice and is not an error code. |
| `data` | `object` | required | The response payload. |
| `data.url` | `string` | required | A temporary link to the file. Fetch it with a plain `GET` and **no** `X-API-KEY` header. It is short-lived — do not store it, request it right before downloading. |

**Example**

```json
{
  "success": true,
  "message": "Download link generated.",
  "data": {
    "url": "https://s3.lumean.app/lumean/storage/501/orders/9f8b2c1a-4d3e-4f5a-8b9c-0d1e2f3a4b5c/output/final.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE1234567%2F20260717%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260717T150000Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c"
  }
}
```

### 401 — The key is missing, unknown, disabled or expired.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Always `false` in error responses. |
| `message` | `string` | required | Error text in the key's language (set through the `X-Locale` header). |
| `data` | `null` — nullable | required | Always `null` when the request is rejected before processing. |

**Example**

```json
{
  "success": false,
  "message": "API key is missing. Provide it in the X-API-KEY header.",
  "data": null
}
```

### 403 — The key lacks the required permission, or the object belongs to another user.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Always `false` in error responses. |
| `message` | `string` | required | The reason for the rejection in words, in the account language. Meant for display; do not branch on it, the wording changes. |
| `errors` | `null` — nullable | required | A per-field breakdown — present only on validation errors (`422`): the key names a request field, the value lists the complaints about it. Other codes send `null`, since there is nothing to break down and the whole reason is in `message`. |

**Example**

```json
{
  "success": false,
  "message": "You do not have access to this file."
}
```

### 404 — File not found: wrong `path`, or the order's retention period has passed.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Always `false` in error responses. |
| `message` | `string` | required | The reason for the rejection in words, in the account language. Meant for display; do not branch on it, the wording changes. |
| `errors` | `null` — nullable | required | A per-field breakdown — present only on validation errors (`422`): the key names a request field, the value lists the complaints about it. Other codes send `null`, since there is nothing to break down and the whole reason is in `message`. |

**Example**

```json
{
  "success": false,
  "message": "File not found."
}
```

### 422 — Validation error. Offending fields are listed in `errors`.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `message` | `string` | required | Errors overview. |
| `errors` | `object` | required | A detailed description of each field that failed validation. |
| `errors.*` | `array<string>` — arbitrary keys | — |  |

**Example**

```json
{
  "message": "string",
  "errors": {}
}
```

### 500 — The link could not be issued. Retry — the operation is idempotent.

**Response fields**

| Field | Type | Req. | Description |
| --- | --- | --- | --- |
| `success` | `boolean` | required | Always `false` in error responses. |
| `message` | `null \| array<any> \| string \| object` — nullable | required | The reason for the rejection in words, in the account language. Meant for display; do not branch on it, the wording changes. |
| `errors` | `null` — nullable | required | A per-field breakdown — present only on validation errors (`422`): the key names a request field, the value lists the complaints about it. Other codes send `null`, since there is nothing to break down and the whole reason is in `message`. |

**Example**

```json
{
  "success": false,
  "message": "Failed to generate the download link. Please try again later."
}
```

---

- This page in the browser: https://lumean.app/en/developers/docs/api/post-storage-url
- Full machine-readable reference: https://api.lumean.app/docs/llm.md
