| title | API Endpoints |
|---|---|
| description | Complete reference for all Minimal Block API endpoints — methods, request bodies, and response shapes. |
| outline | deep |
All endpoints are served by the Cloudflare Worker at apps/api.
Base URL (local dev): http://localhost:8787
Base URL (production): your Cloudflare Worker URL or custom domain.
Every endpoint except /health and OPTIONS preflight requires a Supabase JWT:
Authorization: Bearer <supabase-access-token>
Requests without a valid bearer token return 401 Unauthorized.
All responses include:
Access-Control-Allow-Origin: <CORS_ORIGIN env var, default *>
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
Access-Control-Allow-Headers: authorization, content-type
Send an OPTIONS preflight on any path to receive a 204 No Content with these headers.
Returns the liveness status of the worker. No authentication required.
Response 200
{ "ok": true }Create a new product and kick off a 3D model generation job from source images.
Request body
{
product: {
name: string // required
description?: string
category: string // e.g. "furniture", "electronics"
}
sourceAssets: Array<{ // required, at least 1
url: string
storageKey: string
mimeType: string // e.g. "image/jpeg"
sizeBytes: number
}>
manualModelAsset?: { // skip AI generation, use this GLB directly
url: string
storageKey: string
mimeType: string
sizeBytes: number
}
qualityHint?: string // hint passed to Gemini ("high_detail", etc.)
}Response 200
{
productId: string
conversionId: string
jobId: string
status: "processing" | "awaiting_approval" | "approved" | "failed"
}Fetch the current state of a conversion.
Path parameter: conversionId — UUID of the conversion.
Response 200
{
conversion: {
id: string
productId: string
ownerId: string
status: string
sourceAssets: Array<{ url; storageKey; mimeType; sizeBytes }>
outputAsset?: { url; storageKey; mimeType; sizeBytes }
errorMessage?: string
provider: string
qualityReport?: { ... }
approvedAt?: string // ISO 8601
rejectionReason?: string
createdAt: string
updatedAt: string
}
}Error 404 — conversion not found or not owned by caller.
Mark a conversion as approved and publish the 3D model.
Also records a positive feedback signal for the Gemini generation loop.
Path parameter: conversionId
Request body: none
Response 200 — same shape as GET /api/conversions/:id.
Reject a conversion with an optional reason.
Records a negative feedback signal for the generation loop.
Path parameter: conversionId
Request body
{ reason?: string }Response 200 — same shape as GET /api/conversions/:id.
Scrape a product URL, extract images and metadata, then run AI autofill to populate name, description, category, materials, and dimensions.
Request body
{ url: string } // e.g. "https://www.ikea.com/..."Response 200
{
product: {
productId: string
name: string
description: string
category: string
workflowStatus: string
inputMethod: string
importData: { ... } | null
}
}Save the seller's reviewed and confirmed import — selected images, edited fields, and confirmed metadata.
Triggers AI image analysis and readiness scoring.
Path parameter: productId
Request body
{
title: string // required, non-empty
description: string
category: string
materials: string[]
dimensions: string
selectedImageIds: string[] // required, at least 1
sellerConfirmedText: boolean // required true
sellerConfirmedImages: boolean // required true
}Response 200
{
product: { /* ProductImportSnapshot */ }
selectedImages: Array<{ /* ImageCandidate */ }>
readinessScore: number // 0–100
}Re-scrape the original import URL from scratch. Useful when the initial scrape failed or returned incomplete data.
Path parameter: productId
Request body: none
Response 200 — same shape as POST /api/products/import-url.
When a scraped page contains multiple products, accept one cluster (sub-product) and scope the import to its images and fields.
Path parameter: productId
Request body
{ clusterId: string } // requiredResponse 200
{ product: { /* ProductImportSnapshot */ } }Error 400 — product has no clusters or clusterId not found.
Kick off a 3D model generation job using the product's already-imported and selected source images.
Path parameter: productId
Request body: none
Response 200 — same shape as POST /api/conversions.
Run Gemini analysis on the product: materials, confidence score, missing visuals, return risk factors, quality and merchant recommendations, and readiness score.
Request body
{ productId: string }Response 200
{
analysis: {
categorySuggestion?: string
materials: string[]
confidenceScore: number
missingVisuals: string[]
suggestedCopy: { seoTitle; bullets; description } | null
returnRiskFactors: Array<{ risk: string; fix: string }>
qualityRecommendations: string[]
merchantRecommendations: string[]
readinessScore?: number
lastUpdatedAt: string
}
}Generate 3–5 interactive hotspot suggestions for the product 3D viewer (material, dimension, feature, warning, or assembly annotations).
Request body
{ productId: string }Response 200
{
hotspots: Array<{
id: string
title: string
description: string
type: "material" | "dimension" | "feature" | "warning" | "assembly"
status: "pending"
}>
}Generate SEO-optimised e-commerce copy (title, bullet points, description) for the product.
Request body
{ productId: string }Response 200
{
suggestedCopy: {
seoTitle: string
bullets: string[]
description: string
} | null
}Identify return-risk factors and their suggested fixes for the product.
Request body
{ productId: string }Response 200
{
returnRiskFactors: Array<{ risk: string; fix: string }>
}Compute a composite readiness score and quality recommendations, combining:
- Conversion quality report (file size, triangle count, texture dim)
- Gemini Visual QA score (if available)
- Import image readiness (unique view coverage)
Request body
{ productId: string }Response 200
{
readinessScore?: number // 0–100
qualityRecommendations: string[]
}Generate a Trendyol product listing draft (title, description, category, brand, pricing, attributes) using Gemini.
Request body
{ productId: string }Response 200
{
draft: {
title: string
description: string
categoryId: number
brandName: string
listPrice: number
salePrice: number
attributes: Array<{ name: string; value: string }>
}
}Submit one or more product listings to the Trendyol catalog.
Request body
{
items: TrendyolProduct[] // at least 1
}Response 200
{ batchRequestId: string }Poll the result of a Trendyol batch product submission.
Path parameter: batchRequestId
Response 200
{
batch: {
batchRequestId: string
status: string
items: Array<{ ... }>
}
}List unapproved Trendyol products for the seller account (paginated, 20 per page).
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
page |
number | 0 |
Zero-based page index |
Response 200
{
content: TrendyolUnapprovedProduct[]
totalElements: number
}Retrieve buybox information for up to 10 product barcodes.
Request body
{ barcodes: string[] } // 1–10 barcodesResponse 200
{ result: TrendyolBuyboxResult[] }List shipment packages (orders) with optional status filtering (paginated).
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
page |
number | 0 |
Zero-based page index |
size |
number | 50 |
Items per page |
status |
string | — | Filter by package status |
Response 200
{
content: TrendyolPackage[]
totalPages: number
totalElements: number
}Update the status of a shipment package (e.g. mark as Picking or Invoiced).
Path parameter: packageId
Request body
{
status: "Picking" | "Invoiced" // required
invoiceNumber?: string // required when status = "Invoiced"
}Response 200
{ ok: true }All errors follow a consistent shape:
{ error: string }| HTTP status | Condition |
|---|---|
400 Bad Request |
Missing or invalid request fields |
401 Unauthorized |
Missing, malformed, or expired bearer token |
404 Not Found |
Resource not found or not owned by the caller |
500 Internal Server Error |
Unexpected server or AI provider error |