diff --git a/integration/inputs-and-outputs.mdx b/integration/inputs-and-outputs.mdx
index 2275585..8fd85c4 100644
--- a/integration/inputs-and-outputs.mdx
+++ b/integration/inputs-and-outputs.mdx
@@ -37,8 +37,8 @@ The simplest method if your files are already hosted somewhere publicly accessib
```
- 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.
### URL Requirements
@@ -387,8 +387,9 @@ When a job completes successfully, the response includes download URLs:
### Download URL Expiration
- **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.
To get fresh download URLs for an existing project:
@@ -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.
@@ -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}`);
+ }
}
```
@@ -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",
@@ -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}`);
```
@@ -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.