Compare commits

...

2 Commits

Author SHA1 Message Date
Oleg Proskurin 8ee4a8b108 update sitemap 2025-12-30 01:15:17 +07:00
Oleg Proskurin a508ff9527 feat: add docs draft 2025-12-30 01:13:08 +07:00
10 changed files with 1882 additions and 581 deletions

View File

@ -0,0 +1,448 @@
'use client';
/**
* API Reference: Advanced Generation
*
* Based on docs/api/image-generation-advanced.md
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import {
Hero,
SectionHeader,
InlineCode,
EndpointCard,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'reference-images', text: 'Reference Images', level: 2 },
{ id: 'alias-assignment', text: 'Alias Assignment', level: 2 },
{ id: 'alias-resolution', text: '3-Tier Alias Resolution', level: 2 },
{ id: 'flows', text: 'Generation Flows', level: 2 },
{ id: 'regeneration', text: 'Regeneration', level: 2 },
{ id: 'flow-management', text: 'Flow Management', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
const referenceLimits = [
['Max references', '3 images'],
['Max file size', '5MB per image'],
['Supported formats', 'PNG, JPEG, WebP'],
];
const aliasFormat = [
['Prefix', 'Must start with @'],
['Characters', 'Alphanumeric, underscore, hyphen'],
['Pattern', '@[a-zA-Z0-9_-]+'],
['Max length', '50 characters'],
['Examples', '@logo, @hero-bg, @image_1'],
];
const technicalAliases = [
['@last', 'Most recently generated image in flow'],
['@first', 'First generated image in flow'],
['@upload', 'Most recently uploaded image in flow'],
];
const flowIdBehavior = [
['undefined (not provided)', 'Auto-generate pendingFlowId, lazy creation'],
['null (explicitly null)', 'No flow association'],
['"uuid-string"', 'Use provided ID, create flow if doesn\'t exist'],
];
const regenerationTriggers = [
['prompt', 'Yes'],
['aspectRatio', 'Yes'],
['flowId', 'No (metadata only)'],
['meta', 'No (metadata only)'],
];
export default function AdvancedGenerationPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/api' },
{ label: 'Advanced Generation' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/live',
title: 'Live URLs',
description: 'Generate images on-demand via URL parameters.',
accent: 'primary',
},
{
href: '/docs/api/upload',
title: 'Image Upload',
description: 'Upload reference images for generation workflows.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Advanced Generation"
subtitle="Reference images, aliases, flows, and regeneration for complex workflows."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
The Advanced Generation API extends basic generation with powerful features for
production workflows: reference images for style guidance, aliases for human-readable
identifiers, and flows for organizing related generations.
</p>
<TipBox variant="compact" type="info">
<strong>Tip:</strong> For basic generation without these features, see the{' '}
<a href="/docs/api/generate" className="text-purple-400 hover:underline">Image Generation</a> page.
</TipBox>
</section>
{/* Reference Images */}
<section id="reference-images" className="mb-12">
<SectionHeader level={2} id="reference-images">
Reference Images
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Use existing images as style or content references for generation.
</p>
<SectionHeader level={3} id="using-references" className="mb-4">
Using References
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Add <InlineCode>referenceImages</InlineCode> array to your generation request:
</p>
<CodeBlock
code={`{
"prompt": "A product photo with the logo in the corner",
"referenceImages": ["@brand-logo", "@product-style"]
}`}
language="json"
filename="Request with References"
/>
<p className="text-gray-300 leading-relaxed mt-6 mb-4">
References can be:
</p>
<ul className="list-disc list-inside text-gray-300 space-y-2 mb-6">
<li><strong>Project aliases:</strong> <InlineCode>@logo</InlineCode>, <InlineCode>@brand-style</InlineCode></li>
<li><strong>Flow aliases:</strong> <InlineCode>@hero</InlineCode> (with flowId context)</li>
<li><strong>Technical aliases:</strong> <InlineCode>@last</InlineCode>, <InlineCode>@first</InlineCode>, <InlineCode>@upload</InlineCode></li>
<li><strong>Image UUIDs:</strong> <InlineCode>550e8400-e29b-41d4-a716-446655440000</InlineCode></li>
</ul>
<SectionHeader level={3} id="auto-detection" className="mb-4">
Auto-Detection from Prompt
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Aliases in the prompt are automatically detected and used as references:
</p>
<CodeBlock
code={`{
"prompt": "Create a banner using @brand-logo with blue background"
}
// @brand-logo is auto-detected and added to referenceImages`}
language="json"
filename="Auto-Detection"
/>
<SectionHeader level={3} id="reference-limits" className="mt-6 mb-4">
Reference Limits
</SectionHeader>
<Table
headers={['Constraint', 'Limit']}
rows={referenceLimits.map(([constraint, limit]) => [
constraint,
<InlineCode key="limit">{limit}</InlineCode>,
])}
/>
</section>
{/* Alias Assignment */}
<section id="alias-assignment" className="mb-12">
<SectionHeader level={2} id="alias-assignment">
Alias Assignment
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Assign aliases to generated images for easy referencing.
</p>
<SectionHeader level={3} id="project-alias" className="mb-4">
Project-Scoped Alias
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Use <InlineCode>alias</InlineCode> parameter to assign a project-wide alias:
</p>
<CodeBlock
code={`{
"prompt": "A hero banner image",
"alias": "@hero-banner"
}`}
language="json"
filename="Project Alias"
/>
<SectionHeader level={3} id="flow-alias" className="mt-6 mb-4">
Flow-Scoped Alias
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Use <InlineCode>flowAlias</InlineCode> parameter to assign a flow-specific alias:
</p>
<CodeBlock
code={`{
"prompt": "A hero image variation",
"flowId": "550e8400-...",
"flowAlias": "@best"
}`}
language="json"
filename="Flow Alias"
/>
<SectionHeader level={3} id="alias-format" className="mt-6 mb-4">
Alias Format
</SectionHeader>
<Table
headers={['Rule', 'Description']}
rows={aliasFormat.map(([rule, description]) => [
rule,
<InlineCode key="desc">{description}</InlineCode>,
])}
/>
<div className="mt-6">
<TipBox variant="compact" type="warning">
<strong>Override Behavior:</strong> When assigning an existing alias, the new image gets
the alias and the old image loses it. The old image is not deleted, just unlinked.
</TipBox>
</div>
</section>
{/* 3-Tier Alias Resolution */}
<section id="alias-resolution" className="mb-12">
<SectionHeader level={2} id="alias-resolution">
3-Tier Alias Resolution
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Aliases are resolved in order of precedence:
</p>
<SectionHeader level={3} id="tier-1" className="mb-4">
1. Technical Aliases (Highest Priority)
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Computed on-the-fly, require flow context:
</p>
<Table
headers={['Alias', 'Returns']}
rows={technicalAliases.map(([alias, returns]) => [
<InlineCode key="alias">{alias}</InlineCode>,
returns,
])}
/>
<SectionHeader level={3} id="tier-2" className="mt-6 mb-4">
2. Flow Aliases
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Stored in flow&apos;s <InlineCode>aliases</InlineCode> JSONB field.
Different flows can have the same alias pointing to different images.
</p>
<SectionHeader level={3} id="tier-3" className="mt-6 mb-4">
3. Project Aliases (Lowest Priority)
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Stored in image&apos;s <InlineCode>alias</InlineCode> column.
Global across the project, unique per project.
</p>
<div className="mt-6">
<CodeBlock
code={`// Request with flowId
GET /api/v1/images/@hero?flowId=abc-123
// Resolution order:
// 1. Is "@hero" a technical alias? No
// 2. Does flow abc-123 have "@hero" in aliases? Check flows.aliases JSONB
// 3. Does any image have alias = "@hero"? Check images.alias column`}
language="bash"
filename="Resolution Example"
/>
</div>
</section>
{/* Generation Flows */}
<section id="flows" className="mb-12">
<SectionHeader level={2} id="flows">
Generation Flows
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Flows organize related generations into chains.
</p>
<SectionHeader level={3} id="lazy-creation" className="mb-4">
Lazy Flow Creation
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
When <InlineCode>flowId</InlineCode> is not provided, a pending flow ID is generated.
The flow record is created when a second generation uses the same flowId or a flowAlias is assigned.
</p>
<SectionHeader level={3} id="eager-creation" className="mt-6 mb-4">
Eager Flow Creation
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
When <InlineCode>flowAlias</InlineCode> is provided, the flow is created immediately.
</p>
<SectionHeader level={3} id="flowid-behavior" className="mt-6 mb-4">
flowId Behavior
</SectionHeader>
<Table
headers={['Value', 'Behavior']}
rows={flowIdBehavior.map(([value, behavior]) => [
<InlineCode key="value">{value}</InlineCode>,
behavior,
])}
/>
</section>
{/* Regeneration */}
<section id="regeneration" className="mb-12">
<SectionHeader level={2} id="regeneration">
Regeneration
</SectionHeader>
<SectionHeader level={3} id="regenerate-endpoint" className="mb-4">
Regenerate Generation
</SectionHeader>
<EndpointCard
method="POST"
endpoint="/api/v1/generations/:id/regenerate"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Recreate an image using the exact same parameters. The output image ID and URL are preserved.
</p>
<SectionHeader level={3} id="update-regenerate" className="mt-6 mb-4">
Update and Regenerate
</SectionHeader>
<EndpointCard
method="PUT"
endpoint="/api/v1/generations/:id"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Modify parameters with smart regeneration:
</p>
<Table
headers={['Changed Field', 'Triggers Regeneration']}
rows={regenerationTriggers.map(([field, triggers]) => [
<InlineCode key="field">{field}</InlineCode>,
<span key="triggers" className={triggers === 'Yes' ? 'text-green-400' : 'text-gray-500'}>{triggers}</span>,
])}
/>
<SectionHeader level={3} id="flow-regenerate" className="mt-6 mb-4">
Flow Regenerate
</SectionHeader>
<EndpointCard
method="POST"
endpoint="/api/v1/flows/:id/regenerate"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4">
Regenerate the most recent generation in a flow.
</p>
</section>
{/* Flow Management */}
<section id="flow-management" className="mb-12">
<SectionHeader level={2} id="flow-management">
Flow Management
</SectionHeader>
<div className="space-y-6">
<div>
<EndpointCard
method="GET"
endpoint="/api/v1/flows"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-2">List all flows with pagination</p>
</div>
<div>
<EndpointCard
method="GET"
endpoint="/api/v1/flows/:id"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-2">Get flow with computed counts and aliases</p>
</div>
<div>
<EndpointCard
method="GET"
endpoint="/api/v1/flows/:id/generations"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-2">List all generations in the flow</p>
</div>
<div>
<EndpointCard
method="GET"
endpoint="/api/v1/flows/:id/images"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-2">List all images in the flow (generated and uploaded)</p>
</div>
<div>
<EndpointCard
method="PUT"
endpoint="/api/v1/flows/:id/aliases"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-2">Update flow aliases (merges with existing)</p>
</div>
<div>
<EndpointCard
method="DELETE"
endpoint="/api/v1/flows/:id"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-2">Delete flow with cascade behavior</p>
</div>
</div>
<div className="mt-6">
<TipBox variant="prominent" type="warning">
<strong>Cascade Delete:</strong> Deleting a flow hard-deletes all generations and images
without project aliases. Images with project aliases are kept but unlinked from the flow.
</TipBox>
</div>
</section>
</DocPage>
);
}

