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

# Signed URLs

> Generate temporary access URLs for private files.

Generate a signed URL to access a private file. Signed URLs expire after the specified duration.

<Note>
  This endpoint requires a **secret key** (`sk_`). Public keys cannot generate signed URLs.
</Note>

## Request

### Path Parameters

<ParamField path="id" type="string" required>
  The file ID. Example: `f_abc123xyz`
</ParamField>

### Body Parameters

<ParamField body="expires_in" type="integer" required>
  URL validity in seconds. Max: 604800 (7 days).
</ParamField>

## Response

<ResponseField name="url" type="string" required>
  Signed URL that grants temporary access to the file.
</ResponseField>

<ResponseField name="expires_at" type="string" required>
  ISO 8601 timestamp when the URL expires.
</ResponseField>

## Examples

### Generate a 1-Hour URL

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.stashfyle.com/v1/files/f_abc123xyz/signed-url \
    -H "Authorization: Bearer sk_live_xxx" \
    -H "Content-Type: application/json" \
    -d '{"expires_in": 3600}'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.stashfyle.com/v1/files/f_abc123xyz/signed-url',
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer sk_live_xxx',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ expires_in: 3600 })
    }
  );

  const { url, expires_at } = await response.json();
  ```

  ```python Python theme={null}
  response = requests.post(
      'https://api.stashfyle.com/v1/files/f_abc123xyz/signed-url',
      headers={
          'Authorization': 'Bearer sk_live_xxx',
          'Content-Type': 'application/json'
      },
      json={'expires_in': 3600}
  )

  data = response.json()
  signed_url = data['url']
  ```
</CodeGroup>

```json Response theme={null}
{
  "url": "https://cdn.stashfyle.com/live/user_123/f_abc123xyz/document.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=...",
  "expires_at": "2024-01-15T11:30:00Z"
}
```

### Common Expiration Times

| Duration   | Seconds |
| ---------- | ------- |
| 15 minutes | 900     |
| 1 hour     | 3600    |
| 24 hours   | 86400   |
| 7 days     | 604800  |

## Use Cases

### Secure File Downloads

```javascript theme={null}
// Generate a short-lived URL for download
async function getSecureDownloadUrl(fileId) {
  const response = await fetch(
    `https://api.stashfyle.com/v1/files/${fileId}/signed-url`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.STASHFYLE_SECRET_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ expires_in: 300 }) // 5 minutes
    }
  );

  const { url } = await response.json();
  return url;
}

// Use in your API endpoint
app.get('/download/:fileId', async (req, res) => {
  const downloadUrl = await getSecureDownloadUrl(req.params.fileId);
  res.redirect(downloadUrl);
});
```

### Image Previews

```javascript theme={null}
// Generate URLs for private image thumbnails
async function getImagePreviewUrl(fileId) {
  const response = await fetch(
    `https://api.stashfyle.com/v1/files/${fileId}/signed-url`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ expires_in: 3600 }) // 1 hour
    }
  );

  return response.json();
}
```

## Errors

| Code                  | Status | Description                           |
| --------------------- | ------ | ------------------------------------- |
| `unauthorized`        | 401    | Invalid or missing API key            |
| `forbidden`           | 403    | Public key used (requires secret key) |
| `not_found`           | 404    | File not found                        |
| `bad_request`         | 400    | Invalid expires\_in value             |
| `rate_limit_exceeded` | 429    | Too many requests                     |

## Best Practices

<AccordionGroup>
  <Accordion title="Use short expiration times">
    Generate URLs with the minimum required validity. This limits exposure if a URL is leaked.
  </Accordion>

  <Accordion title="Generate on-demand">
    Don't store signed URLs. Generate them when needed—they're fast to create.
  </Accordion>

  <Accordion title="Don't expose in client-side code">
    Generate signed URLs on your server and pass them to the client. Never expose your secret key.
  </Accordion>
</AccordionGroup>
