Overview
All endpoints are served under https://api.huhu.ai/public/v1. Create an API key from Studio → API → API Keys to get started.
Data Model
Understanding the entity hierarchy:
Team
├── Program (workspace / brand)
│ ├── Model (AI fashion model identity)
│ ├── Project (batch of work)
│ │ └── SKU (single product/garment)
│ │ └── Task (generation job → output images)
└── Credits (shared pool for all operations)- Team — top-level billing unit. API key is bound to one team.
- Program — workspace (brand/product line). Contains models, projects. Can be shared across teams.
- Project — batch of work (collection/campaign). Contains SKUs. Status: Creating → Created → In process → Completed.
- SKU — single product. Has garment images, category (13 types), and generation tasks.
- Task — generation job. Lifecycle: Created → Submitted → Generation → Editing → Review → Approved.
- Credits — shared pool. Each operation deducts credits. API and UI share the same pool.
Credit Costs
| Operation | Credits |
|---|---|
| Image Generation (SD) | 1 |
| Image Generation (HD) / Same-ID | 2 |
| Multipose Generation | 2 |
| Image Editing / Inpainting | 2 |
| Face Swap / Upscale | 2 |
| Background Change / Removal | 2 |
| Hand Fix / Foot Fix | 2 |
| Logo Transfer / Accessory Try-On | 2 |
| Ghost Mannequin | 2 |
| Prompt Generation | 2 |
| Video Generation (5s) | 25 |
| Video Generation (10s) | 40 |
New teams receive 50 free credits and 10 concurrent request slots.
Authentication
Create an API key from the API Keys page. Include it in every request:
curl https://api.huhu.ai/public/v1/metering/plan \
-H "Authorization: Bearer sk-<your-api-key>"Keys are scoped to a team. Requests to resources outside your team return 403. Revoked or invalid keys return 401.
Base URL
Production: https://api.huhu.ai/public/v1
Gamma: https://api-gamma.huhu.ai/public/v1Metering
/metering/planCheck your team's credit balance, plan status, and concurrent request limit. Call this before generation requests.
| Parameter | Type | Required | Description |
|---|---|---|---|
program_id | string | No | Resolves owner team for shared programs |
# Response
{
"credits": 99999,
"plan_status": "free",
"concurrent_requests": 10,
"plan_name": null,
"subscription_id": null
}/metering/credit-historyGet paginated credit usage history for a program.
| Parameter | Type | Required | Description |
|---|---|---|---|
program_id | string | No | Program ID (uses key default if omitted) |
pagination_token | string | No | Token for next page |
fetch_all | boolean | No | Fetch all records (max 10,000) |
# Response
{
"items": [
{ "program_id": "xxx", "request_id": "xxx", "credits": 1, "timestamp": 1773702421471, "operation": "model_gen" }
],
"pagination_token": null
}Programs
/programList programs accessible to your team.
| Parameter | Type | Required | Description |
|---|---|---|---|
owned | bool | No | If true, only programs you own (default: false) |
pagination_token | string | No | Token for next page |
# Response
{ "programs": [{ "program_id": "xxx", "program_name": "My Brand", "create_time": 1773702421471 }], "pagination_token": null }/programCreate a new program (workspace) within your team.
| Parameter | Type | Required | Description |
|---|---|---|---|
program_name | string | Yes | Name for the program |
# Response
{
"program_name": "My New Brand",
"user_id": "xxx", "create_time": 1773702421471
}Projects
/projectList projects, or get a single project detail by adding project_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | No | If provided, returns single project detail |
pagination_token | string | No | Token for next page (list only) |
# List response (no project_id)
{ "projects": [{ "project_id": "xxx", "project_name": "Spring", "project_status": "Created" }], "pagination_token": null }
# Detail response (with project_id)
{ "project_id": "xxx", "project_name": "Spring", "project_status": "Created", "create_time": 1773702421471 }/projectCreate a new project within a program.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_name | string | Yes | Name for the project |
program_id | string | No | Program ID (uses key default if omitted) |
/project/uploadUpload garment images (base64) or initiate multipart zip upload for SKU archives.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project ID |
file_type | string | Yes | "garment_image", "sku_zip", "model_image", "reference_image" |
program_id | string | No | Program ID (uses key default if omitted) |
file_data | string | No | Base64 image data (for image types) |
file_size | number | No | File size in bytes (for sku_zip) |
# For garment_image → { "success": true, "image_id": "xxx" }
# For sku_zip → { "message": "success", "key": "...", "upload_id": "...", "upload_urls": ["..."] }/project/complete_multipart_uploadComplete a multipart zip upload after all parts are uploaded to presigned URLs.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project ID |
key | string | Yes | S3 key from upload response |
upload_id | string | Yes | Upload ID from upload response |
parts | object[] | Yes | [{PartNumber, ETag}] for each part |
program_id | string | No | Program ID (uses key default if omitted) |
# Response: { "message": "success" }Models
/modelCreate a new AI model in the gallery with searchable attributes. Add images after with /model/image.
| Parameter | Type | Required | Description |
|---|---|---|---|
model_name | string | Yes | Display name |
model_type | string | No | "Men", "Women", "Baby", "Toddler", "Kid", "Maternity" |
model_size | string | No | "Petit", "Regular", "Plus size" |
attributes | object | No | Descriptive attributes for filtering: ethnicity, age_range, hair_color, hair_style, skin_tone, eye_color |
program_id | string | No | Program ID (uses key default if omitted) |
# Request with attributes
{ "model_name": "Caucasian Plus-Size Woman",
"model_type": "Women", "model_size": "Plus size",
"attributes": { "ethnicity": "Caucasian", "age_range": "Middle-aged",
"hair_color": "Blonde", "hair_style": "Long", "skin_tone": "Fair" } }
# Response: {"success": true, "model_id": "xxx"}/modelList models with attribute filters, or get a model's images by adding model_id. LLM agents: use filters to match user descriptions.
| Parameter | Type | Required | Description |
|---|---|---|---|
model_id | string | No | If provided, returns model images instead of model list |
model_type | string | No | Filter: "Men", "Women", "Baby", "Toddler", "Kid", "Maternity" |
model_size | string | No | Filter: "Petit", "Regular", "Plus size" |
ethnicity | string | No | Filter: Caucasian, East Asian, Black, Hispanic, etc. |
age_range | string | No | Filter: Young, Young Adult, Middle-aged, Mature, Senior |
hair_color | string | No | Filter: Blonde, Brown, Black, Red, etc. |
skin_tone | string | No | Filter: Fair, Light, Medium, Olive, Tan, Brown, Dark |
# List response — with attributes for LLM selection
{ "models": [{ "model_id": "xxx", "model_name": "Dae Nakamura", "model_type": "Women",
"attributes": { "ethnicity": "East Asian", "age_range": "Young Adult",
"hair_color": "Black", "skin_tone": "Light" } }] }
# Images response (with model_id)
{ "model_images": [
{ "model_image_id": "...", "model_image_url": "https://...",
"model_id": "xxx", "style_id": "yyy",
"view_type": "front_facing", "crop_type": "fullbody" }
] }
# view_type: front_facing, back_facing (or null)
# crop_type: fullbody, head_half_cropped, top, bottom (or null)/model/faceGenerate AI face images. Supports variety (diverse) and fidelity (precise) modes.
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Face description with attributes |
mode | "variety" | "fidelity" | No | variety = diverse, fidelity = precise (default: variety) |
num_output_images | int | No | 1-4 images (default: 1) |
program_id | string | No | Program ID (uses key default if omitted) |
# Response
{ "request_id": "xxx", "status": "Request Submitted", "images_generated": [], "num_output_images": 2 }
# Poll via GET /model/image?request_id=xxx until status = "Generation Completed"
# Then save to model via POST /model/image with model_id + image_url from resultPrompt template: Headshot front facing portrait of a {Age} {Race} {BodySize} {Gender} with {HairColor} {HairStyle} hair with {EyeColor} eye with {SkinColor} skin tone in the {Background} setting with {Expression}. {Pose} pose, professional lighting, high detail.
/model/imageTwo modes: generate a new image (with prompt) or upload/save an existing image to a model (with model_id).
Mode 1 — Generate (provide prompt):
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Text prompt for image generation |
reference_image | string | No | Gallery image ID, HTTP URL, or base64 |
num_output_images | int | No | 1-8 images (default: 1) |
program_id | string | No | Program ID (uses key default if omitted) |
# Generate request
{ "prompt": "professional photo of model wearing garment" }
# Generate response — poll GET /model/image?request_id=xxx for results
{ "request_id": "xxx", "status": "Request Submitted", "images_generated": [], "num_output_images": 1 }Mode 2 — Upload/Save to model (provide model_id):
| Parameter | Type | Required | Description |
|---|---|---|---|
model_id | string | Yes | Model ID to save image to |
style_id | string | Yes | Style ID |
image_url | string | No | Image URL to save (e.g. from generation result) |
file_data | string | No | Base64 image to upload |
program_id | string | No | Program ID (uses key default if omitted) |
# Upload request — save a generated image to a model
{ "model_id": "xxx", "style_id": "yyy", "image_url": "https://s3.../generated.png?signed..." }
# Upload response
{ "success": true, "image_id": "...", "image_url": "https://...", "view_type": "front_facing", "crop_type": "fullbody" }
# view_type/crop_type are auto-detected by AI: front_facing/back_facing, fullbody/head_half_cropped/model/imagePoll generation status for POST /model/face, POST /model/image (generate), or POST /model/pose.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id | string | Yes | Request ID from any generation response |
program_id | string | No | Program ID (uses key default if omitted) |
# Response (completed)
{
"status": "Generation Completed",
"images_generated": [
{ "model_image_id": "path/to/output.png", "model_image_url": "https://s3.../output.png?signed..." }
]
}/model/poseGenerate model images in specific poses using a reference image. Costs credits.
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Prompt describing the desired pose |
reference_image | string | Yes | Model image ID from gallery (e.g. from GET /model?model_id=) |
program_id | string | No | Program ID (uses key default if omitted) |
# Response
{ "request_id": "xxx" }
# Poll: GET /model/image?request_id=xxx → same polling as face/image generation
# Save results: POST /model/image with model_id + style_id + image_url from result
# The reference_image's model_id and style_id can be found via GET /model?model_id=Poses
/pose/libraryBrowse 143 preset poses. Filter by model_category and prompt_type. Use pose reference images with POST /model/pose.
| Parameter | Type | Required | Description |
|---|---|---|---|
model_category | string | No | Filter: Women, Men, Child, Infant |
prompt_type | string | No | Filter: fullbody, head_half_cropped, top, bottom, close_up_upper_body, close_up_lower_body |
# Response
{ "poses": [{ "pose_id": "f00cb596...", "pose_name": "Full body upright",
"model_category": "Women", "prompt_type": "fullbody",
"reference_image_url": "https://cdn.huhu.ai/poses/xxx.png",
"sketch_url": "https://cdn.huhu.ai/poses/yyy.png" }], "total": 14 }
# LLM agent tips: "front view" → fullbody + pose_name with "front"
# "back view" → pose_name with "back", "close-up" → close_up_upper_body
# Use reference_image_url as reference_image in POST /model/pose/poseList your custom poses, or get detail by adding pose_id. For preset poses, use GET /pose/library.
| Parameter | Type | Required | Description |
|---|---|---|---|
pose_id | string | No | If provided, returns single pose detail |
program_id | string | No | Program ID (uses key default if omitted) |
# List response
{ "poses": [{ "pose_id": "xxx", "title": "Standing Front", "prompt": "standing pose, front facing...",
"reference_image_presigned_url": "https://...", "prompt_type": "fullbody" }] }
# Detail response (with pose_id)
{ "pose_id": "xxx", "title": "...", "prompt": "...", "reference_image_presigned_url": "https://..." }/poseCreate a new pose from a reference image. AI extracts the pose prompt automatically.
| Parameter | Type | Required | Description |
|---|---|---|---|
reference_image | string | Yes | Base64 image or HTTP URL of a person in the desired pose |
pose_title | string | Yes | Name for this pose |
program_id | string | No | Program ID (uses key default if omitted) |
# Response
{ "status": "success", "pose_id": "xxx", "pose_prompt": "standing pose, front facing, arms at sides...", "pose_title": "My Pose" }
# Use the pose_prompt in POST /model/pose to generate images in this poseSKUs
/skuList SKUs in a project, or get detail by adding sku_id. Detail includes AI-classified garment images.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project ID |
sku_id | string | No | If provided, returns single SKU detail with garment classification |
pagination_token | string | No | Token for next page (list only) |
# Detail response (with sku_id) — includes AI garment classification
{
"skus": [{
"sku_id": "xxx", "sku_name": "sku1", "garment_type": "Full body",
"garment_images": [
{ "garment_id": "abc", "image_id": "assets/sku_images/xxx.png", "image_url": "https://...",
"garment_type": "Top", "view": "front", "presentation": "flatlay_clean" },
{ "garment_id": "def", "image_id": "assets/sku_images/yyy.png", "image_url": "https://...",
"garment_type": "Bottom", "view": "front", "presentation": "flatlay_clean" },
{ "garment_id": "ghi", "image_id": "assets/sku_images/zzz.png", "image_url": "https://...",
"garment_type": "Shoes", "view": "front", "presentation": "flatlay_clean" }
]
}]
}Garment classification: After zip upload, images are automatically classified by AI into garment_type (Top, Bottom, Shoes, Accessory) and view (front, back). This classification is used when creating tasks.
/skuUpdate SKUs — assign model, style, garment type.
# Body: { "sku_ids": ["sku-001"], "model_id": "...", "style_id": "...", "garment_type": "Full body" }Tasks & Try-On
/taskCreate generation tasks for SKUs. Tasks are auto-created based on garment image classification (front/back views).
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project ID |
sku_ids | string[] | Yes | SKU IDs to create tasks for |
program_id | string | No | Program ID (uses key default if omitted) |
# Response: {"message": "success"}
# Tasks are created based on garment pairing (front view task + back view task)
# Then GET /task to find new task IDs with status "Task created"/taskUpdate a task — set model_image before generation. REQUIRED for try-on.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project ID |
task_id | string | Yes | Task ID to update |
sku_id | string | Yes | SKU ID the task belongs to |
program_id | string | No | Program ID (uses key default if omitted) |
model_id | string | No | Model ID to assign |
model_image | string | No | Model image ID (from GET /model/.../images). Required before generation. |
/task/generateSubmit try-on generation. Each task must have model_image set first.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project ID |
task_ids | string[] | Yes | Task IDs to generate (must have model_image set) |
program_id | string | No | Program ID (uses key default if omitted) |
num_output_images | int | No | Images per task, 1-4 (default: 1) |
prompt | string | No | Optional text prompt |
# Response: {"message": "success"}
# Errors: 429 (concurrent limit exceeded)
# Poll: GET /task → ~90s to complete/taskList tasks (poll status), or get task detail with output images by adding project_id + sku_id + task_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | No | For task detail |
sku_id | string | No | For task detail |
task_id | string | No | For task detail |
program_id | string | No | Program ID (uses key default if omitted) |
# List response (no detail params) — poll for generation progress
{ "skus": [{ "sku_name": "Blue Dress", "tasks": [{ "task_id": "xxx", "task_status": "Generation completed" }] }] }
# Detail response (with project_id + sku_id + task_id) — get output images
{ "task_id": "xxx", "task_status": "Generation completed", "output": [{ "image_id": "xxx", "image_url": "https://..." }] }Video
/videoGenerate a video from a try-on output image. Costs 25 credits (5s) or 40 credits (10s).
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project ID |
sku_id | string | Yes | SKU ID |
video_length | string | Yes | "5" (5 seconds) or "10" (10 seconds) |
prompt | string | Yes | Video motion prompt |
first_frame_image | object | Yes | {image_base64: "..."} or {image_id: "..."} |
program_id | string | No | Program ID (uses key default if omitted) |
source_task_id | string | No | Source task ID |
/video/requestsPoll video generation status. Returns video_url when completed.
| Parameter | Type | Required | Description |
|---|---|---|---|
program_id | string | No | Program ID (uses key default if omitted) |
limit | int | No | Max results (default: 10) |
pagination_token | string | No | For pagination |
# Response
{
"requests": [{
"request_status": "Generation completed",
"video_url": "https://s3.../video.mp4?signed...",
"duration": "5"
}]
}Upscale
/upscaleUpscale an image to 2x-4x resolution (max 18M output pixels). Costs 2 credits per image.
| Parameter | Type | Required | Description |
|---|---|---|---|
image | string | Yes | Base64-encoded image or HTTP URL |
upscale_factor | float | No | Scale factor: 1-4 (default: 2) |
num_images | int | No | Output variations: 1-4 (default: 1) |
program_id | string | No | Program ID (uses key default if omitted) |
# Response
{ "status": "success", "request_id": "req-upscale-123", "cost": 2 }
# Resolution limits: input_pixels × factor² ≤ 18,000,000
# 1024×1024 → 4x = 16.8M ✓ | 2048×2048 → 4x = 67M ✗/upscalePoll upscale status. Returns result URLs when completed.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_id | string | Yes | Request ID from POST /upscale |
program_id | string | No | Program ID (uses key default if omitted) |
# Response (completed)
{ "request_id": "...", "status": "success", "result_urls": ["https://...upscaled.png"] }Error Codes
| Code | Meaning | Action |
|---|---|---|
401 | Invalid, missing, or revoked API key | Check your API key |
402 | Insufficient credits | Top up credits or wait for renewal |
403 | API key not authorized for this team | Check API key team binding |
404 | Resource not found | Verify IDs exist |
422 | Validation error | Check request body and params |
429 | Rate limited | Back off and retry |
500 | Internal server error | Retry after a delay |
Common Workflows
Generate Images
# 1. Check credits
curl https://api.huhu.ai/public/v1/metering/plan \
-H "Authorization: Bearer sk-..."
# 2. Submit generation
curl -X POST https://api.huhu.ai/public/v1/model/image \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json"
# 3. Poll task status (every 5-10 seconds)
curl https://api.huhu.ai/public/v1/task \
-H "Authorization: Bearer sk-..."
# → Check task_status until "Generation completed"
# → Retrieve output_image_url from completed tasksExplore Resources
# List programs → pick program_id
curl https://api.huhu.ai/public/v1/program -H "Authorization: Bearer sk-..."
# List projects → pick project_id
curl https://api.huhu.ai/public/v1/project -H "Authorization: Bearer sk-..."
# Create a new project
curl -X POST "https://api.huhu.ai/public/v1/project" \
-H "Content-Type: application/json" \
-d '{"project_name":"New Project"}' \
-H "Authorization: Bearer sk-..."Best Practices
- Check credits first — Always call
/metering/planbefore generation. - Respect concurrency — The
concurrent_requestsfield limits parallel jobs. - Poll gently — Check
/taskevery 5-10 seconds, not faster. - Handle 402 — Stop submitting if credits run out.
- Pagination — If
pagination_tokenis non-null, pass it to get more results. - Timestamps — All timestamps are Unix epoch in milliseconds.
- IDs — All IDs are UUID or hex strings. Store as strings.
LLM Agent Integration
Two integration options for AI agents and automation tools:
- OpenAPI Spec — Machine-readable JSON schema for auto-generating tool definitions, importing into Postman/Swagger, or building client SDKs. Best for agents with function-calling/tool-use capabilities.
- SKILL.md — Complete human-readable reference with decision trees, workflows, best practices, and prompt templates. Best for loading into LLM context windows.
Quick Reference
BASE: https://api.huhu.ai/public/v1
AUTH: Authorization: Bearer sk-<key>
GET /metering/plan → credits & plan
GET /metering/credit-history → spending log
GET /program → list programs (add ?owned=true for owned only)
POST /program → create program
GET /project → list projects (add ?project_id= for detail)
POST /project → create project
POST /model → create model in gallery
GET /model → list models
GET /model?model_id= → model images (omit for list)
POST /model/face → generate face (variety/fidelity)
POST /model/image → generate or upload model image
GET /model/image?request_id= → poll generation status
POST /model/pose → generate with poses
GET /pose/library → browse 143 preset poses (filter by category, type)
GET /pose → list custom poses (add ?pose_id= for detail)
POST /pose → create pose from reference image
GET /sku?project_id= → list SKUs (add &sku_id= for detail)
PUT /sku → update SKU (model, style)
POST /project/upload → upload garment image or init zip
POST /project/complete_multipart_upload → finalize zip upload
POST /task → create tasks for SKUs
PUT /task → set model_image on task
POST /task/generate → try-on generation
GET /task → list tasks (add ?project_id=&sku_id=&task_id= for detail)
POST /video → create video
GET /video/requests → poll video status
POST /upscale → upscale image (2x-4x, max 18M px)
GET /upscale?request_id= → poll upscale status