View File

@ -0,0 +1,352 @@
'use client';
/**
* API Reference: Image Generation
*
* Based on docs/api/image-generation.md
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import { InteractiveAPIWidget } from '@/components/docs/blocks/InteractiveAPIWidget';
import {
Hero,
SectionHeader,
InlineCode,
EndpointCard,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'endpoint', text: 'Create Generation', level: 2 },
{ id: 'parameters', text: 'Parameters', level: 2 },
{ id: 'aspect-ratios', text: 'Aspect Ratios', level: 2 },
{ id: 'prompt-enhancement', text: 'Prompt Enhancement', level: 2 },
{ id: 'generation-status', text: 'Generation Status', level: 2 },
{ id: 'response', text: 'Response Format', level: 2 },
{ id: 'error-codes', text: 'Error Codes', level: 2 },
{ id: 'interactive', text: 'Try It Live', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
const parameters = [
{ name: 'prompt', type: 'string', required: true, description: 'Text description of the image to generate' },
{ name: 'aspectRatio', type: 'string', required: false, default: '"1:1"', description: 'Image aspect ratio (1:1, 16:9, 9:16, 3:2, 21:9)' },
{ name: 'autoEnhance', type: 'boolean', required: false, default: 'true', description: 'Enable AI prompt enhancement for better results' },
{ name: 'meta', type: 'object', required: false, default: '{}', description: 'Custom metadata to store with generation' },
];
const aspectRatios = [
['1:1', 'Square images, social media posts, profile pictures'],
['16:9', 'Landscape, hero banners, video thumbnails'],
['9:16', 'Portrait, mobile screens, stories'],
['3:2', 'Photography standard, print'],
['21:9', 'Ultra-wide banners, cinematic'],
];
const statuses = [
['pending', 'Generation created, waiting to start'],
['processing', 'AI is generating the image'],
['success', 'Image generated successfully'],
['failed', 'Generation failed (see errorMessage)'],
];
const errorCodes = [
['400', 'VALIDATION_ERROR', 'Invalid parameters in the request body'],
['401', 'UNAUTHORIZED', 'Missing or invalid API key in X-API-Key header'],
['404', 'GENERATION_NOT_FOUND', 'Generation does not exist'],
['429', 'RATE_LIMIT_EXCEEDED', 'Too many requests (100/hour limit)'],
['500', 'GENERATION_FAILED', 'AI generation failed'],
];
export default function ImageGenerationPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/api' },
{ label: 'Image Generation' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/advanced',
title: 'Advanced Generation',
description: 'Learn about references, aliases, and generation flows.',
accent: 'primary',
},
{
href: '/docs/api/upload',
title: 'Image Upload',
description: 'Upload reference images for image-to-image generation.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Image Generation"
subtitle="Generate high-quality AI images from text prompts with automatic enhancement."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
The Image Generation API allows you to create AI-generated images from natural language descriptions.
Powered by advanced AI models, it produces high-quality images optimized for your specified requirements.
</p>
<TipBox variant="compact" type="info">
<strong>Tip:</strong> Enable <InlineCode>autoEnhance</InlineCode> (on by default)
to let AI improve your prompts for better image quality.
</TipBox>
</section>
{/* Endpoint */}
<section id="endpoint" className="mb-12">
<SectionHeader level={2} id="endpoint">
Create Generation
</SectionHeader>
<EndpointCard
method="POST"
endpoint="/api/v1/generations"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-4">
Requires <InlineCode>X-API-Key</InlineCode> header with your Project Key.
</p>
</section>
{/* Parameters */}
<section id="parameters" className="mb-12">
<SectionHeader level={2} id="parameters" className="mb-6">
Parameters
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
All parameters should be sent in the request body as JSON.
</p>
<Table
headers={['Parameter', 'Type', 'Required', 'Default', 'Description']}
rows={parameters.map((param) => [
<InlineCode key="name">{param.name}</InlineCode>,
<span key="type" className="text-cyan-400">{param.type}</span>,
<span key="required" className={param.required ? 'text-green-400' : 'text-gray-500'}>
{param.required ? 'Yes' : 'No'}
</span>,
param.default ? <InlineCode key="default">{param.default}</InlineCode> : <span className="text-gray-500">-</span>,
param.description,
])}
/>
<div className="mt-6">
<CodeBlock
code={`{
"prompt": "a red sports car on a mountain road",
"aspectRatio": "16:9",
"autoEnhance": true
}`}
language="json"
filename="Example Request"
/>
</div>
</section>
{/* Aspect Ratios */}
<section id="aspect-ratios" className="mb-12">
<SectionHeader level={2} id="aspect-ratios" className="mb-6">
Aspect Ratios
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Supported aspect ratios for image generation:
</p>
<Table
headers={['Aspect Ratio', 'Use Case']}
rows={aspectRatios.map(([ratio, useCase]) => [
<InlineCode key="ratio">{ratio}</InlineCode>,
useCase,
])}
/>
</section>
{/* Prompt Enhancement */}
<section id="prompt-enhancement" className="mb-12">
<SectionHeader level={2} id="prompt-enhancement" className="mb-6">
Prompt Enhancement
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
By default, prompts are automatically enhanced by AI to produce better results.
</p>
<SectionHeader level={3} id="how-it-works" className="mb-4">
How It Works
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
When <InlineCode>autoEnhance: true</InlineCode> (default):
</p>
<ul className="list-disc list-inside text-gray-300 space-y-2 mb-6">
<li>Your original prompt is preserved in <InlineCode>originalPrompt</InlineCode></li>
<li>AI enhances it with style details, lighting, and composition</li>
<li>The enhanced version is stored in <InlineCode>prompt</InlineCode> and used for generation</li>
</ul>
<p className="text-gray-300 leading-relaxed mb-4">
When <InlineCode>autoEnhance: false</InlineCode>:
</p>
<ul className="list-disc list-inside text-gray-300 space-y-2 mb-6">
<li>Both <InlineCode>prompt</InlineCode> and <InlineCode>originalPrompt</InlineCode> contain your original text</li>
<li>No AI enhancement is applied</li>
</ul>
<TipBox variant="prominent" type="info">
<strong>Enhancement Templates (Roadmap):</strong> Template selection for enhancement styles
(photorealistic, illustration, minimalist, etc.) is planned for a future release.
Currently all enhanced prompts use the default general style.
</TipBox>
<div className="mt-6">
<CodeBlock
code={`// Request
{
"prompt": "a cat",
"autoEnhance": true
}
// Response
{
"prompt": "A photorealistic close-up portrait of a domestic cat with soft fur, captured with an 85mm lens...",
"originalPrompt": "a cat",
"autoEnhance": true
}`}
language="json"
filename="Enhancement Example"
/>
</div>
</section>
{/* Generation Status */}
<section id="generation-status" className="mb-12">
<SectionHeader level={2} id="generation-status" className="mb-6">
Generation Status
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Generations go through these status stages:
</p>
<Table
headers={['Status', 'Description']}
rows={statuses.map(([status, description]) => [
<InlineCode key="status">{status}</InlineCode>,
description,
])}
/>
<p className="text-gray-300 leading-relaxed mt-6 mb-4">
Poll the generation endpoint to check status:
</p>
<EndpointCard
method="GET"
endpoint="/api/v1/generations/:id"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-4">
When <InlineCode>status: "success"</InlineCode>, the <InlineCode>outputImageId</InlineCode> field
contains the generated image ID.
</p>
</section>
{/* Response */}
<section id="response" className="mb-12">
<SectionHeader level={2} id="response" className="mb-4">
Response Format
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
On success, the API returns a JSON object containing the generation details and output image.
</p>
<CodeBlock
code={`{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
"prompt": "A photorealistic establishing shot of a sleek red sports car...",
"originalPrompt": "a red sports car on a mountain road",
"autoEnhance": true,
"aspectRatio": "16:9",
"status": "success",
"outputImageId": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
"outputImage": {
"id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
"storageUrl": "https://cdn.banatie.app/default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
"mimeType": "image/png",
"width": 1792,
"height": 1024,
"fileSize": 1909246
},
"processingTimeMs": 8500,
"createdAt": "2025-11-28T10:00:00.000Z"
}
}`}
language="json"
filename="Success Response"
/>
<TipBox variant="compact" type="info" >
<strong>Note:</strong> The image URL uses the UUID as filename with no extension.
Content-Type is stored in object metadata.
</TipBox>
</section>
{/* Error Codes */}
<section id="error-codes" className="mb-12">
<SectionHeader level={2} id="error-codes" className="mb-6">
Error Codes
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
The API uses standard HTTP status codes and returns descriptive error messages.
</p>
<Table
headers={['Status', 'Code', 'Description']}
rows={errorCodes.map(([status, code, description]) => [
<InlineCode key="status" color="error">{status}</InlineCode>,
<InlineCode key="code">{code}</InlineCode>,
description,
])}
/>
<div className="mt-6">
<TipBox variant="compact" type="warning">
<strong>Rate Limits:</strong> Project API keys are limited to 100 requests per hour.
Check <InlineCode>X-RateLimit-Remaining</InlineCode> header for current usage.
</TipBox>
</div>
</section>
{/* Interactive Widget */}
<section id="interactive" className="mb-12">
<SectionHeader level={2} id="interactive" className="mb-4">
Try It Live
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Test the API directly from this page. Enter your API key and customize the parameters below.
</p>
<InteractiveAPIWidget
endpoint="/api/v1/generations"
method="POST"
description="Generate an image from a text prompt"
parameters={parameters}
/>
</section>
</DocPage>
);
}

