@creativekit/client
v1.0.23
Published
Browser-safe CreativeKit SDK (no secrets required)
Downloads
1,550
Maintainers
Readme
@creativekit/client
Browser-safe CreativeKit SDK using presigned URLs
✅ No credentials needed - talks to YOUR backend only
Quick Start
npm install @creativekit/clientimport { CreativeKitClient } from "@creativekit/client";
const client = new CreativeKitClient({
backendUrl: "/api/creatives", // YOUR backend, not CreativeKit API
});
// Upload a file with progress tracking
const result = await client.upload(file, "audio:podcast-aac", (progress) => {
console.log(`${progress.percent.toFixed(1)}% - ${progress.message}`);
});
console.log("Creative:", result.id, result.status);
// Status values are UPPERCASE: "AWAITING_UPLOAD" | "QUEUED" | "PROCESSING" | "SUCCEEDED" | "FAILED" | "CANCELED"How It Works
1. Client → Your Backend: "Give me upload URL"
2. Your Backend → CreativeKit API: Create creative (using server SDK)
3. Client → Storage: Upload directly to presigned URL
4. Client → Your Backend: "Commit creative" (triggers processing)
5. Client polls or streams SSE for completionYour backend needs these endpoints:
| Endpoint | Description |
| ----------------------------------- | -------------------------------------- |
| POST /api/creatives | Create and return presigned upload URL |
| POST /api/creatives/:id/commit | Commit upload to start processing |
| GET /api/creatives/:id | Get status and artifacts |
| GET /api/creatives/:id/events | SSE stream for live updates (optional) |
| POST /api/creatives/:id/finalize | Mark creative as permanent |
| GET /api/creatives/profiles | List available profiles |
API Reference
Upload Methods
// Simple upload (handles everything automatically)
// Includes: request URL → upload → commit → wait for completion
const result = await client.upload(file, profile, onProgress);
// Manual control (advanced)
const request = await client.requestUpload(profile, {
fileName: file.name, // Optional: original filename
fileSize: file.size, // Optional: file size in bytes
contentType: file.type, // Optional: MIME type
});
await client.uploadFile(
request.uploadUrl,
file,
request.uploadHeaders,
(loaded, total) => console.log(`${loaded}/${total} bytes`), // Optional progress
);
await client.commit(request.creativeId); // IMPORTANT: Must call to start processing
// Cancel an in-progress upload
client.cancelUpload();Status & Monitoring
// Get creative status and artifacts
const creative = await client.getCreative(id);
// Status values: "AWAITING_UPLOAD" | "QUEUED" | "PROCESSING" | "SUCCEEDED" | "FAILED" | "CANCELED"
// AWAITING_UPLOAD = waiting for file upload + commit (rare in client flow)
// Progress is 0.0-1.0 (multiply by 100 for percentage)
// Stream live status updates via SSE
// NOTE: Stream auto-closes on terminal states (SUCCEEDED, FAILED, CANCELED)
const stream = client.streamEvents(creativeId, {
onUpdate: (update) => {
// update.progress is 0.0-1.0, multiply by 100 for percentage
console.log(`Status: ${update.status}, Progress: ${update.progress * 100}%`);
},
onError: (error) => console.error("Stream error:", error),
onClose: () => console.log("Stream closed"),
});
// Manual close if needed
stream.close();
// Check stream status
console.log(stream.closed); // booleanLifecycle Management
// Finalize a creative (mark as permanent, prevent auto-cleanup)
await client.finalize(creativeId);
// List available processing profiles
const profiles = await client.listProfiles();
// Returns: Profile[] with id, type, description, etc.Progress Tracking
interface UploadProgress {
stage:
| "preparing" // Requesting upload URL
| "uploading" // Uploading to presigned URL
| "committing" // Committing upload
| "processing" // Server processing
| "complete" // Done successfully
| "failed"; // Error occurred
percent: number; // 0-100
message: string; // Human-readable status
timeElapsed: number; // Milliseconds since start
estimatedTimeRemaining?: number; // Milliseconds remaining (when available)
}Progress stages and percentages:
preparing: 0%uploading: 10-80% (based on bytes uploaded)committing: 80%processing: 85-100% (based on server progress)complete: 100%
React Example
import { CreativeKitClient } from "@creativekit/client";
import { useState } from "react";
const client = new CreativeKitClient({ backendUrl: "/api/creatives" });
function UploadForm() {
const [progress, setProgress] = useState(0);
const [status, setStatus] = useState("");
const handleUpload = async (file: File) => {
try {
const result = await client.upload(file, "audio:podcast-aac", (p) => {
setProgress(p.percent);
setStatus(p.message);
});
console.log("Done!", result.id, result.status);
} catch (error) {
console.error("Upload failed:", error);
}
};
return (
<div>
<input type="file" onChange={(e) => handleUpload(e.target.files![0])} />
<progress value={progress} max={100} />
<p>{status}</p>
</div>
);
}SSE for Real-Time Updates
For monitoring creatives outside the upload flow:
import { useEffect, useRef } from "react";
function CreativeStatus({ creativeId }: { creativeId: string }) {
const [status, setStatus] = useState("PROCESSING");
useEffect(() => {
const stream = client.streamEvents(creativeId, {
onUpdate: (update) => {
setStatus(update.status);
// SSE = signal only. Fetch full details on terminal state:
if (["SUCCEEDED", "FAILED", "CANCELED"].includes(update.status)) {
client.getCreative(creativeId).then(setCreative);
}
},
});
return () => stream.close();
}, [creativeId]);
return <div>Status: {status}</div>;
}Note: SSE events contain only
id,status, andprogress. CallgetCreative()for full details (artifacts, failure info, etc.).
Finalize Workflow (Draft → Permanent)
Processed creatives start in a "draft" state. They can be previewed but will be automatically cleaned up after the draft TTL expires (default: 24 hours). To keep a creative permanently:
// After user reviews the preview and confirms
await client.finalize(creativeId);
// The creative is now permanent and won't be auto-deleted
const creative = await client.getCreative(creativeId);
console.log(creative.finalized); // true
console.log(creative.finalizedAt); // timestamp in millisecondsWhy finalize?
- Cost control: Unfinalized drafts are cleaned up automatically
- User confirmation: Let users preview before committing to storage
- Idempotent: Safe to call multiple times (no error if already finalized)
Exported Types
The SDK exports these TypeScript types for your use:
import {
// Client
CreativeKitClient,
CreativeKitClientConfig,
// Progress & Events
UploadProgress,
StreamEventsOptions,
CreativeStatusUpdate,
EventStream,
// Errors
APIError,
ErrorDetail,
// Re-exported from @creativekit/shared
Profile,
CreativeStatusResponse,
UploadResult,
// ... and more
} from "@creativekit/client";Key Types
// SSE event update (minimal signal)
interface CreativeStatusUpdate {
id: string;
status: string; // "AWAITING_UPLOAD" | "QUEUED" | "PROCESSING" | "SUCCEEDED" | "FAILED" | "CANCELED"
progress?: number; // 0.0-1.0
}
// SSE stream controller
interface EventStream {
close(): void;
readonly closed: boolean;
}
// API error structure (Google API conventions)
interface APIError {
code: number; // HTTP status code
status: string; // e.g., "FAILED_PRECONDITION"
message: string; // Human-readable message
details?: ErrorDetail[];
}Error Handling
The SDK throws ApiError for all API failures:
import { ApiError } from "@creativekit/client";
try {
const result = await client.upload(file, profile);
} catch (error) {
if (error instanceof ApiError) {
console.error("API Error:", error.message);
// Access error details
if (error.details?.code === "VALIDATION_FAILED") {
console.error("Validation failed at step:", error.details.step);
}
if (error.details?.code === "PROCESSING_FAILED") {
console.error("Processing failed:", error.details.cause);
console.error("Retriable:", error.details.retriable);
}
}
}Common error codes:
VALIDATION_FAILED- File validation failed (wrong format, size, etc.)PROCESSING_FAILED- Processing pipeline failed404- Creative not found400- Bad request (e.g., creative not ready for finalization)
SSE Fallback to Polling
The upload() method automatically handles SSE availability:
- Tries SSE first - Connects to your backend's
/eventsendpoint - Falls back to polling - If SSE fails or doesn't connect within 2 seconds
- Polls every 2 seconds - Until processing completes
This is handled automatically - no configuration needed.
Full Documentation
See the main SDK documentation for:
- Backend setup examples (Express, Next.js, NestJS)
- Complete API reference
- AWS-style pattern explanation
- Troubleshooting guide
Security
- ✅ Never accesses CreativeKit API directly
- ✅ No credentials in browser
- ✅ Only talks to YOUR backend
- ✅ You control access with your auth
License
MIT
