Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 46 additions & 22 deletions integration/inputs-and-outputs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ The simplest method if your files are already hosted somewhere publicly accessib
```

<Tip>
Pre-signed URLs work when the URL itself grants access to the file. Magic Hour
cannot forward your browser session, cookies, or custom authorization headers.
Pre-signed URLs work when the URL itself grants access to the file. Magic Hour cannot forward your
browser session, cookies, or custom authorization headers.
</Tip>

### URL Requirements
Expand Down Expand Up @@ -387,8 +387,9 @@ When a job completes successfully, the response includes download URLs:
### Download URL Expiration

<Warning>
**24-Hour Expiration**: Download URLs expire after 24 hours. Download files promptly or request
fresh URLs using the GET endpoint.
**Temporary URLs**: Download URLs are signed for 24 hours, but GET can return a still-valid cached
URL. Use each download's `expires_at` for its remaining validity. If a URL expires, retrieve the
same completed project using GET for a current URL; do not create another generation.
</Warning>

To get fresh download URLs for an existing project:
Expand All @@ -411,7 +412,8 @@ const downloadUrl = status.downloads[0].url;

### Streaming Downloads

For production applications, use streaming downloads to handle large files efficiently:
The Python example streams response chunks. The Node.js example buffers the complete file in memory
before writing; it is not a memory-efficient streaming implementation.

<CodeGroup>

Expand Down Expand Up @@ -516,20 +518,23 @@ Some APIs generate multiple files (e.g., multiple images):

```python Python
# Download all outputs
status_response = client.v1.image_projects.get(id="project_id")
for i, download in enumerate(status_response.downloads):
filename = f"output_{i+1}.png"
filepath = f"outputs/{filename}"
download_file(download.url, filepath)
print(f"Downloaded: {filename}")
if download_file(download.url, filepath):
print(f"Downloaded: {filename}")
```

```javascript Node.js
// Download all outputs
const statusResponse = await client.v1.imageProjects.get({ id: "project_id" });
for (const [i, download] of statusResponse.downloads.entries()) {
const filename = `output_${i + 1}.png`;
const filepath = `outputs/${filename}`;
await downloadFile(download.url, filepath);
console.log(`Downloaded: ${filename}`);
if (await downloadFile(download.url, filepath)) {
console.log(`Downloaded: ${filename}`);
}
}
```

Expand All @@ -544,6 +549,8 @@ The SDK's `generate()` function can download files automatically:
```python Python
result = client.v1.ai_image_generator.generate(
image_count=1,
model="z-image-turbo",
resolution="640px",
aspect_ratio="16:9",
style={"prompt": "Cool image", "tool": "ai-anime-generator"},
name="AI Image Generated by Magic Hour",
Expand All @@ -552,22 +559,39 @@ result = client.v1.ai_image_generator.generate(
download_directory="."
)

# Print where files were saved
paths = getattr(result, "downloaded_paths", None) or []
print(f"✅ Downloaded to: {paths}" if paths else "✅ Done (no downloaded_paths returned by SDK)")
if result.status != "complete":
raise RuntimeError(f"Project {result.id}: {result.status}; {result.error}")

paths = result.downloaded_paths or []
if not paths:
raise RuntimeError(f"Project {result.id} completed, but no files were downloaded. Retrieve this same project ID to retry the download.")
print(f"✅ Downloaded to: {paths}")
```

```javascript Node.js
const result = await client.v1.aiImageGenerator.generate({
imageCount: 1,
aspectRatio: "16:9",
style: { prompt: "Mountain sunset", tool: "ai-anime-generator" },
waitForCompletion: true,
downloadOutputs: true, // Auto-download
downloadDirectory: ".", // Where to save
});
const result = await client.v1.aiImageGenerator.generate(
{
imageCount: 1,
model: "z-image-turbo",
resolution: "640px",
aspectRatio: "16:9",
style: { prompt: "Mountain sunset", tool: "ai-anime-generator" },
},
{
waitForCompletion: true,
downloadOutputs: true, // Auto-download
downloadDirectory: ".", // Where to save
}
);

// Files are already downloaded
if (result.status !== "complete") {
throw new Error(`Project ${result.id}: ${result.status}; ${JSON.stringify(result.error)}`);
}
if (!result.downloadedPaths?.length) {
throw new Error(
`Project ${result.id} completed, but no files were downloaded. Retrieve this same project ID to retry the download.`
);
}
console.log(`Downloaded to: ${result.downloadedPaths}`);
```

Expand Down Expand Up @@ -628,7 +652,7 @@ result2 = client.v1.ai_clothes_changer.create(assets={"image_file_path": file_pa

**Permanent Storage**: Generated files are stored indefinitely in Magic Hour cloud storage.

**Download URL Expiration**: Download URLs expire after 24 hours for security, but you can request fresh URLs anytime.
**Download URL Expiration**: Use each download's `expires_at`; retrieving a project does not guarantee another full 24 hours. If a URL expires, retrieve that same available, completed project for current URLs rather than generating again.

**Web Dashboard**: All generated content appears in your [magichour.ai/my-library](https://magichour.ai/my-library?utm_source=docs&utm_medium=referral&utm_campaign=inputs-and-outputs) dashboard.

Expand Down