View File

@ -0,0 +1,330 @@
'use client';
/**
* API Reference: Image Management
*
* Based on docs/api/images-upload.md (CRUD sections)
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import {
Hero,
SectionHeader,
InlineCode,
EndpointCard,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'list-images', text: 'List Images', level: 2 },
{ id: 'get-image', text: 'Get Image', level: 2 },
{ id: 'update-metadata', text: 'Update Metadata', level: 2 },
{ id: 'assign-alias', text: 'Assign Alias', level: 2 },
{ id: 'delete-image', text: 'Delete Image', level: 2 },
{ id: 'response-fields', text: 'Response Fields', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
const listQueryParams = [
{ name: 'flowId', type: 'string', default: '-', description: 'Filter by flow UUID' },
{ name: 'source', type: 'string', default: '-', description: 'Filter by source: generated, uploaded' },
{ name: 'alias', type: 'string', default: '-', description: 'Filter by exact alias match' },
{ name: 'limit', type: 'number', default: '20', description: 'Results per page (max: 100)' },
{ name: 'offset', type: 'number', default: '0', description: 'Pagination offset' },
{ name: 'includeDeleted', type: 'boolean', default: 'false', description: 'Include soft-deleted records' },
];
const responseFields = [
['id', 'string', 'Image UUID (same as filename in storage)'],
['projectId', 'string', 'Project UUID'],
['flowId', 'string', 'Associated flow UUID (null if none)'],
['storageUrl', 'string', 'CDN URL for direct access'],
['mimeType', 'string', 'Image MIME type'],
['fileSize', 'number', 'File size in bytes'],
['width', 'number', 'Image width in pixels'],
['height', 'number', 'Image height in pixels'],
['source', 'string', '"generated" or "uploaded"'],
['alias', 'string', 'Project-scoped alias (null if none)'],
['focalPoint', 'object', '{ x, y } coordinates (0.0-1.0)'],
['meta', 'object', 'Custom metadata'],
['createdAt', 'string', 'ISO timestamp'],
['updatedAt', 'string', 'ISO timestamp'],
];
export default function ImageManagementPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/api' },
{ label: 'Image Management' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/live',
title: 'Live URLs',
description: 'Learn about CDN access and live URL generation.',
accent: 'primary',
},
{
href: '/docs/api/advanced',
title: 'Advanced Generation',
description: 'Use aliases and references in generation workflows.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Image Management"
subtitle="List, retrieve, update, and delete images in your project."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
The Image Management API provides CRUD operations for images in your project.
Access images by UUID or alias, update metadata, and manage aliases for easy reference.
</p>
<TipBox variant="compact" type="info">
<strong>Tip:</strong> Use aliases like <InlineCode>@hero</InlineCode> for human-readable
references instead of UUIDs.
</TipBox>
</section>
{/* List Images */}
<section id="list-images" className="mb-12">
<SectionHeader level={2} id="list-images">
List Images
</SectionHeader>
<EndpointCard
method="GET"
endpoint="/api/v1/images"
baseUrl="https://api.banatie.app"
/>
<SectionHeader level={3} id="list-params" className="mt-6 mb-4">
Query Parameters
</SectionHeader>
<Table
headers={['Parameter', 'Type', 'Default', 'Description']}
rows={listQueryParams.map((param) => [
<InlineCode key="name">{param.name}</InlineCode>,
<span key="type" className="text-cyan-400">{param.type}</span>,
param.default === '-' ? <span className="text-gray-500">-</span> : <InlineCode key="default">{param.default}</InlineCode>,
param.description,
])}
/>
<div className="mt-6">
<CodeBlock
code={`{
"success": true,
"data": [
{
"id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
"storageUrl": "https://cdn.banatie.app/default/my-project/img/7c4ccf47-...",
"source": "uploaded",
"alias": "@brand-logo",
"width": 512,
"height": 512,
"createdAt": "2025-11-28T10:00:00.000Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 25,
"hasMore": true
}
}`}
language="json"
filename="Response"
/>
</div>
</section>
{/* Get Image */}
<section id="get-image" className="mb-12">
<SectionHeader level={2} id="get-image">
Get Image
</SectionHeader>
<EndpointCard
method="GET"
endpoint="/api/v1/images/:id_or_alias"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Retrieve a single image by UUID or alias.
</p>
<CodeBlock
code={`# By UUID
GET /api/v1/images/7c4ccf47-41ce-4718-afbc-8c553b2c631a
# By project alias
GET /api/v1/images/@brand-logo
# By technical alias (requires flowId)
GET /api/v1/images/@last?flowId=flow-123
# By flow alias
GET /api/v1/images/@hero?flowId=flow-123`}
language="bash"
filename="Examples"
/>
<div className="mt-6">
<TipBox variant="compact" type="info">
<strong>Alias Resolution:</strong> When using aliases with <InlineCode>flowId</InlineCode>,
the system checks flow aliases first, then falls back to project aliases.
</TipBox>
</div>
</section>
{/* Update Metadata */}
<section id="update-metadata" className="mb-12">
<SectionHeader level={2} id="update-metadata">
Update Metadata
</SectionHeader>
<EndpointCard
method="PUT"
endpoint="/api/v1/images/:id_or_alias"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Update image metadata including focal point and custom data.
</p>
<CodeBlock
code={`{
"focalPoint": { "x": 0.5, "y": 0.3 },
"meta": {
"description": "Updated brand logo",
"tags": ["logo", "brand", "2025"]
}
}`}
language="json"
filename="Request Body"
/>
<p className="text-gray-400 text-sm mt-4">
<strong>Note:</strong> Alias assignment has its own dedicated endpoint.
</p>
</section>
{/* Assign Alias */}
<section id="assign-alias" className="mb-12">
<SectionHeader level={2} id="assign-alias">
Assign Alias
</SectionHeader>
<EndpointCard
method="PUT"
endpoint="/api/v1/images/:id_or_alias/alias"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Assign or remove a project-scoped alias from an image.
</p>
<CodeBlock
code={`// Assign alias
{ "alias": "@new-logo" }
// Remove alias
{ "alias": null }`}
language="json"
filename="Request Body"
/>
<div className="mt-6">
<TipBox variant="compact" type="warning">
<strong>Override Behavior:</strong> If another image has the alias, it loses the alias.
The target image gets the alias. The old image is preserved, just unlinked.
</TipBox>
</div>
</section>
{/* Delete Image */}
<section id="delete-image" className="mb-12">
<SectionHeader level={2} id="delete-image">
Delete Image
</SectionHeader>
<EndpointCard
method="DELETE"
endpoint="/api/v1/images/:id_or_alias"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Permanently delete an image and its storage file.
</p>
<CodeBlock
code={`{
"success": true,
"message": "Image deleted"
}`}
language="json"
filename="Response"
/>
<div className="mt-6">
<TipBox variant="prominent" type="warning">
<strong>Warning:</strong> This action is irreversible. The image record and storage file
are permanently deleted. Related generations will have their <InlineCode>outputImageId</InlineCode> set to null.
</TipBox>
</div>
</section>
{/* Response Fields */}
<section id="response-fields" className="mb-12">
<SectionHeader level={2} id="response-fields" className="mb-6">
Response Fields
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Complete list of fields returned in image responses:
</p>
<Table
headers={['Field', 'Type', 'Description']}
rows={responseFields.map(([field, type, description]) => [
<InlineCode key="field">{field}</InlineCode>,
<span key="type" className="text-cyan-400">{type}</span>,
description,
])}
/>
<SectionHeader level={3} id="accessing-images" className="mt-8 mb-4">
Accessing Images
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Use <InlineCode>storageUrl</InlineCode> for direct CDN access:
</p>
<CodeBlock
code={`<!-- Direct access via UUID (fastest, cached) -->
<img src="https://cdn.banatie.app/default/my-project/img/7c4ccf47-..." />
<!-- Access via alias (requires API resolution) -->
<img src="https://cdn.banatie.app/default/my-project/img/@brand-logo" />`}
language="html"
filename="Usage Examples"
/>
<TipBox variant="compact" type="info">
<strong>Performance:</strong> UUID URLs are served directly from CDN.
Alias URLs require API resolution but enable dynamic reassignment.
</TipBox>
</section>
</DocPage>
);
}

View File

@ -0,0 +1,414 @@
'use client';
/**
* API Reference: Live URLs & CDN
*
* Based on docs/api/live-url.md
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import {
Hero,
SectionHeader,
InlineCode,
EndpointCard,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'url-architecture', text: 'URL Architecture', level: 2 },
{ id: 'cdn-serving', text: 'CDN Image Serving', level: 2 },
{ id: 'live-generation', text: 'Live URL Generation', level: 2 },
{ id: 'cache-behavior', text: 'Cache Behavior', level: 2 },
{ id: 'rate-limiting', text: 'IP Rate Limiting', level: 2 },
{ id: 'scope-management', text: 'Scope Management', level: 2 },
{ id: 'use-cases', text: 'Use Cases', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
const urlPatterns = [
['/{org}/{proj}/img/{uuid}', 'Direct image by UUID', 'Caddy → MinIO (public)'],
['/{org}/{proj}/img/@{alias}', 'Image by alias', 'Caddy → API → MinIO'],
['/{org}/{proj}/live/{scope}?prompt=...', 'Live generation', 'Caddy → API → MinIO'],
];
const liveQueryParams = [
{ name: 'prompt', type: 'string', required: true, default: '-', description: 'Image description' },
{ name: 'aspectRatio', type: 'string', required: false, default: '"1:1"', description: 'Aspect ratio' },
{ name: 'autoEnhance', type: 'boolean', required: false, default: 'true', description: 'Enable prompt enhancement' },
];
const cacheHitHeaders = [
['Content-Type', 'image/jpeg'],
['Cache-Control', 'public, max-age=31536000'],
['X-Cache-Status', 'HIT'],
['X-Scope', 'Scope identifier'],
['X-Image-Id', 'Image UUID'],
];
const cacheMissHeaders = [
['Content-Type', 'image/jpeg'],
['Cache-Control', 'public, max-age=31536000'],
['X-Cache-Status', 'MISS'],
['X-Scope', 'Scope identifier'],
['X-Generation-Id', 'Generation UUID'],
['X-Image-Id', 'Image UUID'],
['X-RateLimit-Limit', '10'],
['X-RateLimit-Remaining', 'Remaining requests'],
['X-RateLimit-Reset', 'Seconds until reset'],
];
const rateLimits = [
['New generations', '10 per hour per IP'],
['Cache hits', 'Unlimited'],
];
const scopeParams = [
{ name: 'slug', type: 'string', required: true, default: '-', description: 'Unique identifier' },
{ name: 'allowNewGenerations', type: 'boolean', required: false, default: 'true', description: 'Allow new generations' },
{ name: 'newGenerationsLimit', type: 'number', required: false, default: '30', description: 'Max generations in scope' },
{ name: 'meta', type: 'object', required: false, default: '{}', description: 'Custom metadata' },
];
const errorCodes = [
['400', 'SCOPE_INVALID_FORMAT', 'Invalid scope slug format'],
['403', 'SCOPE_CREATION_DISABLED', 'New scope creation not allowed'],
['404', 'ORG_NOT_FOUND', 'Organization not found'],
['404', 'PROJECT_NOT_FOUND', 'Project not found'],
['404', 'SCOPE_NOT_FOUND', 'Scope does not exist'],
['409', 'SCOPE_ALREADY_EXISTS', 'Scope slug already in use'],
['429', 'IP_RATE_LIMIT_EXCEEDED', 'IP rate limit (10/hour) exceeded'],
['429', 'SCOPE_GENERATION_LIMIT_EXCEEDED', 'Scope limit reached'],
];
export default function LiveUrlsPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/api' },
{ label: 'Live URLs' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/generate',
title: 'Image Generation',
description: 'API-based generation with full control over parameters.',
accent: 'primary',
},
{
href: '/docs/api/advanced',
title: 'Advanced Generation',
description: 'Learn about reference images, aliases, and generation flows.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Live URLs & CDN"
subtitle="Generate images on-demand via URL parameters with automatic caching."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Live URLs provide a simple way to generate images on-demand using URL parameters.
Images are cached automatically, so repeated requests return instantly without counting
toward rate limits.
</p>
<TipBox variant="compact" type="info">
<strong>No API Key Required:</strong> Live URLs are public endpoints. Rate limiting
is done by IP address instead of API key.
</TipBox>
</section>
{/* URL Architecture */}
<section id="url-architecture" className="mb-12">
<SectionHeader level={2} id="url-architecture">
URL Architecture
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
All images are accessible via CDN at <InlineCode>https://cdn.banatie.app</InlineCode>:
</p>
<Table
headers={['URL Pattern', 'Description', 'Routing']}
rows={urlPatterns.map(([pattern, description, routing]) => [
<InlineCode key="pattern">{pattern}</InlineCode>,
description,
<span key="routing" className="text-gray-400 text-sm">{routing}</span>,
])}
/>
<div className="mt-6">
<CodeBlock
code={`# Direct UUID access (fastest, edge cached)
https://cdn.banatie.app/default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a
# Alias access (API resolution)
https://cdn.banatie.app/default/my-project/img/@hero-banner
# Live generation (on-demand with caching)
https://cdn.banatie.app/default/my-project/live/hero?prompt=mountain+sunset`}
language="bash"
filename="Example URLs"
/>
</div>
</section>
{/* CDN Image Serving */}
<section id="cdn-serving" className="mb-12">
<SectionHeader level={2} id="cdn-serving">
CDN Image Serving
</SectionHeader>
<EndpointCard
method="GET"
endpoint="/cdn/:orgSlug/:projectSlug/img/:imageIdOrAlias"
baseUrl="https://cdn.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Serve images by UUID or project-scoped alias. No authentication required.
</p>
<CodeBlock
code={`# By UUID (direct MinIO access, fastest)
GET /cdn/acme/website/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a
# By alias (API resolution, then MinIO)
GET /cdn/acme/website/img/@hero`}
language="bash"
filename="Examples"
/>
<TipBox variant="compact" type="info">
<strong>Performance:</strong> UUID requests are served directly from MinIO with edge caching.
Alias requests require API resolution but enable dynamic reassignment.
</TipBox>
</section>
{/* Live URL Generation */}
<section id="live-generation" className="mb-12">
<SectionHeader level={2} id="live-generation">
Live URL Generation
</SectionHeader>
<EndpointCard
method="GET"
endpoint="/cdn/:orgSlug/:projectSlug/live/:scope"
baseUrl="https://cdn.banatie.app"
/>
<p className="text-gray-300 leading-relaxed mt-4 mb-4">
Generate images on-demand via URL parameters. No authentication required.
</p>
<SectionHeader level={3} id="live-params" className="mb-4">
Query Parameters
</SectionHeader>
<Table
headers={['Parameter', 'Type', 'Required', 'Default', 'Description']}
rows={liveQueryParams.map((param) => [
<InlineCode key="name">{param.name}</InlineCode>,
<span key="type" className="text-cyan-400">{param.type}</span>,
<span key="required" className={param.required ? 'text-green-400' : 'text-gray-500'}>
{param.required ? 'Yes' : 'No'}
</span>,
param.default === '-' ? <span className="text-gray-500">-</span> : <InlineCode key="default">{param.default}</InlineCode>,
param.description,
])}
/>
<div className="mt-6">
<CodeBlock
code={`<!-- Generate a 16:9 landscape image -->
<img src="https://cdn.banatie.app/acme/website/live/hero-section?prompt=mountain+landscape&aspectRatio=16:9" />`}
language="html"
filename="Example Usage"
/>
</div>
</section>
{/* Cache Behavior */}
<section id="cache-behavior" className="mb-12">
<SectionHeader level={2} id="cache-behavior">
Cache Behavior
</SectionHeader>
<SectionHeader level={3} id="cache-hit" className="mb-4">
Cache HIT
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Image exists in cache - returns instantly without rate limit check.
</p>
<Table
headers={['Header', 'Value']}
rows={cacheHitHeaders.map(([header, value]) => [
<InlineCode key="header">{header}</InlineCode>,
<InlineCode key="value">{value}</InlineCode>,
])}
/>
<SectionHeader level={3} id="cache-miss" className="mt-8 mb-4">
Cache MISS
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
New generation - generates image, stores in cache, counts toward rate limit.
</p>
<Table
headers={['Header', 'Value']}
rows={cacheMissHeaders.map(([header, value]) => [
<InlineCode key="header">{header}</InlineCode>,
<InlineCode key="value">{value}</InlineCode>,
])}
/>
<div className="mt-6">
<TipBox variant="compact" type="info">
<strong>Cache Key:</strong> Computed from{' '}
<InlineCode>projectId + scope + prompt + aspectRatio + autoEnhance</InlineCode>
</TipBox>
</div>
</section>
{/* IP Rate Limiting */}
<section id="rate-limiting" className="mb-12">
<SectionHeader level={2} id="rate-limiting">
IP Rate Limiting
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Live URLs are rate limited by IP address:
</p>
<Table
headers={['Limit', 'Value']}
rows={rateLimits.map(([limit, value]) => [
limit,
<InlineCode key="value">{value}</InlineCode>,
])}
/>
<div className="mt-6">
<TipBox variant="compact" type="warning">
<strong>Important:</strong> Only cache MISS (new generations) count toward the limit.
Cache HIT requests are unlimited.
</TipBox>
</div>
</section>
{/* Scope Management */}
<section id="scope-management" className="mb-12">
<SectionHeader level={2} id="scope-management">
Scope Management
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Scopes organize live URL generation budgets. Scope endpoints require Project Key authentication.
</p>
<SectionHeader level={3} id="create-scope" className="mb-4">
Create Scope
</SectionHeader>
<EndpointCard
method="POST"
endpoint="/api/v1/live/scopes"
baseUrl="https://api.banatie.app"
/>
<Table
headers={['Parameter', 'Type', 'Required', 'Default', 'Description']}
rows={scopeParams.map((param) => [
<InlineCode key="name">{param.name}</InlineCode>,
<span key="type" className="text-cyan-400">{param.type}</span>,
<span key="required" className={param.required ? 'text-green-400' : 'text-gray-500'}>
{param.required ? 'Yes' : 'No'}
</span>,
param.default === '-' ? <span className="text-gray-500">-</span> : <InlineCode key="default">{param.default}</InlineCode>,
param.description,
])}
/>
<SectionHeader level={3} id="lazy-scope" className="mt-6 mb-4">
Lazy Scope Creation
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Scopes are auto-created on first live URL request if <InlineCode>project.allowNewLiveScopes = true</InlineCode>.
</p>
<SectionHeader level={3} id="scope-endpoints" className="mt-6 mb-4">
Scope Endpoints
</SectionHeader>
<div className="space-y-4">
<div>
<EndpointCard method="GET" endpoint="/api/v1/live/scopes" baseUrl="https://api.banatie.app" />
<p className="text-gray-400 text-sm mt-2">List all scopes with pagination</p>
</div>
<div>
<EndpointCard method="GET" endpoint="/api/v1/live/scopes/:slug" baseUrl="https://api.banatie.app" />
<p className="text-gray-400 text-sm mt-2">Get scope with statistics</p>
</div>
<div>
<EndpointCard method="PUT" endpoint="/api/v1/live/scopes/:slug" baseUrl="https://api.banatie.app" />
<p className="text-gray-400 text-sm mt-2">Update scope settings</p>
</div>
<div>
<EndpointCard method="POST" endpoint="/api/v1/live/scopes/:slug/regenerate" baseUrl="https://api.banatie.app" />
<p className="text-gray-400 text-sm mt-2">Regenerate scope images</p>
</div>
<div>
<EndpointCard method="DELETE" endpoint="/api/v1/live/scopes/:slug" baseUrl="https://api.banatie.app" />
<p className="text-gray-400 text-sm mt-2">Delete scope (cascades to images)</p>
</div>
</div>
<div className="mt-6">
<TipBox variant="prominent" type="warning">
<strong>Delete Warning:</strong> Deleting a scope permanently removes all cached images.
Aliased images may be kept based on alias protection rules.
</TipBox>
</div>
</section>
{/* Use Cases */}
<section id="use-cases" className="mb-12">
<SectionHeader level={2} id="use-cases">
Use Cases
</SectionHeader>
<SectionHeader level={3} id="hero-images" className="mb-4">
Dynamic Hero Images
</SectionHeader>
<CodeBlock
code={`<img src="https://cdn.banatie.app/acme/website/live/hero?prompt=professional+office+workspace&aspectRatio=16:9" />`}
language="html"
filename="Hero Image"
/>
<p className="text-gray-400 text-sm mt-2 mb-6">First load generates, subsequent loads are cached.</p>
<SectionHeader level={3} id="placeholders" className="mb-4">
Product Placeholders
</SectionHeader>
<CodeBlock
code={`<img src="https://cdn.banatie.app/acme/store/live/products?prompt=product+placeholder+gray+box&aspectRatio=1:1" />`}
language="html"
filename="Product Placeholder"
/>
<SectionHeader level={3} id="blog-images" className="mt-6 mb-4">
Blog Post Images
</SectionHeader>
<CodeBlock
code={`<img src="https://cdn.banatie.app/acme/blog/live/posts?prompt=abstract+technology+background&aspectRatio=16:9" />`}
language="html"
filename="Blog Image"
/>
</section>
</DocPage>
);
}

View File

@ -1,226 +0,0 @@
'use client';
/**
* API Reference: Text to Image
*
* Refactored to use DocPage component for consistent layout
* Layout handles SubsectionNav and Left Sidebar
* DocPage handles Breadcrumb, Article structure, Next Steps, and TOC
* This page provides only the content (Hero + sections)
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import { InteractiveAPIWidget } from '@/components/docs/blocks/InteractiveAPIWidget';
import {
Hero,
SectionHeader,
InlineCode,
EndpointCard,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'endpoint', text: 'Endpoint', level: 2 },
{ id: 'parameters', text: 'Parameters', level: 2 },
{ id: 'response', text: 'Response', level: 2 },
{ id: 'error-codes', text: 'Error Codes', level: 2 },
{ id: 'interactive', text: 'Try It Live', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
const parameters = [
{ name: 'prompt', type: 'string', required: true, description: 'Text description of the image to generate' },
{ name: 'filename', type: 'string', required: false, description: 'Output filename (without extension)' },
{ name: 'aspectRatio', type: 'string', required: false, description: 'Image aspect ratio: "1:1", "16:9", "9:16", "4:3"' },
{ name: 'autoEnhance', type: 'boolean', required: false, description: 'Enable AI prompt enhancement for better results' },
];
export default function TextToImageAPIPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/api' },
{ label: 'Text to Image' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/upload',
title: 'Upload API',
description: 'Learn how to upload reference images for image-to-image generation.',
accent: 'primary',
},
{
href: '/docs/guides/error-handling',
title: 'Error Handling',
description: 'Best practices for handling errors and retries in production.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Text to Image"
subtitle="Generate high-quality images from text prompts using AI-powered models."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
The Text to Image endpoint allows you to generate images from natural language descriptions.
Powered by Google Gemini 2.5 Flash and Imagen 4.0, it produces photorealistic images
optimized for your specified requirements.
</p>
<TipBox variant="compact" type="info">
<strong>Tip:</strong> Enable <InlineCode>autoEnhance</InlineCode>
to let AI improve your prompts for better image quality.
</TipBox>
</section>
{/* Endpoint */}
<section id="endpoint" className="mb-12">
<SectionHeader level={2} id="endpoint">
Endpoint
</SectionHeader>
<EndpointCard
method="POST"
endpoint="/api/text-to-image"
baseUrl="https://api.banatie.com"
/>
</section>
{/* Parameters */}
<section id="parameters" className="mb-12">
<SectionHeader level={2} id="parameters" className="mb-6">
Parameters
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
All parameters should be sent in the request body as JSON.
</p>
<Table
headers={['Parameter', 'Type', 'Required', 'Description']}
rows={parameters.map((param) => [
<InlineCode key="name">{param.name}</InlineCode>,
<span key="type" className="text-cyan-400">{param.type}</span>,
<span key="required" className={param.required ? 'text-green-400' : 'text-gray-500'}>
{param.required ? 'Yes' : 'No'}
</span>,
param.description,
])}
/>
<div className="mt-6">
<TipBox variant="compact" type="info">
<strong>Default Values:</strong> If not specified, <InlineCode>filename</InlineCode> is
auto-generated, <InlineCode>aspectRatio</InlineCode> defaults
to "1:1", and <InlineCode>autoEnhance</InlineCode> is false.
</TipBox>
</div>
</section>
{/* Response */}
<section id="response" className="mb-12">
<SectionHeader level={2} id="response" className="mb-4">
Response
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
On success, the API returns a JSON object containing the generated image URL and metadata.
</p>
<CodeBlock
code={`{
"success": true,
"data": {
"url": "https://cdn.banatie.com/org/project/generated/2025-01/image.png",
"filepath": "org/project/generated/2025-01/image.png",
"width": 1024,
"height": 1024,
"promptEnhancement": {
"enhancedPrompt": "A highly detailed, photorealistic...",
"wasEnhanced": true
}
},
"metadata": {
"generationTime": 3.42,
"model": "imagen-4.0"
}
}`}
language="json"
filename="Success Response"
/>
</section>
{/* Error Codes */}
<section id="error-codes" className="mb-12">
<SectionHeader level={2} id="error-codes" className="mb-6">
Error Codes
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
The API uses standard HTTP status codes and returns descriptive error messages.
</p>
<Table
headers={['Status Code', 'Error Type', 'Description']}
rows={[
[
<InlineCode key="code" color="error">400</InlineCode>,
'Bad Request',
'Missing or invalid parameters in the request body',
],
[
<InlineCode key="code" color="error">401</InlineCode>,
'Unauthorized',
'Missing or invalid API key in X-API-Key header',
],
[
<InlineCode key="code" color="error">429</InlineCode>,
'Rate Limit',
'Too many requests. Check rate limit headers for retry timing',
],
[
<InlineCode key="code" color="error">500</InlineCode>,
'Server Error',
'Internal server error. Contact support if persists',
],
]}
/>
<div className="mt-6">
<TipBox variant="compact" type="warning">
<strong>Rate Limits:</strong> Project API keys are limited to 100 requests per hour.
Upgrade your plan for higher limits.
</TipBox>
</div>
</section>
{/* Interactive Widget */}
<section id="interactive" className="mb-12">
<SectionHeader level={2} id="interactive" className="mb-4">
Try It Live
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Test the API directly from this page. Enter your API key and customize the parameters below.
</p>
<InteractiveAPIWidget
endpoint="/api/text-to-image"
method="POST"
description="Generate an image from a text prompt"
parameters={parameters}
/>
</section>
</DocPage>
);
}

View File

@ -0,0 +1,269 @@
'use client';
/**
* API Reference: Image Upload
*
* Based on docs/api/images-upload.md (upload section)
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import {
Hero,
SectionHeader,
InlineCode,
EndpointCard,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'endpoint', text: 'Upload Endpoint', level: 2 },
{ id: 'parameters', text: 'Form Parameters', level: 2 },
{ id: 'constraints', text: 'File Constraints', level: 2 },
{ id: 'flow-association', text: 'Flow Association', level: 2 },
{ id: 'response', text: 'Response Format', level: 2 },
{ id: 'error-codes', text: 'Error Codes', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
const formParameters = [
{ name: 'file', type: 'file', required: true, description: 'Image file (PNG, JPEG, WebP)' },
{ name: 'alias', type: 'string', required: false, description: 'Project-scoped alias (e.g., @logo)' },
{ name: 'flowId', type: 'string', required: false, description: 'Flow UUID to associate with' },
{ name: 'flowAlias', type: 'string', required: false, description: 'Flow-scoped alias (requires flowId)' },
{ name: 'meta', type: 'string', required: false, description: 'JSON string with custom metadata' },
];
const fileConstraints = [
['Max file size', '5MB'],
['Supported formats', 'PNG, JPEG, JPG, WebP'],
['MIME types', 'image/png, image/jpeg, image/webp'],
];
const flowIdBehavior = [
['Not provided', 'Auto-generate pendingFlowId, lazy flow creation'],
['null', 'No flow association'],
['"uuid"', 'Associate with specified flow'],
];
const errorCodes = [
['400', 'VALIDATION_ERROR', 'Invalid parameters'],
['400', 'FILE_TOO_LARGE', 'File exceeds 5MB limit'],
['400', 'UNSUPPORTED_FILE_TYPE', 'Not PNG, JPEG, or WebP'],
['400', 'ALIAS_FORMAT_CHECK', 'Alias must start with @'],
['401', 'UNAUTHORIZED', 'Missing or invalid API key'],
];
export default function ImageUploadPage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'API Reference', href: '/docs/api' },
{ label: 'Image Upload' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/images',
title: 'Image Management',
description: 'Learn how to list, update, and delete uploaded images.',
accent: 'primary',
},
{
href: '/docs/api/advanced',
title: 'Advanced Generation',
description: 'Use uploaded images as references for generation.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Image Upload"
subtitle="Upload images for reference in generations or as standalone assets."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
The Upload API allows you to upload images to your project. Uploaded images can be used as
references for image-to-image generation, assigned aliases for easy access, or stored as
standalone assets.
</p>
<TipBox variant="compact" type="info">
<strong>Tip:</strong> Use aliases like <InlineCode>@logo</InlineCode> to reference
uploaded images by name instead of UUID.
</TipBox>
</section>
{/* Endpoint */}
<section id="endpoint" className="mb-12">
<SectionHeader level={2} id="endpoint">
Upload Endpoint
</SectionHeader>
<EndpointCard
method="POST"
endpoint="/api/v1/images/upload"
baseUrl="https://api.banatie.app"
/>
<p className="text-gray-400 text-sm mt-4">
Requires <InlineCode>X-API-Key</InlineCode> header with your Project Key.
Content-Type must be <InlineCode>multipart/form-data</InlineCode>.
</p>
</section>
{/* Parameters */}
<section id="parameters" className="mb-12">
<SectionHeader level={2} id="parameters" className="mb-6">
Form Parameters
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Send parameters as <InlineCode>multipart/form-data</InlineCode>:
</p>
<Table
headers={['Parameter', 'Type', 'Required', 'Description']}
rows={formParameters.map((param) => [
<InlineCode key="name">{param.name}</InlineCode>,
<span key="type" className="text-cyan-400">{param.type}</span>,
<span key="required" className={param.required ? 'text-green-400' : 'text-gray-500'}>
{param.required ? 'Yes' : 'No'}
</span>,
param.description,
])}
/>
<div className="mt-6">
<CodeBlock
code={`curl -X POST https://api.banatie.app/api/v1/images/upload \\
-H "X-API-Key: YOUR_PROJECT_KEY" \\
-F "file=@logo.png" \\
-F "alias=@brand-logo" \\
-F 'meta={"tags": ["logo", "brand"]}'`}
language="bash"
filename="Example Request"
/>
</div>
</section>
{/* File Constraints */}
<section id="constraints" className="mb-12">
<SectionHeader level={2} id="constraints" className="mb-6">
File Constraints
</SectionHeader>
<Table
headers={['Constraint', 'Limit']}
rows={fileConstraints.map(([constraint, limit]) => [
constraint,
<InlineCode key="limit">{limit}</InlineCode>,
])}
/>
<div className="mt-6">
<TipBox variant="compact" type="warning">
<strong>File Size:</strong> Files larger than 5MB will be rejected with a 400 error.
Compress images before uploading if needed.
</TipBox>
</div>
</section>
{/* Flow Association */}
<section id="flow-association" className="mb-12">
<SectionHeader level={2} id="flow-association" className="mb-6">
Flow Association
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Images can be associated with generation flows for organized workflows.
The <InlineCode>flowId</InlineCode> parameter controls this behavior:
</p>
<Table
headers={['flowId Value', 'Behavior']}
rows={flowIdBehavior.map(([value, behavior]) => [
<InlineCode key="value">{value}</InlineCode>,
behavior,
])}
/>
<div className="mt-6">
<CodeBlock
code={`# Associate with existing flow
curl -X POST https://api.banatie.app/api/v1/images/upload \\
-H "X-API-Key: YOUR_PROJECT_KEY" \\
-F "file=@reference.png" \\
-F "flowId=550e8400-e29b-41d4-a716-446655440000" \\
-F "flowAlias=@reference"`}
language="bash"
filename="Upload with Flow"
/>
</div>
</section>
{/* Response */}
<section id="response" className="mb-12">
<SectionHeader level={2} id="response" className="mb-4">
Response Format
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
On success, the API returns a JSON object containing the uploaded image details.
</p>
<CodeBlock
code={`{
"success": true,
"data": {
"id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
"projectId": "57c7f7f4-47de-4d70-9ebd-3807a0b63746",
"flowId": null,
"storageUrl": "https://cdn.banatie.app/default/my-project/img/7c4ccf47-41ce-4718-afbc-8c553b2c631a",
"mimeType": "image/png",
"fileSize": 45678,
"width": 512,
"height": 512,
"source": "uploaded",
"alias": "@brand-logo",
"focalPoint": null,
"meta": { "tags": ["logo", "brand"] },
"createdAt": "2025-11-28T10:00:00.000Z"
}
}`}
language="json"
filename="Success Response"
/>
<TipBox variant="compact" type="info">
<strong>Note:</strong> The <InlineCode>id</InlineCode> field equals the filename in storage (UUID).
Original filename is preserved in object metadata.
</TipBox>
</section>
{/* Error Codes */}
<section id="error-codes" className="mb-12">
<SectionHeader level={2} id="error-codes" className="mb-6">
Error Codes
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
The API uses standard HTTP status codes and returns descriptive error messages.
</p>
<Table
headers={['Status', 'Code', 'Description']}
rows={errorCodes.map(([status, code, description]) => [
<InlineCode key="status" color="error">{status}</InlineCode>,
<InlineCode key="code">{code}</InlineCode>,
description,
])}
/>
</section>
</DocPage>
);
}

View File

@ -1,313 +0,0 @@
'use client';
/**
* Authentication Guide
*
* Refactored to use DocPage component for consistent layout
* Layout handles SubsectionNav and Left Sidebar
* DocPage handles Breadcrumb, Article structure, Next Steps, and TOC
* This page provides only the content (Hero + sections)
*/
import { TipBox } from '@/components/docs/shared/TipBox';
import { Table } from '@/components/docs/shared/Table';
import { CodeBlock } from '@/components/docs/shared/CodeBlock';
import { DocPage } from '@/components/docs/layout/DocPage';
import {
Hero,
SectionHeader,
InlineCode,
} from '@/components/docs/blocks';
const tocItems = [
{ id: 'overview', text: 'Overview', level: 2 },
{ id: 'api-keys', text: 'API Keys', level: 2 },
{ id: 'key-types', text: 'Key Types', level: 3 },
{ id: 'creating-keys', text: 'Creating Keys', level: 3 },
{ id: 'using-keys', text: 'Using API Keys', level: 2 },
{ id: 'rate-limits', text: 'Rate Limits', level: 2 },
{ id: 'security', text: 'Security Best Practices', level: 2 },
{ id: 'next-steps', text: 'Next Steps', level: 2 },
];
export default function AuthenticationGuidePage() {
return (
<DocPage
breadcrumbItems={[
{ label: 'Documentation', href: '/docs' },
{ label: 'Guides', href: '/docs/guides' },
{ label: 'Authentication' },
]}
tocItems={tocItems}
nextSteps={{
links: [
{
href: '/docs/api/text-to-image',
title: 'Start Generating Images',
description: 'Explore the Text to Image API and start building your integration.',
accent: 'primary',
},
{
href: '/docs/guides/error-handling',
title: 'Error Handling Guide',
description: 'Learn how to handle authentication errors and implement retry logic.',
accent: 'secondary',
},
],
}}
>
{/* Hero Section */}
<Hero
title="Authentication"
subtitle="Learn how to authenticate with the Banatie API using API keys, manage rate limits, and implement security best practices."
/>
{/* Overview */}
<section id="overview" className="mb-12">
<SectionHeader level={2} id="overview" className="mb-4">
Overview
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Banatie uses API keys to authenticate requests. All API endpoints require authentication
via the <InlineCode>X-API-Key</InlineCode> header.
API keys are tied to organizations and projects, providing fine-grained access control.
</p>
<TipBox variant="compact" type="info">
<strong>Quick Start:</strong> New to API authentication? Check out our{' '}
<a href="/docs" className="text-purple-400 hover:underline">
Getting Started guide
</a>{' '}
for a step-by-step walkthrough.
</TipBox>
</section>
{/* API Keys */}
<section id="api-keys" className="mb-12">
<SectionHeader level={2} id="api-keys" className="mb-6">
API Keys
</SectionHeader>
<div id="key-types" className="mb-8">
<SectionHeader level={3} id="key-types" className="mb-4">
Key Types
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
Banatie supports two types of API keys, each with different permissions and use cases:
</p>
<Table
headers={['Key Type', 'Permissions', 'Expiration', 'Use Case']}
rows={[
[
<InlineCode key="type">Master Key</InlineCode>,
'Full admin access, can create/revoke keys',
<span key="exp" className="text-green-400">Never expires</span>,
'Server-side admin operations, key management',
],
[
<InlineCode key="type" color="success">Project Key</InlineCode>,
'Image generation only',
<span key="exp" className="text-amber-400">90 days</span>,
'Application integration, API requests',
],
]}
/>
<div className="mt-6">
<TipBox variant="prominent" type="warning">
<strong className="text-amber-300">Master Key Security:</strong> Master keys have full
administrative access and never expire. Store them securely in encrypted vaults or
secret managers. Never expose master keys in client-side code, logs, or version control.
Use project keys for application integration whenever possible.
</TipBox>
</div>
</div>
<div id="creating-keys" className="mb-8">
<SectionHeader level={3} id="creating-keys" className="mb-4">
Creating Keys
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
For first-time setup, use the bootstrap endpoint to create your initial master key:
</p>
<CodeBlock
code={`# Bootstrap your first master key (one-time only)
curl -X POST https://api.banatie.com/api/bootstrap/initial-key
# Response
{
"success": true,
"data": {
"key": "bnt_master_abc123...",
"type": "master"
}
}`}
language="bash"
filename="Bootstrap Master Key"
/>
<div className="mt-6">
<p className="text-gray-300 leading-relaxed mb-4">
Once you have a master key, you can create project keys for your applications:
</p>
<CodeBlock
code={`# Create a project key with master key authentication
curl -X POST https://api.banatie.com/api/admin/keys \\
-H "X-API-Key: YOUR_MASTER_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"type": "project",
"projectId": "my-app",
"name": "Production API Key"
}'
# Response
{
"success": true,
"data": {
"key": "bnt_project_xyz789...",
"type": "project",
"expiresAt": "2025-04-14T00:00:00Z"
}
}`}
language="bash"
filename="Create Project Key"
/>
</div>
</div>
</section>
{/* Using API Keys */}
<section id="using-keys" className="mb-12">
<SectionHeader level={2} id="using-keys" className="mb-4">
Using API Keys
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Include your API key in the <InlineCode>X-API-Key</InlineCode> header
with every request:
</p>
<CodeBlock
code={`# Example: Generate an image with project key
curl -X POST https://api.banatie.com/api/text-to-image \\
-H "X-API-Key: bnt_project_xyz789..." \\
-H "Content-Type: application/json" \\
-d '{
"prompt": "a sunset over the ocean",
"aspectRatio": "16:9"
}'`}
language="bash"
filename="Authenticated Request"
/>
<div className="mt-6">
<TipBox variant="compact" type="info">
<strong>Environment Variables:</strong> Store API keys in environment variables, not
hardcoded in your application. Example:{' '}
<InlineCode>BANATIE_API_KEY</InlineCode>
</TipBox>
</div>
</section>
{/* Rate Limits */}
<section id="rate-limits" className="mb-12">
<SectionHeader level={2} id="rate-limits" className="mb-6">
Rate Limits
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-6">
API keys are subject to rate limits to ensure fair usage and system stability. Limits
vary by key type and plan tier:
</p>
<Table
headers={['Key Type', 'Rate Limit', 'Reset Window', 'Upgrade Available']}
rows={[
[
<InlineCode key="type">Master Key</InlineCode>,
<span key="limit" className="text-green-400">Unlimited</span>,
'N/A',
'N/A',
],
[
<InlineCode key="type" color="success">Project Key (Free)</InlineCode>,
<span key="limit" className="text-amber-400">100 requests/hour</span>,
'1 hour rolling',
<a key="upgrade" href="/pricing" className="text-purple-400 hover:underline">Yes</a>,
],
[
<InlineCode key="type" color="success">Project Key (Pro)</InlineCode>,
<span key="limit" className="text-green-400">1,000 requests/hour</span>,
'1 hour rolling',
<a key="upgrade" href="/pricing" className="text-purple-400 hover:underline">Yes</a>,
],
]}
/>
<div className="mt-6">
<p className="text-gray-300 leading-relaxed mb-4">
When you exceed rate limits, the API returns a <InlineCode color="error">429 Too Many Requests</InlineCode> status.
Check the response headers for retry timing:
</p>
<CodeBlock
code={`# Rate limit response headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704153600
# Error response body
{
"error": "Rate limit exceeded",
"retryAfter": 3600
}`}
language="bash"
filename="Rate Limit Response"
/>
</div>
</section>
{/* Security Best Practices */}
<section id="security" className="mb-12">
<SectionHeader level={2} id="security" className="mb-6">
Security Best Practices
</SectionHeader>
<TipBox variant="prominent" type="warning">
<strong className="text-amber-300">Critical Security Guidelines:</strong>
<ul className="mt-3 space-y-2 list-disc list-inside">
<li>Never commit API keys to version control systems (Git, SVN, etc.)</li>
<li>Store keys in environment variables or secret management services</li>
<li>Use project keys in applications, reserve master keys for admin operations</li>
<li>Rotate keys regularly, especially after team member changes</li>
<li>Implement server-side API calls for production applications</li>
<li>Monitor API key usage in your dashboard for suspicious activity</li>
</ul>
</TipBox>
<div className="mt-6">
<h3 className="text-lg font-semibold text-white mb-3">Key Rotation Example</h3>
<CodeBlock
code={`# 1. Create new project key
curl -X POST https://api.banatie.com/api/admin/keys \\
-H "X-API-Key: YOUR_MASTER_KEY" \\
-d '{"type": "project", "projectId": "my-app", "name": "New Key"}'
# 2. Update your application to use new key
export BANATIE_API_KEY="bnt_project_new..."
# 3. Revoke old key
curl -X DELETE https://api.banatie.com/api/admin/keys/OLD_KEY_ID \\
-H "X-API-Key: YOUR_MASTER_KEY"`}
language="bash"
filename="Key Rotation Process"
/>
</div>
</section>
</DocPage>
);
}

View File

@ -40,15 +40,15 @@ export default function GettingStartedPage() {
'Now that you have generated your first image, explore these resources to build more advanced integrations:',
links: [
{
href: '/docs/api/text-to-image',
title: 'API Reference',
href: '/docs/api/generate',
title: 'Image Generation',
description: 'Explore all available endpoints, parameters, and response formats.',
accent: 'primary',
},
{
href: '/docs/guides/authentication',
title: 'Authentication Guide',
description: 'Learn about API keys, rate limits, and security best practices.',
href: '/docs/api/live',
title: 'Live URLs & CDN',
description: 'Generate images on-demand via URL parameters with automatic caching.',
accent: 'secondary',
},
],
@ -67,9 +67,8 @@ export default function GettingStartedPage() {
Introduction
</SectionHeader>
<p className="text-gray-300 leading-relaxed mb-4">
Banatie is a developer-first API for AI-powered image generation. Built on Google Gemini
2.5 Flash and Imagen 4.0, it transforms text prompts and reference images into
production-ready visuals.
Banatie is a developer-first API for AI-powered image generation. Built on Google Gemini,
it transforms text prompts and reference images into production-ready visuals.
</p>
<p className="text-gray-300 leading-relaxed mb-6">
Whether you are building a content creation platform, e-commerce site, or creative tool,
@ -135,7 +134,7 @@ go get github.com/banatie/sdk-go`}
<CodeBlock
code={`# Create your first API key (one-time bootstrap)
curl -X POST https://api.banatie.com/api/bootstrap/initial-key
curl -X POST https://api.banatie.app/api/bootstrap/initial-key
# Save the returned key securely
export BANATIE_API_KEY="bnt_your_key_here"`}
@ -156,12 +155,11 @@ export BANATIE_API_KEY="bnt_your_key_here"`}
</p>
<CodeBlock
code={`curl -X POST https://api.banatie.com/api/text-to-image \\
code={`curl -X POST https://api.banatie.app/api/v1/generations \\
-H "X-API-Key: YOUR_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"prompt": "a serene mountain landscape at sunset",
"filename": "mountain_sunset",
"aspectRatio": "16:9",
"autoEnhance": true
}'`}
@ -173,17 +171,22 @@ export BANATIE_API_KEY="bnt_your_key_here"`}
<p className="text-sm font-semibold text-gray-300 mb-3">Expected Response:</p>
<ResponseBlock
status="success"
statusCode={200}
statusLabel="✓ 200 Success"
statusCode={201}
statusLabel="201 Created"
content={`{
"success": true,
"data": {
"url": "https://cdn.banatie.com/org/project/generated/2025-01/mountain_sunset.png",
"filepath": "org/project/generated/2025-01/mountain_sunset.png",
"width": 1920,
"height": 1080,
"promptEnhancement": {
"enhancedPrompt": "A breathtaking mountain landscape..."
"id": "550e8400-e29b-41d4-a716-446655440000",
"prompt": "A breathtaking mountain landscape at sunset...",
"originalPrompt": "a serene mountain landscape at sunset",
"autoEnhance": true,
"aspectRatio": "16:9",
"status": "success",
"outputImage": {
"id": "7c4ccf47-41ce-4718-afbc-8c553b2c631a",
"storageUrl": "https://cdn.banatie.app/default/my-project/img/7c4ccf47-...",
"width": 1792,
"height": 1024
}
}
}`}

View File

@ -1,12 +1,50 @@
import { MetadataRoute } from 'next';
export default function sitemap(): MetadataRoute.Sitemap {
const baseUrl = 'https://banatie.app';
return [
{
url: 'https://banatie.app/',
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1,
},
{
url: `${baseUrl}/docs`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.9,
},
{
url: `${baseUrl}/docs/api/generate`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${baseUrl}/docs/api/advanced`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${baseUrl}/docs/api/upload`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${baseUrl}/docs/api/images`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${baseUrl}/docs/api/live`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
];
}

View File

@ -19,9 +19,8 @@
*
* Navigation Structure:
* - Getting Started
* - API Reference (collapsible)
* - Guides (collapsible)
* - Examples
* - API Reference (collapsible): Generation, Advanced, Upload, Images, Live URLs
* - Guides (collapsible): Authentication, API Keys
*/
import { useState } from 'react';
@ -48,30 +47,17 @@ const navigationItems: NavItem[] = [
href: '/docs/api',
icon: '📚',
children: [
{ label: 'Text to Image', href: '/docs/api/text-to-image' },
{ label: 'Upload', href: '/docs/api/upload' },
{ label: 'Images', href: '/docs/api/images' },
{ label: 'Image Generation', href: '/docs/api/generate' },
{ label: 'Advanced Generation', href: '/docs/api/advanced' },
{ label: 'Image Upload', href: '/docs/api/upload' },
{ label: 'Image Management', href: '/docs/api/images' },
{ label: 'Live URLs', href: '/docs/api/live' },
],
},
{
label: 'Guides',
href: '/docs/guides',
icon: '📖',
children: [
{ label: 'Authentication', href: '/docs/guides/authentication' },
{ label: 'Error Handling', href: '/docs/guides/error-handling' },
{ label: 'Rate Limits', href: '/docs/guides/rate-limits' },
],
},
{
label: 'Examples',
href: '/docs/examples',
icon: '💡',
},
];
export const DocsSidebar = ({ currentPath }: DocsSidebarProps) => {
const [expandedSections, setExpandedSections] = useState<string[]>(['API Reference', 'Guides']);
const [expandedSections, setExpandedSections] = useState<string[]>(['API Reference']);
const toggleSection = (label: string) => {
setExpandedSections((prev) =>